Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
473361d332 | ||
|
|
3c370bb600 | ||
|
|
458377891d | ||
|
|
821689c160 | ||
|
|
263b0cfa43 | ||
|
|
c30af860e7 | ||
|
|
419b7940d6 | ||
|
|
3dc33c6d8b | ||
|
|
22e54ea294 | ||
|
|
8b6b584117 | ||
|
|
43c65547c3 | ||
|
|
d3682b9dc2 | ||
|
|
944c983ae0 | ||
|
|
d393f6b614 | ||
|
|
02cc172bf0 | ||
|
|
fa3dc44076 | ||
|
|
8e60dd658d | ||
|
|
762ea3ebf6 | ||
|
|
4019c4fe17 | ||
|
|
b4c071b695 | ||
|
|
8969b905c1 | ||
|
|
71b570fb94 | ||
|
|
01a51c2214 | ||
|
|
c93f2d0b5a | ||
|
|
10d1c45969 | ||
|
|
e74f34cdd2 | ||
|
|
73752c7e7d | ||
|
|
167f855ffd | ||
|
|
522428a230 | ||
|
|
90b82487b3 | ||
|
|
18dffe91e1 | ||
|
|
f6996eb124 | ||
|
|
21a17a47b3 | ||
|
|
e6a7ef9247 | ||
|
|
d102c5507e | ||
|
|
6c5638ab05 | ||
|
|
36c7c73f9f | ||
|
|
0358087cd6 | ||
|
|
eb843cd051 | ||
|
|
5168fd5842 | ||
|
|
cb91e5d154 | ||
|
|
72447de801 | ||
|
|
de048ae478 | ||
|
|
d1d31f51a9 | ||
|
|
5d2c999758 | ||
|
|
6b0527108e | ||
|
|
d0c23a0ddf | ||
|
|
09c80b88d9 | ||
|
|
72c6958d6a | ||
|
|
8226c07595 | ||
|
|
47aefeb768 | ||
|
|
d35a1f647d | ||
|
|
10f0ea2eef |
@@ -94,16 +94,6 @@ inputs:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
# Unfortunately, "uses:" fields cannot have references to variables like
|
||||
# ${{env.MFEM_ACTIONS_VERSION}}, so the branch/tag name has to be hard coded.
|
||||
# Therefore, in the future, when updating the version of the
|
||||
# mfem/github-actions to use, we'll have to replace:
|
||||
# - all definitions of MFEM_ACTIONS_VERSION and
|
||||
# - all "uses:" fields that refer to mfem/github-actions.
|
||||
MFEM_ACTIONS_VERSION:
|
||||
description: Version (branch or tag) of the mfem/github-actions to use.
|
||||
default: v2.7
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -128,7 +118,6 @@ runs:
|
||||
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MFEM_ACTIONS_VERSION=${{inputs.MFEM_ACTIONS_VERSION}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (dir)
|
||||
|
||||
@@ -25,7 +25,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
@@ -53,7 +53,7 @@ runs:
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- uses: mfem/github-actions/build-mfem@v2.7
|
||||
- uses: mfem/github-actions/build-mfem@v2.5
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
env:
|
||||
CXXFLAGS: ${{env.CXXFLAGS}}
|
||||
@@ -82,7 +82,7 @@ runs:
|
||||
run: find . -type f -name '*.o' -delete
|
||||
shell: bash
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
name: 'Install MPI'
|
||||
description: 'Installs MPI and set up its environment variables'
|
||||
|
||||
inputs:
|
||||
NO_FLAGS:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -32,7 +27,6 @@ runs:
|
||||
shell: bash
|
||||
|
||||
- name: Env (bis)
|
||||
if: ${{ inputs.NO_FLAGS != 'true' }}
|
||||
run: |
|
||||
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
|
||||
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
|
||||
|
||||
@@ -36,7 +36,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
@@ -49,7 +49,7 @@ runs:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
@@ -23,7 +23,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache/restore@v5 # Cache for LLVM libcxx
|
||||
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
@@ -32,32 +32,32 @@ runs:
|
||||
- uses: ./.github/actions/sanitize/mpi
|
||||
if: ${{inputs.par == 'true'}}
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Hypre
|
||||
- uses: actions/cache/restore@v4 # Cache for Hypre
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{env.MFEM_ACTIONS_VERSION}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Metis
|
||||
- uses: actions/cache/restore@v4 # Cache for Metis
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
|
||||
- name: Hypre/Metis links
|
||||
if: ${{inputs.par == 'true'}}
|
||||
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for LSAN suppression file
|
||||
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
- uses: actions/checkout@v6 # Checkout the repository
|
||||
- uses: actions/checkout@v4 # Checkout the repository
|
||||
with:
|
||||
path: mfem
|
||||
# ref: ${{env.BRANCH}}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
# MFEM Pull Request Review Agent Guide
|
||||
|
||||
## Purpose and scope
|
||||
Review MFEM PRs for correctness, maintainability, performance, portability, test coverage, and MFEM consistency. Use the diff and PR context; reference source files, tests, and CI results when available. Follow `CONTRIBUTING.md`, especially Developer Guidelines, PR rules, checklist, and testing.
|
||||
|
||||
## Critical review pillars
|
||||
- Correctness and numerical behavior
|
||||
- API and user-facing impact
|
||||
- Performance implications
|
||||
- Maintainability and portability
|
||||
|
||||
## Review workflow
|
||||
1. Read the PR description, linked issues, and intended behavior.
|
||||
2. Inspect the diff before commenting.
|
||||
3. Identify affected MFEM components, examples, tests, build or docs changes, and downstream APIs.
|
||||
4. Analyze the code against the critical review pillars.
|
||||
5. Compare the change against nearby code and MFEM patterns; flag unmotivated deviations.
|
||||
6. Check whether tests and documentation were updated appropriately.
|
||||
7. Review CI results and suggest actions.
|
||||
8. Produce a structured review with prioritized findings.
|
||||
9. Always limit conclusions to available evidence.
|
||||
|
||||
## MFEM-specific review checklist
|
||||
- Component-aware scope: identify the touched subsystem (FEM, solvers, preconditioners, linear algebra, mesh, examples, miniapps, build, or docs) and assess its impact against the review pillars.
|
||||
- Numerical and algorithmic behavior: assess issues in convergence, stability, tolerances, precision, iteration limits, and failure handling. If clear opportunities exist to improve the algorithmic approach, call them out with expected impact.
|
||||
- API and user-facing impact: assess backward compatibility, user-visible behavior and default changes, migration impact, deprecations, and whether documentation clearly explains user-facing API changes.
|
||||
- Data structure and memory semantics: assess ownership, lifetime, aliasing, container behavior, and device-host synchronization.
|
||||
- Parallel and serial behavior: assess whether the change preserves equivalent semantics in serial and parallel modes where applicable; if logic is currently mode-specific, check whether extension to the other mode is straightforward (clear abstractions, no hard-wired assumptions), document constraints, and call out expected behavior differences explicitly.
|
||||
- Backend and portability impact: assess likely cross-backend risks in CPU, CUDA, HIP, OCCA, RAJA, partial assembly, fallback paths, compiler compatibility, and platform assumptions.
|
||||
- Build, dependency, and configuration impact: assess CMake or make changes, optional dependency behavior, and feature-flag interactions.
|
||||
- Tests and docs alignment: check available regression or unit coverage evidence for changed behavior, and ensure docs are updated for new flags, APIs, options, or behavior changes.
|
||||
- MFEM developer-guideline fit: keep code lean, simple, general, logically separated, and portable; suggest C++17 improvements when they clearly improve safety, clarity, or maintainability.
|
||||
- New source files, examples, or miniapps: if a PR adds source/header files, verify they are properly wired into the relevant `makefile` and `CMakeLists.txt`, referenced in docs where applicable (including `doc/CodeDocumentation.dox`), and added to top-level `.gitignore` only when generated artifacts require it.
|
||||
- Changelog: verify `CHANGELOG` is updated if the PR introduces significant new features or user-facing changes.
|
||||
- MFEM conventions: use `real_t`; use `mfem::out`/`mfem::err` instead of `std::cout`/`std::cerr` in library code; flag large/binary files; if AI assistance is apparent but undisclosed, suggest following `CONTRIBUTING.md`.
|
||||
- Edge cases: if the PR touches complex or error-prone areas, suggest additional tests for edge cases, failure modes, and parallel behavior.
|
||||
|
||||
## Commenting guidelines
|
||||
- Keep comments concise, actionable, and grounded in the diff.
|
||||
- Focus on correctness, behavior changes, and user impact over style nits.
|
||||
- Be professional, concise, collaborative, technically precise, and avoid unsupported assumptions.
|
||||
|
||||
@@ -13,7 +13,7 @@ Note that some of these scripts use the shared MFEM GitHub Actions from the exte
|
||||
|
||||
<https://github.com/mfem/github-actions>
|
||||
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch (or tag) in the above from which the action is taken.
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch in the above from which the action is taken.
|
||||
|
||||
The current CI workflows are:
|
||||
|
||||
@@ -29,12 +29,16 @@ Runs a number of static repository-level sanity checks.
|
||||
|
||||
- `branch-history` guards against accidental commits of large files using the `--history` option of the `config/githooks/pre-push` script.
|
||||
|
||||
## `mfem-analysis.yml` (`build-analysis`)
|
||||
|
||||
Checks if the code builds and satisfies minimal requirements.
|
||||
|
||||
- `gitignore` builds hypre, METIS, and MFEM using `mfem/github-actions/build-hypre`, `mfem/github-actions/build-metis`, and `mfem/github-actions/build-mfem` and checks for correct `.gitignore` settings by running the `tests/scripts/gitignore` script.
|
||||
|
||||
## `builds-and-tests.yml`
|
||||
|
||||
Runs a matrix of builds and tests runs with different compilers, OS, mfem/hypre settings, etc. Also processes and upload Codecov reports.
|
||||
|
||||
One matrix job runs `tests/scripts/gitignore` after `make test-noclean` to check generated artifacts against `.gitignore`.
|
||||
|
||||
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
|
||||
|
||||
- `mfem/github-actions/build-hypre`
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
remove-docker-images: 'true'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# It's easier to reference named variables than indexes of the matrix
|
||||
- name: Set Environment
|
||||
|
||||
@@ -40,7 +40,6 @@ env:
|
||||
METIS_ARCHIVE_MAC: metis-4.0.3-mac.tgz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
MFEM_TOP_DIR: mfem
|
||||
MFEM_ACTIONS_VERSION: v2.7
|
||||
|
||||
# Note for future improvements:
|
||||
#
|
||||
@@ -111,7 +110,6 @@ jobs:
|
||||
build-system: make
|
||||
hypre-target: int64
|
||||
precision: fp64
|
||||
gitignore-check: YES
|
||||
- os: ubuntu-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
@@ -155,7 +153,7 @@ jobs:
|
||||
# /home/runner/work/mfem/mfem/mfem
|
||||
# Note: Done now to access "install-hypre" and "install-metis" actions.
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.MFEM_TOP_DIR }}
|
||||
# Fetch the complete history for codecov to access commits ID
|
||||
@@ -172,6 +170,20 @@ jobs:
|
||||
env
|
||||
shell: bash
|
||||
|
||||
# For info on Xcode see:
|
||||
# - https://github.com/actions/runner-images/issues/12541
|
||||
# - https://github.com/actions/runner-images/blob/releases/macos-15-arm64/20250811/images/macos/macos-15-arm64-Readme.md#xcode
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
XCODE_PATH="/Applications/Xcode_16.4.app"
|
||||
echo "> sudo xcode-select -s ${XCODE_PATH}"
|
||||
sudo xcode-select -s ${XCODE_PATH}
|
||||
echo "> g++ -v"
|
||||
g++ -v
|
||||
echo "> clang++ -v"
|
||||
clang++ -v
|
||||
|
||||
# Only get MPI if defined for the job.
|
||||
# TODO: It would be nice to have only one step, e.g. with a dedicated
|
||||
# action, but I (@adrienbernede) don't see how at the moment.
|
||||
@@ -213,14 +225,14 @@ jobs:
|
||||
- name: cache hypre
|
||||
id: hypre-cache
|
||||
if: matrix.mpi == 'par'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
|
||||
- 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.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -230,7 +242,7 @@ jobs:
|
||||
|
||||
- name: get hypre (Windows)
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -243,14 +255,14 @@ jobs:
|
||||
- name: cache metis
|
||||
id: metis-cache
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
- 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.7
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
with:
|
||||
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
@@ -258,7 +270,7 @@ jobs:
|
||||
- name: cache vcpkg (Windows)
|
||||
id: vcpkg-cache
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vcpkg_cache
|
||||
key: ${{ runner.os }}-${{ matrix.mpi }}-vcpkg-v1
|
||||
@@ -283,8 +295,7 @@ jobs:
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew update
|
||||
brew install enzyme
|
||||
ENZYME_LLVM=$(brew info enzyme | sed -n 's/^Required.*:.*\(llvm[^ ]*\).*/\1/p')
|
||||
echo "ENZYME_LLVM=$ENZYME_LLVM"
|
||||
ENZYME_LLVM=$(brew info enzyme | sed -n 's/^Required:.*\(llvm[^ ]*\).*/\1/p')
|
||||
LLVM_PREFIX=$(brew --prefix $ENZYME_LLVM)
|
||||
echo "LLVM_PREFIX=$LLVM_PREFIX" >> $GITHUB_ENV
|
||||
echo "OMPI_CC=$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV
|
||||
@@ -292,7 +303,7 @@ jobs:
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.7
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
env:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
|
||||
with:
|
||||
@@ -318,13 +329,7 @@ jobs:
|
||||
- name: tests
|
||||
if: matrix.build-system == 'make' && (matrix.target == 'opt' || matrix.os == 'ubuntu-latest')
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }}
|
||||
if [[ "${{ matrix.gitignore-check }}" == "YES" ]]; then
|
||||
make test-noclean
|
||||
else
|
||||
make test
|
||||
fi
|
||||
shell: bash
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make test
|
||||
|
||||
- name: cmake checks
|
||||
if: matrix.build-system == 'cmake' && matrix.target == 'dbg'
|
||||
@@ -369,16 +374,8 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v2.7
|
||||
uses: mfem/github-actions/upload-coverage@v2.5
|
||||
with:
|
||||
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 }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
directories: "fem general linalg mesh"
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: gitignore
|
||||
if: matrix.gitignore-check == 'YES'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }}/tests/scripts
|
||||
./runtest gitignore
|
||||
|
||||
@@ -14,19 +14,9 @@ name: "Static Analysis"
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "next"]
|
||||
paths-ignore: &docs-only-paths
|
||||
- "**/*.md"
|
||||
- "doc/**"
|
||||
- ".binder/**"
|
||||
- "CITATION.cff"
|
||||
- "LICENSE"
|
||||
- "NOTICE"
|
||||
- "CHANGELOG"
|
||||
- "INSTALL"
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["master"]
|
||||
paths-ignore: *docs-only-paths
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -50,11 +40,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -67,7 +57,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -80,4 +70,4 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
name: "Build Analysis"
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- next
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HYPRE_ARCHIVE: v2.19.0.tar.gz
|
||||
HYPRE_TOP_DIR: hypre-2.19.0
|
||||
METIS_ARCHIVE: metis-4.0.3.tar.gz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
COVERAGE_ENV: mfem-coverage
|
||||
|
||||
jobs:
|
||||
gitignore:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout MFEM
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
- name: Get MPI (Linux)
|
||||
run: |
|
||||
sudo apt-get install openmpi-bin libopenmpi-dev
|
||||
export OMPI_MCA_rmaps_base_oversubscribe=1
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: int32
|
||||
|
||||
- name: Cache Metis Install
|
||||
id: metis-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
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
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
mfem-dir: mfem
|
||||
|
||||
- name: test (no clean)
|
||||
run: |
|
||||
cd mfem && make test-noclean
|
||||
|
||||
- name: gitignore
|
||||
run: |
|
||||
cd mfem/tests/scripts
|
||||
./runtest gitignore
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: copyright check
|
||||
id: copyright
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get astyle
|
||||
run: |
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get doxygen and graphviz
|
||||
run: |
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -17,24 +17,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 2.19.0
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{env.HYPRE_TGZ}}
|
||||
dir: ${{env.HYPRE_DIR}}
|
||||
|
||||
@@ -27,13 +27,13 @@ jobs:
|
||||
llvm_use_sanitizer: "Undefined"
|
||||
name: ${{matrix.sanitizer}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: lsan.supp
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
@@ -17,24 +17,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 4.0.3
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.7
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
with:
|
||||
archive: ${{env.METIS_TGZ}}
|
||||
dir: ${{env.METIS_DIR}}
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/mfem
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
env:
|
||||
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
|
||||
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
env:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build/tests/unit
|
||||
run: find . -type f -name '*.o' -delete
|
||||
- uses: actions/upload-artifact@v7
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
@@ -165,14 +165,14 @@ jobs:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
np: ${{inputs.par && '_np=2' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
|
||||
@@ -17,17 +17,7 @@ permissions:
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "next"]
|
||||
paths-ignore: &docs-only-paths
|
||||
- "**/*.md"
|
||||
- "doc/**"
|
||||
- ".binder/**"
|
||||
- "CITATION.cff"
|
||||
- "LICENSE"
|
||||
- "NOTICE"
|
||||
- "CHANGELOG"
|
||||
- "INSTALL"
|
||||
pull_request:
|
||||
paths-ignore: *docs-only-paths
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
||||
+14
-47
@@ -19,15 +19,9 @@ CMakeFiles/
|
||||
# Clangd server cache
|
||||
*.cache*
|
||||
|
||||
#vscode settings
|
||||
/.vscode/
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
|
||||
# clangd index
|
||||
/.cache/
|
||||
|
||||
# Default install location
|
||||
/mfem/
|
||||
|
||||
@@ -85,17 +79,12 @@ examples/sol_u.*
|
||||
examples/sol_p.*
|
||||
examples/sol_r.*
|
||||
examples/sol_i.*
|
||||
examples/sol_z.*
|
||||
examples/ex6p-checkpoint.*
|
||||
examples/order.*
|
||||
examples/ex9.mesh
|
||||
examples/ex9-mesh.*
|
||||
examples/ex9-init.*
|
||||
examples/ex9-final.*
|
||||
examples/ex41.mesh
|
||||
examples/ex41-mesh.*
|
||||
examples/ex41-init.*
|
||||
examples/ex41-final.*
|
||||
examples/deformed.*
|
||||
examples/velocity.*
|
||||
examples/elastic_energy.*
|
||||
@@ -227,9 +216,6 @@ miniapps/electromagnetics/Joule_[0-9]*
|
||||
miniapps/electromagnetics/Lorentz_[0-9]*
|
||||
miniapps/electromagnetics/Lorentz.dat
|
||||
|
||||
miniapps/fluids/schrodinger-flow/schrodinger_flow
|
||||
miniapps/fluids/schrodinger-flow/pschrodinger_flow
|
||||
|
||||
miniapps/gslib/field-diff
|
||||
miniapps/gslib/field-interp
|
||||
miniapps/gslib/findpts
|
||||
@@ -237,7 +223,6 @@ miniapps/gslib/pfindpts
|
||||
miniapps/gslib/schwarz_ex1
|
||||
miniapps/gslib/schwarz_ex1p
|
||||
miniapps/gslib/interpolated.gf
|
||||
miniapps/gslib/particles_redist
|
||||
|
||||
miniapps/meshing/mobius-strip
|
||||
miniapps/meshing/klein-bottle
|
||||
@@ -284,8 +269,10 @@ miniapps/meshing/refined.mesh
|
||||
miniapps/meshing/bounding-box*
|
||||
miniapps/meshing/jacobian-determinant*
|
||||
|
||||
miniapps/mtop/ParaView/
|
||||
miniapps/mtop/mtop_test_iso_elasticity
|
||||
miniapps/mtop/parheat
|
||||
miniapps/mtop/ParHeat/*
|
||||
miniapps/mtop/seqheat
|
||||
miniapps/mtop/SeqHeat/*
|
||||
|
||||
miniapps/autodiff/paradiff
|
||||
miniapps/autodiff/seqadiff
|
||||
@@ -295,26 +282,21 @@ miniapps/autodiff/seq_example
|
||||
miniapps/autodiff/seq_test
|
||||
miniapps/autodiff/Example/*
|
||||
|
||||
miniapps/fluids/navier/navier_mms
|
||||
miniapps/fluids/navier/navier_kovasznay
|
||||
miniapps/fluids/navier/navier_kovasznay_vs
|
||||
miniapps/fluids/navier/navier_tgv
|
||||
miniapps/fluids/navier/navier_shear
|
||||
miniapps/fluids/navier/navier_3dfoc
|
||||
miniapps/fluids/navier/navier_turbchan
|
||||
miniapps/fluids/navier/navier_cht
|
||||
miniapps/fluids/navier/navier_bifurcation
|
||||
miniapps/fluids/navier/Navier_Bifurcation_[0-9]*
|
||||
miniapps/fluids/navier/ParaView
|
||||
miniapps/fluids/navier/tgv_out*.txt
|
||||
miniapps/fluids/navier/*_output
|
||||
miniapps/navier/navier_mms
|
||||
miniapps/navier/navier_kovasznay
|
||||
miniapps/navier/navier_kovasznay_vs
|
||||
miniapps/navier/navier_tgv
|
||||
miniapps/navier/navier_shear
|
||||
miniapps/navier/navier_3dfoc
|
||||
miniapps/navier/navier_turbchan
|
||||
miniapps/navier/navier_cht
|
||||
miniapps/navier/tgv_out*.txt
|
||||
miniapps/navier/*_output
|
||||
|
||||
miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
miniapps/nurbs/nurbs_ex3
|
||||
miniapps/nurbs/nurbs_ex5
|
||||
miniapps/nurbs/nurbs_ex10
|
||||
miniapps/nurbs/nurbs_ex10p
|
||||
miniapps/nurbs/nurbs_ex11p
|
||||
miniapps/nurbs/nurbs_ex24
|
||||
miniapps/nurbs/nurbs_solenoidal
|
||||
@@ -340,14 +322,7 @@ miniapps/nurbs/nurbs_naca_cmesh
|
||||
miniapps/nurbs/naca-cmesh.mesh
|
||||
miniapps/nurbs/glvis_naca-cmesh.mesh
|
||||
miniapps/nurbs/Naca_cmesh
|
||||
miniapps/nurbs/nurbs_mesh_info
|
||||
miniapps/nurbs/k*_*.dat
|
||||
miniapps/nurbs/*-Surface.mesh
|
||||
miniapps/nurbs/*.mesh
|
||||
miniapps/nurbs/*.sol
|
||||
miniapps/nurbs/deformed.*
|
||||
miniapps/nurbs/elastic_energy.*
|
||||
miniapps/nurbs/velocity.*
|
||||
|
||||
miniapps/performance/ex1
|
||||
miniapps/performance/ex1p
|
||||
@@ -369,7 +344,6 @@ miniapps/shifted/lsf_integral
|
||||
miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
miniapps/tools/convert-dc
|
||||
miniapps/tools/compare-dc
|
||||
miniapps/tools/gridfunction-bounds
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
@@ -440,13 +414,6 @@ miniapps/tribol/contact-patch-test
|
||||
miniapps/diag-smoothers/abs-l1-jacobi
|
||||
miniapps/diag-smoothers/mg-abs-l1-jacobi
|
||||
|
||||
miniapps/contact/contact
|
||||
miniapps/contact/ParaView
|
||||
|
||||
miniapps/plasma/pic/electrostatic-*
|
||||
!miniapps/plasma/pic/electrostatic-*.cpp
|
||||
miniapps/plasma/pic/*.csv
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
FIXME: this file needs to be updated
|
||||
|
||||
This directory contains most of the GitLab CI configuration. MFEM runs both PR
|
||||
and nightly testing on GitLab.
|
||||
|
||||
|
||||
@@ -85,8 +85,3 @@ opt_par_gcc_10_pumi:
|
||||
extends: .mfem_job_on_dane
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +pumi"
|
||||
|
||||
opt_par_gcc_10_gslib:
|
||||
extends: .mfem_job_on_dane
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +gslib"
|
||||
|
||||
@@ -63,8 +63,3 @@ opt_mpi_cuda_hypre_cuda_gcc:
|
||||
extends: .mfem_job_on_matrix
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90 ^hypre+cuda"
|
||||
|
||||
opt_mpi_cuda_gcc_gslib:
|
||||
extends: .mfem_job_on_matrix
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +mpi +cuda +gslib cuda_arch=90 ^hypre+cuda"
|
||||
|
||||
@@ -32,9 +32,9 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "dane" ]]; then
|
||||
srun --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
srun --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
else
|
||||
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
|
||||
exit 1
|
||||
|
||||
@@ -8,94 +8,9 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.9.1 (development)
|
||||
Version 4.8.1 (development)
|
||||
===========================
|
||||
|
||||
- Added policy for AI-assisted contribution to CONTRIBUTING.md.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Improved FindPointsGSLIB surface mesh capability with support for simplices
|
||||
and an option to specify axis-aligned bounding box padding for near-surface
|
||||
point queries.
|
||||
|
||||
- Added GPU-enabled partial assembly for simplicial Bernstein H1 basis based on
|
||||
ragged tensor algorithms (see DOI: 10.1137/11082539X) for mass and diffusion
|
||||
integrators.
|
||||
|
||||
- Replaced legacy simplex quadrature rules with symmetric positive weight rules
|
||||
for triangles (orders 0-25) and tetrahedra (orders 0-20). These rules
|
||||
guarantee all-positive weights and interior quadrature points, improving
|
||||
numerical stability. Higher orders fall back to Grundmann-Moller.
|
||||
* Triangle rules: Witherden and Vincent, DOI: 10.1016/j.camwa.2015.03.017
|
||||
* Tet rules (d=1-13): Witherden and Vincent (same as above)
|
||||
* Tet rules (d=14-20): Chuluunbaatar et al., DOI: 10.1016/j.camwa.2022.08.016
|
||||
|
||||
- Added support for general 1D Gauss-Jacobi quadrature rules and Stroud conical
|
||||
quadrature rules on triangles and tetrahedra.
|
||||
|
||||
- Improved the GridFunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behavior has not changed.
|
||||
|
||||
- Added GridFunction projection methods for trace spaces, i.e., project
|
||||
coefficients on the mesh skeleton.
|
||||
|
||||
- Added methods to estimate function extremum using piecewise linear bounds plus
|
||||
recursive subdivision.
|
||||
|
||||
- Extend FindPointsGSLIB to support surface meshes.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added option to guarantee mesh validity during TMOP-based r-adaptivity, using
|
||||
bounds on the determinant of the mesh transformation Jacobian.
|
||||
|
||||
- Added PA support for TMOP's adaptive limiting functionality. Multiple
|
||||
GridFunctions and Coefficients can be combined to form a composite term.
|
||||
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
- Added the option to include material interfaces (faces separating elements
|
||||
with different element attributes) as additional boundary elements, for
|
||||
parallel visualization, e.g. with GLVis. This is supported by both the Print
|
||||
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added support for trace spaces in PRefinementTransferOperator. This is used in
|
||||
PRefinement multigrid methods for problems posed on trace spaces (see e.g. the
|
||||
DPG miniapps).
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added device assembly support for 3D H(curl) VectorFEDomainLFIntegrator.
|
||||
|
||||
- Added NVIDIA cuDSS library interface. Implementation examples have been
|
||||
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
|
||||
details. Supported versions >= 0.6.0.
|
||||
|
||||
- Allow specifying GPU kernel launch bounds for native and RAJA GPU backends.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- The Lorentz miniapp (in miniapps/electromagnetics) has been updated to
|
||||
leverage the ParticleSet capability.
|
||||
|
||||
- Added (Complex)PRefinementMultigrid solver option in the DPG miniapps.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Fixed signed DOF handling in ParGridFunction reading (read constructor) and
|
||||
saving via SaveAsOne(). Simplified the process of applying the DOF signs by
|
||||
using the new method ApplyDofSigns() in class ParFiniteElementSpace: the
|
||||
method will return immediately if no sign flips are needed.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
|
||||
Starting with this version, MFEM requires a C++17 compiler.
|
||||
|
||||
Discretization improvements
|
||||
@@ -104,149 +19,70 @@ Discretization improvements
|
||||
nonlinear finite element operators, based on Enzyme or dual numbers AD at
|
||||
quadrature points. These features are part of the new mfem::future namespace
|
||||
and some of the API can change in the future. See the new dFEM minimal surface
|
||||
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use. Using
|
||||
Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM built
|
||||
with plugin support. See INSTALL for more details.
|
||||
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use.
|
||||
|
||||
- Introduced initial support for particle methods in MFEM with new classes
|
||||
Particle, ParticleSet and ParticleVector.
|
||||
* Particle is a convenient interface for individual particle data.
|
||||
* ParticleSet manages and stores particle data in a struct-of-arrays form,
|
||||
carrying particle coordinates and IDs along with an arbitrary number of
|
||||
Vector and integer data for each particle.
|
||||
* ParticleVector is a Vector-derived container that stores vector data for an
|
||||
arbitrary number of particles contiguously based on specified vdim/ordering.
|
||||
See the new particle miniapps in miniapps/gslib/ and miniapps/fluids/navier/.
|
||||
|
||||
- Added a new miniapp and specialized AMG solver (AMGF) for optimization-based
|
||||
contact mechanics. The miniapp solves large-scale frictionless contact using a
|
||||
self-contained Interior Point (IP) solver, mortar-based contact constraints
|
||||
provided by Tribol. The resulting linear systems are solved with the new AMGF
|
||||
solver (see below). Benchmark examples include the two-block, ironing, and
|
||||
beam-sphere problems. See the miniapps/contact/ directory.
|
||||
|
||||
- Added support for boundary integration to the hyperbolic framework. Two new
|
||||
classes BdrHyperbolicDirichletIntegrator and BoundaryHyperbolicFlowIntegrator
|
||||
have been introduced for implementation of weak Dirichlet boundary conditions
|
||||
with a general flux or for the linear case respectively.
|
||||
|
||||
- Added a method to compute piecewise linear bounds on high-order functions on
|
||||
tensor-product elements.
|
||||
|
||||
- Added support for interior face integration enabling DG methods in
|
||||
ParMixedBilinearForm, ParNonlinearForm and ParBlockNonlinearForm.
|
||||
- Using Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM
|
||||
built with plugin support. See INSTALL for more details.
|
||||
|
||||
- In the ParMoonolith integration, added support for variational resampling of
|
||||
H1 vector fields.
|
||||
|
||||
- Introduced IMEX ODE solvers based on a split-operator framework. Added
|
||||
examples ex41 and ex41p demonstrating IMEX DG/CG discretizations of the
|
||||
convection–diffusion equation, with ex41p using DG LOR preconditioning.
|
||||
- Added support for boundary integration to the hyperbolic framework. In this
|
||||
regard, new classes `BdrHyperbolicDirichletIntegrator` and
|
||||
`BoundaryHyperbolicFlowIntegrator` have been introduced for implementation
|
||||
of weak Dirichlet boundary conditions with a general flux or for the linear
|
||||
case respectively.
|
||||
|
||||
- Added method to compute piecewise linear bounds on high-order functions on
|
||||
tensor-product elements.
|
||||
|
||||
- Parallel anisotropic refinement of hexahedral meshes is now supported,
|
||||
provided that neighboring hexahedra are not refined in conflicting directions.
|
||||
A new ParMesh method is added to check for such conflicts, before refinement.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- The TMOP kernel hierarchy has been restructured to reduce compilation time.
|
||||
Most large kernels have been split into smaller specific kernels for each
|
||||
metric. The directory structure has been updated with assemble, metrics, mult
|
||||
and tools subdirectories. New kernel dispatch and specialization system has
|
||||
also been integrated. Unit tests have been revised to ensure --all tests pass.
|
||||
|
||||
- Introduced NC-patch NURBS meshes, which are conforming element-wise but allow
|
||||
for nonconforming patch topology. This new mesh format supports element
|
||||
spacing formulas for refinement, as well as local refinement factors for a
|
||||
subset of knot vectors.
|
||||
|
||||
- Added support for higher order meshes in Mesh::MakeSimplicial and
|
||||
ParMesh::MakeSimplicial.
|
||||
|
||||
- Added a new miniapp for interpolating a surface grid of points in 3D using a
|
||||
smooth NURBS surface, that can then be sampled at arbitrary resolution while
|
||||
staying close to the original geometry. See miniapps/nurbs/nurbs_surface.
|
||||
|
||||
- Parallel anisotropic refinement of hexahedral meshes is now supported,
|
||||
provided that neighboring hexahedra are not refined in conflicting directions.
|
||||
A new ParMesh method is added to check for such conflicts, before refinement.
|
||||
|
||||
- Added support for higher order meshes in (Par)Mesh::MakeSimplicial.
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added FilteredSolver: a base class for solvers with filtering. It handles
|
||||
cases where a solver performs well except in small subspaces, by adding a
|
||||
filtering step formulated as a subspace correction.
|
||||
|
||||
- Added AMGFSolver: a derived class of FilteredSolver, specialized for AMG with
|
||||
Filtering (AMGF), providing robust preconditioning for linear systems arising
|
||||
in constrained optimization problems such as frictionless contact.
|
||||
|
||||
Added 'GetResiduals' and 'GetFinalAbsResidualNorm' to 'HyprePCG',
|
||||
'HypreGMRES', and 'HypreFGMRES' to get 'r' and '|r|_p'. Note that the latter
|
||||
computes '|r|_p' from 'r' instead of returning a cached value like the
|
||||
relative 'GetFinalResidualNorm'. These require Hypre >= 2.15.0.
|
||||
|
||||
Changed the default solver parameters for 'HyprePCG' to 'tol=1e-6' and
|
||||
'max_iter=1000'. This matches the default parameters in Hypre 3.0.
|
||||
|
||||
Added various helper functions for querying/modifying Hypre solvers:
|
||||
'HypreSmoother::GetType', 'HypreSmoother::GetSOROptions',
|
||||
'HypreSmoother::GetPolyOptions', 'HypreSmoother::GetWindowParameters',
|
||||
'HypreSmoother::IsOperatorSymmetric', 'HyprePCG::GetTol',
|
||||
'HyprePCG::GetAbsTol', 'HyprePCG::GetMaxIter', 'HyprePCG::SetUseTwoNorm',
|
||||
'HypreGMRES::GetTol', 'HypreGMRES::GetAbsTol', 'HypreGMRES::GetMaxIter',
|
||||
'HypreGMRES::GetKDim', 'HypreFGMRES::GetTol', 'HypreFGMRES::GetMaxIter',
|
||||
'HypreFGMRES::GetKDim', and 'HypreBoomerAMG::GetMaxIter'.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added the 'gpu', 'raja-gpu', and 'ceed-gpu' backend aliases/shortcuts which
|
||||
automatically select between CUDA or HIP.
|
||||
|
||||
- Added the option to enable GPU-aware MPI in MFEM using the environment
|
||||
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
|
||||
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
|
||||
|
||||
- Implemented a GPU-accelerated matrix-free AMR derefinement GridFunction update
|
||||
operator. This supports mixed geometry meshes and variable order spaces, and
|
||||
is the default derefinement operator constructed by FiniteElementSpace::Update
|
||||
and ParFiniteElementSpace::Update. The operator requires the finite element
|
||||
space to be nonconforming.
|
||||
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
|
||||
executes on device if either the vector or the array have the device flag
|
||||
set. This is most often used for setting constant essential boundary
|
||||
conditions. A new function Vector::SetSubVectorHost has been added in cases
|
||||
where host execution is always needed (e.g. when the DOFs array is small).
|
||||
|
||||
- Introduced MFEM_FOREACH_THREAD_DIRECT, which directly maps loop tasks to GPU
|
||||
threads, assigning one task per thread.
|
||||
|
||||
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
|
||||
executes on device if either the vector or the array have the device flag
|
||||
set. This is most often used for setting constant essential BCs. A new method,
|
||||
SetSubVectorHost, has been added for cases where host execution is always
|
||||
needed (e.g. when the DOFs array is small).
|
||||
|
||||
- Added GPU support in GradientGridFunction and InnerProduct Coefficient classes
|
||||
by implementing their Project methods.
|
||||
|
||||
- Implemented a GPU-accelerated matrix-free AMR derefinement `GridFunction`
|
||||
update operator. This supports mixed geometry meshes and variable order
|
||||
spaces, and is the default derefinement operator constructed by
|
||||
`FiniteElementSpace::Update` and `ParFiniteElementSpace::Update`.
|
||||
The operator requires `FiniteElementSpace::Nonconforming() == true`.
|
||||
- Added new method: GridFunction::GetGradients, with GPU support, for computing
|
||||
the gradients of a GridFunction on all elements.
|
||||
|
||||
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
|
||||
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
|
||||
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
|
||||
MFEM build configuration.
|
||||
- Added GPU support in GradientGridFunctionCoefficient and
|
||||
InnerProductCoefficient by implementing their Project methods.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added the miniapps/fluids directory and moved the previous Navier and the new
|
||||
incompressible Schrödinger flow miniapps into it.
|
||||
|
||||
- Introduced the new Incompressible Schrödinger Flow (ISF) miniapp, which models
|
||||
inviscid fluid dynamics by solving the linear Schrödinger equation, leveraging
|
||||
the hydrodynamical analogy to quantum mechanics.
|
||||
|
||||
- New particle-related miniapps:
|
||||
* New transient Navier-Stokes fluid-particles solver NavierParticles in
|
||||
miniapps/fluids/navier/navier_particles, for modeling tracer particles in
|
||||
fluid flow, demonstrating use of the new ParticleSet class.
|
||||
* New Navier miniapp, miniapps/fluids/navier/navier_bifurcation, showing the
|
||||
use of NavierParticles in a 2D bifurcating channel flow.
|
||||
* New FindPointsGSLIB miniapp, miniapps/gslib/particles_redist, showing
|
||||
parallel-redistribution of particle data between MPI ranks.
|
||||
* Particle visualization features in common/particles_extras for viewing
|
||||
particle locations and trajectories (ParticleTrajectories) using GLVis.
|
||||
- Added miniapps to demonstrate an implementation of the absolute-value
|
||||
L(1)-Jacobi preconditioners in partially assembled operators. This includes
|
||||
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
|
||||
operators as smoothers.
|
||||
These miniapps can be found in `miniapps/diag-smoothers`.
|
||||
|
||||
- Added a new miniapp (meshing/mesh-bounding-boxes) that computes the bounding
|
||||
boxes for each element of a given mesh, and the bounds on the determinant of
|
||||
@@ -259,44 +95,36 @@ New and updated examples and miniapps
|
||||
of a charged particle, subject to Lorentz forces, in electrostatic and/or
|
||||
magnetostatic fields as computed by the volta or tesla miniapps.
|
||||
|
||||
- Added miniapps to demonstrate an implementation of the absolute-value
|
||||
l1-Jacobi preconditioners in partially assembled operators. This includes
|
||||
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
|
||||
operators as smoothers. See the miniapps/diag-smoothers/ directory.
|
||||
|
||||
- Updated the mtop miniapp with a GPU enabled forward and adjoint solver for
|
||||
isotropic linear elasticity.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Introduced MFEM_FETCH_TPLS CMake option to enable downloading, configuring,
|
||||
and building of TPLs alongside MFEM (currently supported TPLs are hypre,
|
||||
METIS, and GSLIB).
|
||||
|
||||
- Added quadrature function support to the VisIt and Conduit data collections.
|
||||
|
||||
- Added access to the internal parallel matrix in Par(Mixed)BilinearForm and
|
||||
related utility methods for elimination of BCs.
|
||||
|
||||
- FindPointsGSLIB has a new constructor that accepts the mesh object and
|
||||
internally calls the Setup() method so users do not have to. The FreeData()
|
||||
method has also been moved to the destructor so users do not need to manually
|
||||
free-up the memory if the destructor is called before MPI_Finalize().
|
||||
|
||||
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
|
||||
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
|
||||
|
||||
API changes
|
||||
API changes:
|
||||
-----------
|
||||
- mfem::internal::tensor and mfem::internal::dual have been moved to
|
||||
mfem::future::tensor and mfem::future::dual.
|
||||
|
||||
- API addition: in class Operator, added virtual functions: AbsMult, and
|
||||
AbsMultTranspose; in class Vector, added Abs and Pow.
|
||||
- API addition: in class `Operator`, added virtual functions: `AbsMult`, and
|
||||
`AbsMultTranspose`; in class `Vector`, added `Abs` and `Pow`.
|
||||
|
||||
- ParBilinearForm::EliminateEssentialVDofsInRhs() has been deprecated in favor
|
||||
of ParallelEliminateEssentialTDofsInRhs().
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added the "gpu", "raja-gpu", and "ceed-gpu" backend aliases/shortcuts which
|
||||
automatically select between CUDA or HIP.
|
||||
|
||||
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
|
||||
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
|
||||
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
|
||||
MFEM build configuration.
|
||||
|
||||
- Added the option to enable GPU-aware MPI in MFEM using the environment
|
||||
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
|
||||
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
|
||||
|
||||
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
|
||||
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
|
||||
|
||||
- FindPointsGSLIB has a new constructor that accepts the mesh object and
|
||||
internally calls the Setup() method so that the user does not have to.
|
||||
The FreeData() method has also been moved to the destructor so the user does
|
||||
not need to manually free-up the memory if the destructor is called before
|
||||
MPI_Finalize().
|
||||
|
||||
Version 4.8, released on Apr 9, 2025
|
||||
====================================
|
||||
|
||||
+32
-84
@@ -59,7 +59,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.9.1)
|
||||
set(${PROJECT_NAME}_VERSION 4.8.1)
|
||||
|
||||
# Prohibit in-source build
|
||||
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
|
||||
@@ -133,49 +133,33 @@ if (MFEM_USE_CUDA)
|
||||
if (NOT CMAKE_CUDA_HOST_COMPILER)
|
||||
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
if (NOT CMAKE_CUDA_ARCHITECTURES)
|
||||
# make CUDA_ARCH resemble the same form as CMAKE_CUDA_ARCHITECTURES
|
||||
string(REPLACE "sm_" "" CUDA_ARCH_TMP "${CUDA_ARCH}")
|
||||
string(REPLACE "," ";" CUDA_ARCH "${CUDA_ARCH_TMP}")
|
||||
set(CMAKE_CUDA_ARCHITECTURES "${CUDA_ARCH}")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
|
||||
elseif (NOT CMAKE_CUDA_ARCHITECTURES)
|
||||
string(REGEX REPLACE "^sm_" "" ARCH_NUMBER "${CUDA_ARCH}")
|
||||
if ("${CUDA_ARCH}" STREQUAL "sm_${ARCH_NUMBER}")
|
||||
set(CMAKE_CUDA_ARCHITECTURES "${ARCH_NUMBER}")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown CUDA_ARCH: ${CUDA_ARCH}")
|
||||
endif()
|
||||
else()
|
||||
set(CUDA_ARCH "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
|
||||
endif()
|
||||
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
|
||||
enable_language(CUDA)
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
# backup try to detect if this is clang or nvcc
|
||||
if(CMAKE_CUDA_COMPILER MATCHES "nvcc$")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
set(CUDA_FLAGS "-arch=${CMAKE_CUDA_ARCHITECTURES} ${CUDA_FLAGS}")
|
||||
else()
|
||||
# build -gencode sequence for multiple architectures
|
||||
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(CUDA_FLAGS
|
||||
"-gencode arch=compute_${ENTRY},code=sm_${ENTRY} ${CUDA_FLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
else()
|
||||
# build cuda-gpu-arch sequence for multiple architectures
|
||||
# does not support all/all-major/native
|
||||
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(CUDA_FLAGS "-cuda-gpu-arch=sm_${ENTRY} ${CUDA_FLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
# backup try to detect if this is clang or nvcc
|
||||
if(CMAKE_CUDA_COMPILER MATCHES "nvcc$")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
else()
|
||||
# TODO: all, native, all-major require CMake 3.24+
|
||||
# backport support for CMake 3.18 to 3.24
|
||||
if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS
|
||||
"${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
|
||||
"CUDA standard to use.")
|
||||
@@ -258,16 +242,10 @@ endif()
|
||||
|
||||
# AMD HIP
|
||||
if (MFEM_USE_HIP)
|
||||
if (NOT CMAKE_HIP_ARCHITECTURES)
|
||||
if (HIP_ARCH)
|
||||
set(CMAKE_HIP_ARCHITECTURES CACHE STRING "HIP targets to compile for" "${HIP_ARCH}")
|
||||
set(GPU_TARGETS "${HIP_ARCH}" CACHE STRING "HIP targets to compile for" FORCE)
|
||||
endif()
|
||||
else()
|
||||
set(HIP_ARCH CACHE STRING "HIP targets to compile for" "${CMAKE_HIP_ARCHITECTURES}")
|
||||
set(GPU_TARGETS "${CMAKE_HIP_ARCHITECTURES}" CACHE STRING "HIP targets to compile for" FORCE)
|
||||
if (HIP_ARCH)
|
||||
message(STATUS "Using HIP architecture: ${HIP_ARCH}")
|
||||
set(GPU_TARGETS "${HIP_ARCH}" CACHE STRING "HIP targets to compile for")
|
||||
endif()
|
||||
message(STATUS "Using HIP architecture: ${CMAKE_HIP_ARCHITECTURES}")
|
||||
if (ROCM_PATH)
|
||||
list(INSERT CMAKE_PREFIX_PATH 0 ${ROCM_PATH})
|
||||
endif()
|
||||
@@ -300,19 +278,8 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Warn user if deprecated FETCH_TPLS is provided
|
||||
if (DEFINED FETCH_TPLS)
|
||||
message(STATUS "Setting MFEM_FETCH_TPLS to user-provided value of FETCH_TPLS (i.e., MFEM_FETCH_TPLS=${FETCH_TPLS})")
|
||||
set (MFEM_FETCH_TPLS FETCH_TPLS)
|
||||
message(DEPRECATION "The use of FETCH_TPLS is deprecated and will be removed in future verison. Please use MFEM_FETCH_TPLS instead.")
|
||||
endif()
|
||||
|
||||
# Umpire (must be included before hypre, so hypre can use it if needed)
|
||||
# Umpire (must be included before hypre, so hypre can use it if needed)
|
||||
if (MFEM_USE_UMPIRE)
|
||||
# umpire uses FindCUDA, which needs CMP0146=OLD in CMake >= 3.27
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.27.0)
|
||||
cmake_policy(SET CMP0146 OLD)
|
||||
endif()
|
||||
find_package(UMPIRE REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -433,15 +400,6 @@ if (MFEM_USE_STRUMPACK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# cuDSS can only be enabled in CUDA
|
||||
if (MFEM_USE_CUDSS)
|
||||
if (MFEM_USE_CUDA)
|
||||
find_package(CUDSS REQUIRED)
|
||||
else()
|
||||
message(FATAL_ERROR " *** cuDSS requires that CUDA be enabled.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GnuTLS
|
||||
if (MFEM_USE_GNUTLS)
|
||||
find_package(_GnuTLS REQUIRED)
|
||||
@@ -640,7 +598,7 @@ 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 HDF5
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CUDSS CALIPER CODIPACK
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
ALGOIM ENZYME CUDA::cudart)
|
||||
|
||||
@@ -661,8 +619,6 @@ foreach(TPL IN LISTS MFEM_TPLS)
|
||||
endif()
|
||||
endforeach(TPL)
|
||||
|
||||
# reverse to remove the first instance of entries in TPL_LIBRARIES
|
||||
# so later duplicates are kept (for dependency ordering)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES TPL_LIBRARIES)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
@@ -734,7 +690,6 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
# Declaring the library
|
||||
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
|
||||
target_compile_features(mfem PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
|
||||
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
|
||||
if (TPL_TARGETS)
|
||||
@@ -881,12 +836,11 @@ add_dependencies(exec
|
||||
# - https://cmake.org/Bug/view.php?id=8438
|
||||
|
||||
# Add a target to copy the mfem data directory to the build directory
|
||||
# Implementable as a single copy_directory_if_different command w/ CMake >= 3.26
|
||||
file(GLOB DATA_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/data/*)
|
||||
add_custom_target(copy_data
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
|
||||
COMMENT "Syncing the data directory ...")
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/data data
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying the data directory ...")
|
||||
add_custom_target(copy_data DEPENDS data_is_copied)
|
||||
# Add 'copy_data' as a prerequisite for all executables, if the source and the
|
||||
# build directories are not the same.
|
||||
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
@@ -1018,15 +972,9 @@ install(FILES
|
||||
install(EXPORT ${PROJECT_NAME_UC}Targets
|
||||
DESTINATION ${INSTALL_CMAKE_DIR})
|
||||
|
||||
# Install the data directory if present, i.e. if the copy_data target is built
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
DESTINATION ${MFEM_INSTALL_DIR} OPTIONAL)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
|
||||
# define install rules for 'config.mk' and 'test.mk'
|
||||
#-------------------------------------------------------------------------------
|
||||
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
|
||||
option(MFEM_EXPORT_GPU_CONFIG "Export config.mk for GPU-enabled downstream packages" ON)
|
||||
endif()
|
||||
|
||||
mfem_export_mk_files()
|
||||
|
||||
+60
-74
@@ -3,12 +3,12 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -24,14 +24,6 @@ must be made under this license.
|
||||
Note also that MFEM has a [Code of Conduct](CODE_OF_CONDUCT.md). By participating
|
||||
in the MFEM community, you agree to abide by its rules.
|
||||
|
||||
## AI Policy
|
||||
- Use of AI code generation in MFEM is allowed but must be disclosed, e.g. by
|
||||
selecting the `AI-assisted` label on the PR.
|
||||
- By submitting a PR, the author acknowledges that they have reviewed and
|
||||
understand the changes they are proposing.
|
||||
- PR authors are still responsible for correctness, licensing, and attribution
|
||||
of all changes.
|
||||
|
||||
If you plan on contributing to MFEM, consider reviewing the
|
||||
[issue tracker](https://github.com/mfem/mfem/issues) first to check if a thread
|
||||
already exists for your desired feature or the bug you ran into. Use a pull
|
||||
@@ -84,7 +76,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
follow the [MFEM PR Rules](#mfem-pr-rules).
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning two
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning two
|
||||
reviewers to evaluate the changes.
|
||||
- The reviewers have 3 weeks to evaluate the PR and work with the author to
|
||||
fix issues and implement improvements.
|
||||
@@ -125,7 +117,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── petsc
|
||||
│ ├── pumi
|
||||
│ ├── sundials
|
||||
│ └── superlu
|
||||
| └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── dfem
|
||||
@@ -147,22 +139,19 @@ The MFEM source code has the following structure:
|
||||
│ ├── adjoint
|
||||
│ ├── autodiff
|
||||
│ ├── common
|
||||
│ ├── contact
|
||||
│ ├── dfem
|
||||
│ ├── diag-smoothers
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── fluids
|
||||
│ ├── gslib
|
||||
│ ├── hdiv-linear-solver
|
||||
│ ├── hooke
|
||||
│ ├── meshing
|
||||
│ ├── mtop
|
||||
│ ├── multidomain
|
||||
│ ├── navier
|
||||
│ ├── nurbs
|
||||
│ ├── parelag
|
||||
│ ├── performance
|
||||
│ ├── plasma
|
||||
│ ├── shifted
|
||||
│ ├── solvers
|
||||
│ ├── spde
|
||||
@@ -193,15 +182,15 @@ respectively.
|
||||
|
||||
- The main finite element classes are:
|
||||
+ [`FiniteElement`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElementCollection.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1FiniteElementSpace.html)
|
||||
+ [`GridFunction`](https://docs.mfem.org/html/classmfem_1_1GridFunction.html)
|
||||
+ [`BilinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1BilinearFormIntegrator.html) and [`LinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
|
||||
- The main linear algebra classes and sources are
|
||||
+ [`Operator`](https://docs.mfem.org/html/classmfem_1_1Operator.html) and [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1Vector.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`DenseMatrix`](https://docs.mfem.org/html/classmfem_1_1DenseMatrix.html) and [`SparseMatrix`](https://docs.mfem.org/html/classmfem_1_1SparseMatrix.html)
|
||||
+ Sparse [smoothers](https://docs.mfem.org/html/sparsesmoothers_8hpp.html) and linear [solvers](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
|
||||
@@ -213,8 +202,8 @@ shared geometric entities between different tasks. The parallel source files
|
||||
have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
|
||||
- The main parallel classes are
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParNCMesh.html)
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParFiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1ParFiniteElementSpace.html)
|
||||
+ [`ParGridFunction`](https://docs.mfem.org/html/classmfem_1_1ParGridFunction.html)
|
||||
+ [`ParBilinearForm`](https://docs.mfem.org/html/classmfem_1_1ParBilinearForm.html) and [`ParLinearForm`](https://docs.mfem.org/html/classmfem_1_1ParLinearForm.html)
|
||||
@@ -224,14 +213,14 @@ have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
#### GPU and general device support
|
||||
|
||||
GPU and multi-core CPU support is based on device kernels supporting different
|
||||
backends (CUDA, HIP, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
backends (CUDA, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
device/host memory manager.
|
||||
|
||||
- The main device-relevant classes and sources are:
|
||||
+ [`Device`](https://docs.mfem.org/html/device_8hpp.html)
|
||||
+ [`MemoryManager`](https://docs.mfem.org/html/mem_manager_8hpp.html)
|
||||
+ the [`mfem::forall`](https://docs.mfem.org/html/forall_8hpp.html) function
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html), [`hip.hpp`](https://docs.mfem.org/html/hip_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
|
||||
#### Utilities, building and documentation
|
||||
- The `general/` directory contains C++ classes that serve as utilities for
|
||||
@@ -245,8 +234,8 @@ device/host memory manager.
|
||||
- `examples` and `miniapps` respectively gather simple and more fully-featured
|
||||
demonstrations of the usage on MFEM. They both rely on `data/` for the
|
||||
collection of meshes.
|
||||
- The `tests/` directory contains a unit test suite, additional tests, and
|
||||
benchmarks.
|
||||
- The `tests/` directory contains a unit test suite and will later contain more
|
||||
tests that run example codes.
|
||||
|
||||
See also the [code overview](https://mfem.org/code-overview/) section on the MFEM
|
||||
website.
|
||||
@@ -280,8 +269,8 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
the top of https://github.com/mfem.
|
||||
- Consider making your membership public by going to https://github.com/orgs/mfem/people
|
||||
and clicking on the organization visibility drop box next to your name.
|
||||
- Project discussions and announcements will be posted at https://github.com/orgs/mfem/discussions,
|
||||
tagging the `@mfem/everyone` team when appropriate.
|
||||
- Project discussions and announcements will be posted at
|
||||
https://github.com/orgs/mfem/teams/everyone.
|
||||
|
||||
#### Structure
|
||||
- The MFEM source code is in the [mfem](https://github.com/mfem/mfem)
|
||||
@@ -341,12 +330,11 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Well-designed simple code is frequently more general and powerful.
|
||||
- Lean code base is easier to understand by new collaborators.
|
||||
- New features should be added only if they are necessary or generally useful.
|
||||
- Introduction of language constructs not currently used in MFEM should be
|
||||
- Introduction of language constructions not currently used in MFEM should be
|
||||
justified and generally avoided (to maintain portability to various systems
|
||||
and compilers, including early access hardware).
|
||||
- We prefer basic C++. Use C++17 features judiciously, prioritizing readability,
|
||||
consistency with existing MFEM code, and portability to different systems,
|
||||
compilers and device backends.
|
||||
- We prefer basic C++ and the C++03 standard, to keep the code readable by
|
||||
a large audience and to make sure it compiles anywhere.
|
||||
|
||||
- *Keep the code general and reasonably efficient*
|
||||
- The main goal is fast prototyping for research and application development.
|
||||
@@ -389,7 +377,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- When your branch is ready for other developers to review / comment on
|
||||
the code, create a pull request towards `mfem:master`.
|
||||
|
||||
- Pull requests typically have titles like:
|
||||
- Pull request typically have titles like:
|
||||
|
||||
`Description [new-feature-dev]`
|
||||
|
||||
@@ -410,12 +398,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Add a description, appropriate labels and assign yourself to the PR. The MFEM
|
||||
team will add reviewers as appropriate.
|
||||
|
||||
- List outstanding TODO items in the description.
|
||||
- List outstanding TODO items in the description, see PR #222 for an example.
|
||||
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
or request the `ready-for-review` label.
|
||||
the `ready-for-review` label.
|
||||
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning
|
||||
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
|
||||
the PR and work with the author to implement improvements and fix issues.
|
||||
|
||||
@@ -441,7 +429,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
checks in GitHub Actions enforce MFEM-specific rules which are explained in
|
||||
the error messages and the `tests/scripts` directory.
|
||||
|
||||
- Also note that the tests `branch-history` and `repo-check` found in GitHub
|
||||
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
|
||||
Actions can be triggered automatically before each push using git hooks. See
|
||||
the [git hooks README](config/githooks/README.md) for a detailed explanation.
|
||||
|
||||
@@ -498,15 +486,15 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a
|
||||
|
||||
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
|
||||
|
||||
4. To seek help from the editors in case of difficulties.
|
||||
3. To seek help from the editors in case of difficulties.
|
||||
|
||||
5. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
4. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
|
||||
6. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
7. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
|
||||
8. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
|
||||
#### Responsibilities of Authors
|
||||
|
||||
@@ -532,30 +520,30 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Code builds.
|
||||
- [ ] Code passes `make style`.
|
||||
- [ ] Update `CHANGELOG`:
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Update `INSTALL`:
|
||||
- [ ] Has a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Had a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update `.gitignore`:
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] New examples:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] Add the example code to the appropriate `SEQ_EXAMPLES` and `PAR_EXAMPLES` variables.
|
||||
- [ ] Add any files generated by it to the `clean` target.
|
||||
- [ ] Add the example binary and any files generated by it to the top-level `.gitignore` file.
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Add the example code to the `ALL_EXE_SRCS` variable.
|
||||
- [ ] Make sure `THIS_TEST_OPTIONS` is set correctly for the new example.
|
||||
- [ ] List the new example in `doc/CodeDocumentation.dox`.
|
||||
- [ ] If new examples directory (e.g. `examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new examples directory (e.g.`examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add example-specific documentation, see e.g. the `src/examples.md`.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
@@ -572,13 +560,13 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Add/update the `CMakeLists.txt` file in the new miniapp directory.
|
||||
- [ ] Consider adding a new test for the new miniapp.
|
||||
- [ ] List the new miniapp in `doc/CodeDocumentation.dox`
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New capability:
|
||||
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
|
||||
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
|
||||
@@ -680,7 +668,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- [ ] Update URL shortlinks:
|
||||
- [ ] Create a shortlink at [http://bit.ly/](http://bit.ly/) for the release tarball, e.g. https://mfem.github.io/releases/mfem-3.1.tgz.
|
||||
- [ ] (LLNL only) Add and commit the new shortlink in the `links` and `links-mfem` files of the internal `mfem/downloads` repo.
|
||||
- [ ] Add the new shortlinks to the MFEM package in `spack`.
|
||||
- [ ] Add the new shortlinks to the MFEM packages in `spack`, `homebrew/science`, `VisIt`, etc.
|
||||
- [ ] Update website in `mfem/web` repo:
|
||||
- Update version and shortlinks in `src/index.md` and `src/download.md`.
|
||||
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
|
||||
@@ -732,24 +720,22 @@ commit or push, see the [README](config/githooks/README.md) in the `config/githo
|
||||
directory.
|
||||
|
||||
|
||||
### GitHub Actions smoke tests
|
||||
|
||||
### Linux and Mac smoke tests
|
||||
We use GitHub Actions to drive the default tests on the `master` and `next`
|
||||
branches. See the `.github/workflows` files and the logs at
|
||||
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
|
||||
|
||||
GitHub Actions testing should be kept lightweight, as there is a time
|
||||
constraint on jobs. The current workflows cover Linux, macOS, and Windows
|
||||
configurations.
|
||||
Testing using GitHub Actions should be kept lightweight, as there is a time
|
||||
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
|
||||
|
||||
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
|
||||
- Tests on the `next` branch are currently scheduled to run each night.
|
||||
|
||||
### Additional Windows smoke test
|
||||
|
||||
We also use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor.yml` file
|
||||
and the build logs at
|
||||
### Windows smoke test
|
||||
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor` file and the
|
||||
build logs at
|
||||
[https://ci.appveyor.com/project/mfem/mfem](https://ci.appveyor.com/project/mfem/mfem).
|
||||
|
||||
CMake is used to generate the MSVC Project files and drive the build. A release
|
||||
|
||||
@@ -38,13 +38,14 @@ the option MFEM_USE_METIS.
|
||||
MFEM also includes support for devices such as GPUs, and programming models such
|
||||
as CUDA, HIP, OCCA, OpenMP and RAJA.
|
||||
|
||||
- Starting with version 4.9, MFEM requires a C++17 compiler.
|
||||
- Starting with version 4.0, MFEM requires a C++11 compiler. We recommend using
|
||||
a newer compiler, e.g. GCC version 4.9 or higher.
|
||||
|
||||
- CUDA support requires an NVIDIA GPU and an installation of the CUDA Toolkit
|
||||
https://developer.nvidia.com/cuda-toolkit
|
||||
|
||||
- HIP support requires an AMD GPU and an installation of the ROCm software stack
|
||||
https://rocm.docs.amd.com
|
||||
https://rocmdocs.amd.com
|
||||
|
||||
- OCCA support requires the OCCA library
|
||||
https://libocca.org
|
||||
@@ -82,9 +83,9 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(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
|
||||
@@ -114,15 +115,15 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
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)
|
||||
|
||||
Parallel build with fetching of hypre and METIS:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DMFEM_FETCH_TPLS=YES
|
||||
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DFETCH_TPLS=YES
|
||||
make -j 4
|
||||
|
||||
CUDA build:
|
||||
@@ -133,8 +134,7 @@ CUDA build:
|
||||
|
||||
HIP build:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 \
|
||||
-DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
make -j 4
|
||||
|
||||
Example codes (serial/parallel, depending on the build):
|
||||
@@ -269,7 +269,6 @@ Compilers:
|
||||
CXX - C++ compiler, serial build
|
||||
MPICXX - MPI C++ compiler, parallel build
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
|
||||
HIP_CXX - The HIP compiler, e.g. 'hipcc'
|
||||
|
||||
Compiler options:
|
||||
OPTIM_FLAGS - Options for optimized build
|
||||
@@ -396,11 +395,6 @@ MFEM_USE_STRUMPACK = YES/NO
|
||||
classes. When enabled, this option uses the STRUMPACK_* library options, see
|
||||
below.
|
||||
|
||||
MFEM_USE_CUDSS = YES/NO
|
||||
Enable MFEM functionality based on the cuDSS library. When using cuDSS, CUDA
|
||||
support must be also enabled in MFEM, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
When enabled, this option uses the CUDSS_* library options, see below.
|
||||
|
||||
MFEM_USE_GINKGO = YES/NO
|
||||
Enable MFEM functionality based on the Ginkgo library, which provides
|
||||
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
|
||||
@@ -560,13 +554,13 @@ MFEM_USE_RAJA = YES/NO
|
||||
MFEM_USE_OCCA = YES/NO
|
||||
Enables support for the OCCA library in MFEM. OCCA is an open-source library
|
||||
which aims to make it easy to program different types of devices (e.g. CPU,
|
||||
GPU, FPGA) by providing a unified API for interacting with JIT-compiled
|
||||
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
|
||||
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
|
||||
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
|
||||
MFEM_USE_GSLIB = YES/NO
|
||||
Enables MFEM functionality based on the GSLIB library, and specifically its
|
||||
FindPoints component, which provides robust algorithms to evaluate finite
|
||||
FindPoints component, which provides a robust algorithms to evaluate finite
|
||||
element functions in a collection of points in physical space. When enabled,
|
||||
the user can use the GSLIB-FindPoints methods as shown in miniapps/gslib.
|
||||
|
||||
@@ -725,24 +719,13 @@ The specific libraries and their options are:
|
||||
Options: STRUMPACK_OPT, STRUMPACK_LIB.
|
||||
Versions: STRUMPACK >= 3.0.0.
|
||||
|
||||
- CUDSS (optional), used when MFEM_USE_CUDSS = YES. Note that CUDSS requires
|
||||
CUDA 12.x toolkit and the cuDSS libraries. The supported communication backend
|
||||
is OpenMPI 4.x (default), and OpenMPI 4.x or a later version must be pre-built.
|
||||
The source files in the cuDSS tarball provide guidance for developing custom
|
||||
MPI implementations.
|
||||
URL: https://developer.nvidia.com/cudss
|
||||
https://docs.nvidia.com/cuda/cudss/advanced_features.html#communication-layer-library-in-cudss
|
||||
Options: CUDSS_OPT, CUDSS_LIB.
|
||||
Versions: cuDSS >= 0.6.0.
|
||||
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Ginkgo may have additional
|
||||
requirements and module-specific dependencies; see the webpage below.
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
|
||||
C++ compiler that supports the C++-17 standard. For additional requirements
|
||||
and dependencies of specific modules, see the Ginkgo webpage below.
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
|
||||
Debug).
|
||||
Versions: Ginkgo >= 1.9.0. When building Ginkgo with distributed support, a
|
||||
recent version of the "develop" branch is required (1.11 as defined
|
||||
in include/ginkgo/config.hpp).
|
||||
Versions: Ginkgo >= 1.9.0.
|
||||
|
||||
- AmgX (optional), used when MFEM_USE_AMGX = YES.
|
||||
URL: https://github.com/NVIDIA/AMGX
|
||||
@@ -808,7 +791,7 @@ The specific libraries and their options are:
|
||||
Options: CONDUIT_OPT, CONDUIT_LIB.
|
||||
Versions: Conduit >= 0.3.1.
|
||||
|
||||
- ADIOS2 (optional), used when MFEM_USE_ADIOS2 = YES.
|
||||
- ADIOS2 (optional) used when MFEM_USE_ADIOS2 = YES.
|
||||
URL: https://adios2.readthedocs.io/
|
||||
Versions: ADIOS >= 2.5.0.
|
||||
|
||||
@@ -884,7 +867,7 @@ The specific libraries and their options are:
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 2022.10.3.
|
||||
|
||||
- Moonolith (optional), used when MFEM_USE_MOONOLITH = YES.
|
||||
- Moonolith (optional), use when MFEM_USE_MOONOLITH = YES.
|
||||
URL: https://bitbucket.org/zulianp/par_moonolith
|
||||
Options: MOONOLITH_DIR
|
||||
Versions: MOONOLITH >= 1.1.0.
|
||||
@@ -972,7 +955,7 @@ CMAKE_BUILD_TYPE which can be set to standard values like "Debug", and "Release"
|
||||
To use a specific generator use the "-G <generator>" option of cmake:
|
||||
|
||||
cmake <mfem-source-dir> -G "Xcode"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 17 2022"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 12 2013"
|
||||
cmake <mfem-source-dir> -G "MinGW Makefiles"
|
||||
|
||||
With CMake it is possible to build MFEM as a shared library using the standard
|
||||
@@ -1098,10 +1081,9 @@ The following options are CMake specific:
|
||||
MFEM_ENABLE_TESTING - Enable the ctest framework for testing.
|
||||
MFEM_ENABLE_EXAMPLES - Build all of the examples by default.
|
||||
MFEM_ENABLE_MINIAPPS - Build all of the miniapps by default.
|
||||
MFEM_FETCH_TPLS - Enable fetching of all supported third-party libraries.
|
||||
MFEM_FETCH_GSLIB - Enable fetching of gslib.
|
||||
MFEM_FETCH_HYPRE - Enable fetching of hypre.
|
||||
MFEM_FETCH_METIS - Enable fetching of metis.
|
||||
FETCH_TPLS - Enable fetching of all supported third-party libraries.
|
||||
HYPRE_FETCH - Enable fetching of hypre.
|
||||
METIS_FETCH - Enable fetching of metis.
|
||||
|
||||
External libraries (CMake):
|
||||
---------------------------
|
||||
@@ -1167,7 +1149,6 @@ The MFEM CMake build system also provides fetching (automated building) for the
|
||||
packages/libraries listed below. Note that when fetching is enabled, any related
|
||||
auto-detection functionality is disabled.
|
||||
|
||||
- GSLIB
|
||||
- HYPRE
|
||||
- METIS
|
||||
|
||||
@@ -1217,7 +1198,7 @@ larger problems, there are two options:
|
||||
Specific options for HIP
|
||||
========================
|
||||
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
|
||||
ROCm install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
|
||||
Specific options for RAJA+HIP+MPI
|
||||
=================================
|
||||
|
||||
@@ -28,7 +28,6 @@ license files. These software products and their licenses are as follows:
|
||||
* AmgXWrapper (linalg/amgxsolver.{hpp,cpp}) -- MIT license
|
||||
* Catch++ (tests/unit/catch.hpp) -- Boost 1.0 license
|
||||
* Gecko (general/gecko.{cpp,hpp}) -- BSD 3-clause license
|
||||
* gslib (fem/gslib.{cpp,hpp}, mesh/bb_grid_map.{cpp,hpp}) -- BSD 3-clause license
|
||||
* Picojson (fem/picojson.h) -- Custom 2-clause license
|
||||
* TinyXML2 (general/tinyxml2.{cpp,h}) -- zlib license
|
||||
* Zstr (general/zstr.hpp) -- MIT license
|
||||
|
||||
@@ -35,7 +35,6 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
|
||||
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
|
||||
set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
|
||||
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
|
||||
set(MFEM_USE_CUDSS @MFEM_USE_CUDSS@)
|
||||
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
|
||||
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
|
||||
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
|
||||
@@ -110,14 +109,6 @@ if (MFEM_USE_RAJA)
|
||||
find_dependency(RAJA)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CUDSS)
|
||||
find_dependency(cudss)
|
||||
endif (MFEM_USE_CUDSS)
|
||||
|
||||
if (MFEM_USE_UMPIRE)
|
||||
find_dependency(umpire)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET mfem)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/MFEMTargets.cmake)
|
||||
endif (NOT TARGET mfem)
|
||||
|
||||
@@ -108,15 +108,6 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
#cmakedefine MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
#cmakedefine MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
#cmakedefine MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
#cmakedefine MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable functionality based on the Ginkgo library.
|
||||
#cmakedefine MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
if (NOT cudss_DIR AND CUDSS_DIR)
|
||||
set(cudss_DIR ${CUDSS_DIR}/lib/cmake/cudss)
|
||||
endif()
|
||||
message(STATUS "Looking for CUDSS ...")
|
||||
message(STATUS " in CUDSS_DIR = ${CUDSS_DIR}")
|
||||
message(STATUS " cudss_DIR = ${cudss_DIR}")
|
||||
find_package(cudss)
|
||||
set(CUDSS_FOUND ${cudss_FOUND})
|
||||
set(CUDSS_LIBRARIES "cudss")
|
||||
if (CUDSS_FOUND)
|
||||
message(STATUS
|
||||
"Found CUDSS target: ${CUDSS_LIBRARIES} (version: ${cudss_VERSION})")
|
||||
else()
|
||||
set(msg STATUS)
|
||||
if (CUDSS_FIND_REQUIRED)
|
||||
set(msg FATAL_ERROR)
|
||||
endif()
|
||||
message(${msg}
|
||||
"CUDSS not found. Please set CUDSS_DIR to the install prefix.")
|
||||
endif()
|
||||
|
||||
if(CUDSS_FOUND AND TARGET cudss)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION)
|
||||
if(NOT CUDSS_LIBRARY_LOCATION)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
if(CUDSS_LIBRARY_LOCATION)
|
||||
get_filename_component(CUDSS_LIBRARY_DIR "${CUDSS_LIBRARY_LOCATION}" DIRECTORY)
|
||||
else()
|
||||
message(WARNING "Could not determine the location of the cuDSS library.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "cuDSS target not available; cannot determine library directory.")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS threading library if OpenMP is enabled.
|
||||
# The threading layer library (libcudss_mtlayer_gomp.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
if (MFEM_USE_OPENMP)
|
||||
find_file(
|
||||
CUDSS_THREADING_LIB
|
||||
NAMES libcudss_mtlayer_gomp.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_THREADING_LIB AND CUDSS_THREADING_LIB)
|
||||
set(MFEM_CUDSS_THREADING_LIB "${CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS threading layer library: ${MFEM_CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS communication library if MFEM use OpenMPI.
|
||||
# The communication layer library (libcudss_commlayer_mpi.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
# The communication layer library is used pre-built communication layers for OpenMPI
|
||||
# by default.
|
||||
if (MFEM_USE_MPI)
|
||||
find_file(
|
||||
CUDSS_COMM_LIB
|
||||
NAMES libcudss_commlayer_openmpi.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_COMM_LIB AND CUDSS_COMM_LIB)
|
||||
set(MFEM_CUDSS_COMM_LIB "${CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS communication layer library: ${MFEM_CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
@@ -9,45 +9,10 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Defines the following variables if fetching of TPLs is disabled (default):
|
||||
# Defines the following variables:
|
||||
# - GSLIB_FOUND
|
||||
# - GSLIB_LIBRARIES
|
||||
# - GSLIB_INCLUDE_DIRS
|
||||
# otherwise, the following are defined:
|
||||
# - GSLIB (imported library target)
|
||||
|
||||
if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
|
||||
enable_language(C)
|
||||
set(GSLIB_FETCH_VERSION 1.0.9)
|
||||
add_library(GSLIB STATIC IMPORTED)
|
||||
# set options (technically flags because GSLIB does not use cmake)
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(GSLIB_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(GSLIB_FLAGS "${GSLIB_FLAGS} -fPIC")
|
||||
endif()
|
||||
# define external project and create future include directory so it is present
|
||||
# to pass CMake checks at end of MFEM configuration step
|
||||
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_FLAGS}")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/gslib)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(gslib
|
||||
GIT_REPOSITORY https://github.com/Nek5000/gslib
|
||||
GIT_TAG v${GSLIB_FETCH_VERSION}
|
||||
GIT_SHALLOW TRUE
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS=${GSLIB_FLAGS}"
|
||||
INSTALL_COMMAND "")
|
||||
file(MAKE_DIRECTORY ${PREFIX}/include)
|
||||
# set imported library target properties
|
||||
add_dependencies(GSLIB gslib)
|
||||
set_target_properties(GSLIB PROPERTIES
|
||||
IMPORTED_LOCATION ${PREFIX}/lib/libgs.a
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${PREFIX}/include)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(GSLIB GSLIB GSLIB_DIR "include" gslib.h "lib" gs
|
||||
|
||||
@@ -37,24 +37,21 @@ if (HYPRE_FOUND OR TARGET HYPRE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
|
||||
set(HYPRE_FETCH_VERSION 2.33.0)
|
||||
set(HYPRE_FETCH_TAG "v${HYPRE_FETCH_VERSION}" CACHE STRING "Tag, branch, or commit for HYPRE")
|
||||
add_library(HYPRE STATIC IMPORTED)
|
||||
# set options and associated dependencies
|
||||
if (HYPRE_FETCH OR FETCH_TPLS)
|
||||
# Collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
|
||||
set(HYPRE_CMAKE_OPTIONS "")
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (BUILD_SHARED_LIBS)
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON)
|
||||
endif()
|
||||
# collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
|
||||
get_cmake_property(all_vars VARIABLES)
|
||||
foreach(var ${all_vars})
|
||||
if(var MATCHES "^HYPRE_ENABLE")
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS "-D${var}:BOOL=${${var}}")
|
||||
endif()
|
||||
endforeach()
|
||||
# process all MFEM_USE variables that impact hypre
|
||||
|
||||
set(HYPRE_FETCH_VERSION 2.33.0)
|
||||
set(HYPRE_FETCH_TAG "v${HYPRE_FETCH_VERSION}" CACHE STRING "Tag, branch, or commit for HYPRE")
|
||||
add_library(HYPRE STATIC IMPORTED)
|
||||
# set options and associated dependencies
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (MFEM_USE_CUDA)
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DHYPRE_ENABLE_CUDA:BOOL=ON -DCMAKE_CUDA_ARCHITECTURES:STRING=${CMAKE_CUDA_ARCHITECTURES})
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
@@ -98,6 +95,7 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
SOURCE_SUBDIR src
|
||||
PREFIX ${HYPRE_INSTALL}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . -- -j${CMAKE_BUILD_PARALLEL_LEVEL}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${HYPRE_INSTALL} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${HYPRE_CMAKE_OPTIONS})
|
||||
file(MAKE_DIRECTORY ${HYPRE_INSTALL}/include)
|
||||
# set imported library target properties
|
||||
|
||||
@@ -18,19 +18,11 @@
|
||||
# - METIS (imported library target)
|
||||
# - METIS_VERSION_5 (cache variable)
|
||||
|
||||
if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
|
||||
enable_language(C)
|
||||
if (METIS_FETCH OR FETCH_TPLS)
|
||||
set(METIS_FETCH_VERSION 4.0.3)
|
||||
add_library(METIS STATIC IMPORTED)
|
||||
# set options (technically flags because METIS does not use cmake)
|
||||
set(METIS_FLAGS "-Wno-implicit-int -Wno-incompatible-pointer-types")
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(METIS_FLAGS "${METIS_FLAGS} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(METIS_FLAGS "${METIS_FLAGS} -fPIC")
|
||||
endif()
|
||||
# define external project
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with ${METIS_FLAGS}")
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with default options")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/metis)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(metis
|
||||
@@ -40,7 +32,7 @@ if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
|
||||
BUILD_COMMAND $(MAKE) clean && $(MAKE) "OPTFLAGS=${METIS_FLAGS}"
|
||||
BUILD_COMMAND $(MAKE) COPTIONS=-Wno-incompatible-pointer-types
|
||||
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
|
||||
# set imported library target properties
|
||||
add_dependencies(METIS metis)
|
||||
|
||||
@@ -22,15 +22,15 @@ include(MfemCmakeUtilities)
|
||||
mfem_find_package(SuiteSparse SuiteSparse SuiteSparse_DIR "" "" "" ""
|
||||
"Paths to headers required by SuiteSparse."
|
||||
"Libraries required by SuiteSparse."
|
||||
ADD_COMPONENT "UMFPACK" "include;include/suitesparse;suitesparse" umfpack.h "lib" umfpack
|
||||
ADD_COMPONENT "KLU" "include;include/suitesparse;suitesparse" klu.h "lib" klu
|
||||
ADD_COMPONENT "AMD" "include;include/suitesparse;suitesparse" amd.h "lib" amd
|
||||
ADD_COMPONENT "BTF" "include;include/suitesparse;suitesparse" btf.h "lib" btf
|
||||
ADD_COMPONENT "CHOLMOD" "include;include/suitesparse;suitesparse" cholmod.h "lib" cholmod
|
||||
ADD_COMPONENT "COLAMD" "include;include/suitesparse;suitesparse" colamd.h "lib" colamd
|
||||
ADD_COMPONENT "CAMD" "include;include/suitesparse;suitesparse" camd.h "lib" camd
|
||||
ADD_COMPONENT "CCOLAMD" "include;include/suitesparse;suitesparse" ccolamd.h "lib" ccolamd
|
||||
ADD_COMPONENT "config" "include;include/suitesparse;suitesparse" SuiteSparse_config.h "lib"
|
||||
ADD_COMPONENT "UMFPACK" "include;suitesparse" umfpack.h "lib" umfpack
|
||||
ADD_COMPONENT "KLU" "include;suitesparse" klu.h "lib" klu
|
||||
ADD_COMPONENT "AMD" "include;suitesparse" amd.h "lib" amd
|
||||
ADD_COMPONENT "BTF" "include;suitesparse" btf.h "lib" btf
|
||||
ADD_COMPONENT "CHOLMOD" "include;suitesparse" cholmod.h "lib" cholmod
|
||||
ADD_COMPONENT "COLAMD" "include;suitesparse" colamd.h "lib" colamd
|
||||
ADD_COMPONENT "CAMD" "include;suitesparse" camd.h "lib" camd
|
||||
ADD_COMPONENT "CCOLAMD" "include;suitesparse" ccolamd.h "lib" ccolamd
|
||||
ADD_COMPONENT "config" "include;suitesparse" SuiteSparse_config.h "lib"
|
||||
suitesparseconfig)
|
||||
|
||||
if (SuiteSparse_FOUND AND METIS_VERSION_5)
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
# - UMPIRE_LIBRARIES
|
||||
# - UMPIRE_INCLUDE_DIRS
|
||||
|
||||
if (NOT umpire_ROOT AND UMPIRE_DIR)
|
||||
set(umpire_ROOT ${UMPIRE_DIR})
|
||||
if (NOT umpire_DIR AND UMPIRE_DIR)
|
||||
set(umpire_DIR ${UMPIRE_DIR}/lib/cmake/umpire)
|
||||
endif()
|
||||
message(STATUS "Looking for UMPIRE ...")
|
||||
message(STATUS " in UMPIRE_DIR = ${UMPIRE_DIR}")
|
||||
message(STATUS " umpire_ROOT = ${umpire_ROOT}")
|
||||
message(STATUS " umpire_DIR = ${umpire_DIR}")
|
||||
find_package(umpire CONFIG)
|
||||
set(UMPIRE_FOUND ${umpire_FOUND})
|
||||
set(UMPIRE_LIBRARIES "umpire")
|
||||
|
||||
@@ -701,6 +701,7 @@ endfunction(mfem_find_library)
|
||||
# Extract compile and link options needed by the given target.
|
||||
#
|
||||
function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
|
||||
if (NOT TARGET ${Target})
|
||||
return()
|
||||
endif()
|
||||
@@ -798,12 +799,7 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
# message(STATUS "Lib = ${Lib}")
|
||||
# Filter-out generator expressions
|
||||
if (NOT ("${Lib}" MATCHES "^\\$"))
|
||||
if(NOT ("${Lib}" STREQUAL "dl"))
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
else()
|
||||
# for some reason libdl doesn't include the "-l"
|
||||
list(APPEND LinkOpts "-ldl")
|
||||
endif()
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
endif()
|
||||
else()
|
||||
mfem_get_target_options(${Lib} COpts LOpts)
|
||||
@@ -892,18 +888,9 @@ function(mfem_export_mk_files)
|
||||
set(${var} NO)
|
||||
endif()
|
||||
endforeach()
|
||||
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
|
||||
set(MFEM_CXX ${CMAKE_CUDA_COMPILER})
|
||||
if(MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_HOST_CXX ${CMAKE_CUDA_HOST_COMPILER})
|
||||
else()
|
||||
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
else()
|
||||
# mfem doesn't use enable_language(HIP)
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
# TODO: Add support for MFEM_USE_CUDA=YES
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${MFEM_CXX})
|
||||
set(MFEM_CPPFLAGS "")
|
||||
get_target_property(cxx_std mfem CXX_STANDARD)
|
||||
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
|
||||
@@ -913,50 +900,6 @@ function(mfem_export_mk_files)
|
||||
string(STRIP
|
||||
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
|
||||
MFEM_CXXFLAGS)
|
||||
if(MFEM_EXPORT_GPU_CONFIG)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} ${CMAKE_CUDA_FLAGS}")
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_CXXFLAGS "-x=cu ${MFEM_CXXFLAGS} -ccbin ${CMAKE_CXX_COMPILER} --forward-unknown-to-host-compiler")
|
||||
# The following intentionally hides CUDA deprecation warnings
|
||||
foreach(ENTRY IN LISTS CUDAToolkit_INCLUDE_DIRS)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -isystem ${ENTRY}")
|
||||
endforeach()
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
|
||||
# architecture flags not part of CMAKE_CUDA_FLAGS
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -arch=${CMAKE_CUDA_ARCHITECTURES}")
|
||||
else()
|
||||
foreach (ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS
|
||||
"${MFEM_CXXFLAGS} -gencode arch=compute_${ENTRY},code=sm_${ENTRY}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xcuda --cuda-path=${CUDAToolkit_LIBRARY_ROOT}")
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
|
||||
# architecture flags not part of CMAKE_CUDA_FLAGS
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
# TODO: not supported
|
||||
else()
|
||||
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS "-cuda-gpu-arch=sm_${ENTRY} ${MFEM_CXXFLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xhip")
|
||||
foreach(ENTRY IN LISTS CMAKE_HIP_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS "--offload-arch=${ENTRY} ${MFEM_CXXFLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
set(MFEM_TPLFLAGS "")
|
||||
foreach(dir ${TPL_INCLUDE_DIRS})
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} -I${dir}")
|
||||
@@ -987,9 +930,6 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_SHARED NO)
|
||||
set(MFEM_STATIC YES)
|
||||
endif()
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} -lcudart")
|
||||
endif()
|
||||
set(MFEM_BUILD_TAG "${CMAKE_SYSTEM}")
|
||||
set(MFEM_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
# For the next 4 variables, these are the values for the build-tree version of
|
||||
@@ -998,15 +938,8 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_LIB_DIR "${PROJECT_BINARY_DIR}")
|
||||
set(MFEM_TEST_MK "${PROJECT_SOURCE_DIR}/config/test.mk")
|
||||
set(MFEM_CONFIG_EXTRA "MFEM_BUILD_DIR ?= ${PROJECT_BINARY_DIR}")
|
||||
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_XLINKER "-Xlinker=")
|
||||
else()
|
||||
set(MFEM_XLINKER "${CMAKE_CUDA_LINKER_WRAPPER_FLAG}")
|
||||
endif()
|
||||
else()
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
endif()
|
||||
# TODO: CUDA/HIP support:
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
set(MFEM_MPIEXEC ${MPIEXEC})
|
||||
if (NOT MFEM_MPIEXEC)
|
||||
set(MFEM_MPIEXEC "mpirun")
|
||||
@@ -1054,21 +987,16 @@ function(mfem_export_mk_files)
|
||||
# handle interfaces (e.g., SCOREC::apf)
|
||||
if ("${lib}" MATCHES "SCOREC::.*" OR "${lib}" MATCHES "Ginkgo::.*" OR "${lib}" MATCHES "ParMoonolith::.*")
|
||||
elseif (TARGET "${lib}")
|
||||
mfem_get_target_options(${lib} CompileOpts2 LinkOpts2)
|
||||
# remove generator expressions
|
||||
string(GENEX_STRIP "${CompileOpts2}" CompileOpts)
|
||||
string(GENEX_STRIP "${LinkOpts2}" LinkOpts)
|
||||
mfem_get_target_options(${lib} CompileOpts LinkOpts)
|
||||
# Removing duplicates may lead to issues:
|
||||
# list(REMOVE_DUPLICATES CompileOpts)
|
||||
# list(REMOVE_DUPLICATES LinkOpts)
|
||||
# message(WARNING "${lib}[LinkOpts]: ${LinkOpts}")
|
||||
# message(WARNING "${lib}[CompileOpts]: ${CompileOpts}")
|
||||
foreach(LOpt IN LISTS LinkOpts)
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpt}")
|
||||
endforeach()
|
||||
foreach(COpt IN LISTS CompileOpts)
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpt}")
|
||||
endforeach()
|
||||
string(REPLACE ";" " " COpts "${CompileOpts}")
|
||||
string(REPLACE ";" " " LOpts "${LinkOpts}")
|
||||
# message(STATUS "${lib}[COpts]: '${COpts}'")
|
||||
# message(STATUS "${lib}[LOpts]: '${LOpts}'")
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpts}")
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpts}")
|
||||
# message(FATAL_ERROR "***** interface lib found ... exiting *****")
|
||||
# handle static and shared libs
|
||||
elseif ("${suffix}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
@@ -1076,7 +1004,7 @@ function(mfem_export_mk_files)
|
||||
get_filename_component(fullLibName ${lib} NAME_WE)
|
||||
string(REGEX REPLACE "^lib" "" libname ${fullLibName})
|
||||
set(MFEM_EXT_LIBS
|
||||
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
|
||||
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
|
||||
else()
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${lib}")
|
||||
endif()
|
||||
@@ -1085,7 +1013,7 @@ function(mfem_export_mk_files)
|
||||
# Create the build-tree version of 'config.mk'
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk" @ONLY)
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk")
|
||||
# Copy 'test.mk' from the source-tree to the build-tree
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/test.mk"
|
||||
@@ -1103,7 +1031,7 @@ function(mfem_export_mk_files)
|
||||
# Create the install-tree version of 'config.mk'
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk" @ONLY)
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk")
|
||||
|
||||
# Install rules for 'config.mk' and 'test.mk'
|
||||
install(FILES ${PROJECT_SOURCE_DIR}/config/test.mk
|
||||
|
||||
@@ -157,10 +157,4 @@ constexpr real_t operator""_r(unsigned long long v)
|
||||
#endif
|
||||
#endif // MFEM_USE_MPI not defined
|
||||
|
||||
#ifndef MFEM_USE_CUDA
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
#error Building with cuDSS (MFEM_USE_CUDSS=YES) requires CUDA (MFEM_USE_CUDA=YES)
|
||||
#endif
|
||||
#endif // MFEM_USE_CUDSS not defined
|
||||
|
||||
#endif // MFEM_CONFIG_HPP
|
||||
|
||||
@@ -108,15 +108,6 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
// #define MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
// #define MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
// #define MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
// #define MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable MFEM features based on the Ginkgo library.
|
||||
// #define MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -36,9 +36,6 @@ MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
|
||||
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
|
||||
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
|
||||
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
|
||||
MFEM_USE_CUDSS = @MFEM_USE_CUDSS@
|
||||
MFEM_CUDSS_COMM_LIB = @MFEM_CUDSS_COMM_LIB@
|
||||
MFEM_CUDSS_THREADING_LIB = @MFEM_CUDSS_THREADING_LIB@
|
||||
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
|
||||
|
||||
@@ -38,7 +38,6 @@ option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
|
||||
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
|
||||
option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
|
||||
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
|
||||
option(MFEM_USE_CUDSS "Enable cuDSS 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)
|
||||
@@ -92,10 +91,9 @@ option(MFEM_ENABLE_BENCHMARKS "Build all of the benchmarks" OFF)
|
||||
|
||||
# Allow a user to specify fetching of certain third-party libraries instead of
|
||||
# searching for existing installations.
|
||||
option(MFEM_FETCH_TPLS "Enable fetching of all supported third-party libraries" OFF)
|
||||
option(MFEM_FETCH_GSLIB "Enable fetching of GSLIB" OFF)
|
||||
option(MFEM_FETCH_HYPRE "Enable fetching of hypre" OFF)
|
||||
option(MFEM_FETCH_METIS "Enable fetching of METIS" OFF)
|
||||
option(FETCH_TPLS "Enable fetching of all supported third-party libraries" OFF)
|
||||
option(HYPRE_FETCH "Enable fetching of hypre" OFF)
|
||||
option(METIS_FETCH "Enable fetching of METIS" OFF)
|
||||
|
||||
# Setting CXX/MPICXX on the command line or in user.cmake will overwrite the
|
||||
# autodetected C++ compiler.
|
||||
|
||||
+2
-17
@@ -18,7 +18,6 @@
|
||||
# Some choices below are based on the OS type:
|
||||
NOTMAC := $(subst Darwin,,$(shell uname -s))
|
||||
|
||||
ASTYLE_BIN = astyle
|
||||
ETAGS_BIN = $(shell command -v etags 2> /dev/null)
|
||||
EGREP_BIN = $(shell command -v egrep 2> /dev/null)
|
||||
|
||||
@@ -153,7 +152,6 @@ MFEM_USE_SUPERLU = NO
|
||||
MFEM_USE_SUPERLU5 = NO
|
||||
MFEM_USE_MUMPS = NO
|
||||
MFEM_USE_STRUMPACK = NO
|
||||
MFEM_USE_CUDSS = NO
|
||||
MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_MAGMA = NO
|
||||
@@ -369,19 +367,6 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
|
||||
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
|
||||
$(SCOTCH_LIB) $(SCALAPACK_LIB)
|
||||
|
||||
# CUDSS library configuration
|
||||
CUDSS_DIR = @MFEM_DIR@/../cudss
|
||||
CUDSS_INCLUDE_DIR = $(CUDSS_DIR)/include
|
||||
CUDSS_LIBRARY_DIR = $(CUDSS_DIR)/lib
|
||||
CUDSS_OPT = -I$(CUDSS_INCLUDE_DIR)
|
||||
CUDSS_LIB = \
|
||||
$(XLINKER)-rpath,$(CUDSS_LIBRARY_DIR) -L$(CUDSS_LIBRARY_DIR) -lcudss
|
||||
# The cuDSS communication and threading libraries.
|
||||
MFEM_CUDSS_COMM_LIB = $(abspath $(wildcard $(or $(CUDSS_COMM_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR), $(CUDSS_LIBRARY_DIR)/libcudss_commlayer_openmpi.so))))
|
||||
MFEM_CUDSS_THREADING_LIB = $(abspath $(wildcard $(or $(CUDSS_THREADING_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR),$(CUDSS_LIBRARY_DIR)/libcudss_mtlayer_gomp.so))))
|
||||
|
||||
# Ginkgo library configuration
|
||||
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
|
||||
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
|
||||
@@ -422,7 +407,7 @@ 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 $(LAPACK_LIB)
|
||||
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
|
||||
|
||||
# GnuTLS library configuration
|
||||
GNUTLS_OPT =
|
||||
@@ -635,7 +620,7 @@ PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
|
||||
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 -ltribol_shared -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
@@ -101,7 +101,7 @@ $ cd ../miniapps
|
||||
$ ls
|
||||
CMakeLists.txt common meshing nurbs shifted toys
|
||||
adjoint electromagnetics mtop parelag solvers
|
||||
autodiff gslib fluids performance tools
|
||||
autodiff gslib navier performance tools
|
||||
```
|
||||
|
||||
And an example in "toys"
|
||||
|
||||
+2
-14
@@ -85,10 +85,6 @@ groups_serial=(
|
||||
"DPG miniapps:"
|
||||
"miniapps/dpg"
|
||||
"{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
|
||||
'"isf"
|
||||
"Schrodinger flow miniapps:"
|
||||
"miniapps/fluids/schrodinger-flow"
|
||||
"schrodinger_flow.cpp"'
|
||||
'"gslib"
|
||||
"GSLIB miniapps:"
|
||||
"miniapps/gslib"
|
||||
@@ -170,10 +166,6 @@ groups_parallel=(
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
|
||||
'"isf"
|
||||
"Schrodinger flow miniapps:"
|
||||
"miniapps/fluids/schrodinger-flow"
|
||||
"pschrodinger_flow.cpp"'
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
@@ -199,7 +191,7 @@ groups_parallel=(
|
||||
# todo: miniapps/multidomain
|
||||
'"navier"
|
||||
"Navier miniapps:"
|
||||
"miniapps/fluids/navier"
|
||||
"miniapps/navier"
|
||||
"navier_cht.cpp"'
|
||||
# todo: add other navier miniapps
|
||||
'"nurbs"
|
||||
@@ -289,10 +281,6 @@ groups_all=(
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
|
||||
'"isf"
|
||||
"Schrodinger flow miniapps:"
|
||||
"miniapps/fluids/schrodinger-flow"
|
||||
"{,p}schrodinger_flow.cpp"'
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
@@ -320,7 +308,7 @@ groups_all=(
|
||||
# todo: miniapps/multidomain
|
||||
'"navier"
|
||||
"Navier miniapps:"
|
||||
"miniapps/fluids/navier"
|
||||
"miniapps/navier"
|
||||
"navier_cht.cpp"'
|
||||
# todo: add other navier miniapps
|
||||
'"nurbs"
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
25
|
||||
3 3 0 1 2 3
|
||||
3 3 1 4 5 2
|
||||
3 3 4 6 7 5
|
||||
3 3 6 8 9 7
|
||||
3 3 8 10 11 9
|
||||
3 3 10 12 13 11
|
||||
3 3 12 14 15 13
|
||||
3 3 14 16 17 15
|
||||
3 3 16 18 19 17
|
||||
3 3 18 20 21 19
|
||||
3 3 20 22 23 21
|
||||
3 3 22 24 25 23
|
||||
3 3 24 26 27 25
|
||||
3 3 26 28 29 27
|
||||
3 3 28 30 31 29
|
||||
3 3 30 32 33 31
|
||||
3 3 32 34 35 33
|
||||
3 3 17 19 36 37
|
||||
3 3 37 36 38 39
|
||||
3 3 39 38 40 41
|
||||
3 3 41 40 42 43
|
||||
3 3 43 42 44 45
|
||||
3 3 45 44 46 47
|
||||
3 3 47 46 48 49
|
||||
3 3 49 48 50 51
|
||||
|
||||
boundary
|
||||
52
|
||||
2 1 0 1
|
||||
2 1 2 3
|
||||
1 1 3 0
|
||||
2 1 1 4
|
||||
2 1 5 2
|
||||
2 1 4 6
|
||||
2 1 7 5
|
||||
2 1 6 8
|
||||
2 1 9 7
|
||||
2 1 8 10
|
||||
2 1 11 9
|
||||
2 1 10 12
|
||||
2 1 13 11
|
||||
2 1 12 14
|
||||
2 1 15 13
|
||||
2 1 14 16
|
||||
2 1 17 15
|
||||
2 1 16 18
|
||||
2 1 18 20
|
||||
2 1 21 19
|
||||
2 1 20 22
|
||||
2 1 23 21
|
||||
2 1 22 24
|
||||
2 1 25 23
|
||||
2 1 24 26
|
||||
2 1 27 25
|
||||
2 1 26 28
|
||||
2 1 29 27
|
||||
2 1 28 30
|
||||
2 1 31 29
|
||||
2 1 30 32
|
||||
2 1 33 31
|
||||
2 1 32 34
|
||||
3 1 34 35
|
||||
2 1 35 33
|
||||
2 1 19 36
|
||||
2 1 37 17
|
||||
2 1 36 38
|
||||
2 1 39 37
|
||||
2 1 38 40
|
||||
2 1 41 39
|
||||
2 1 40 42
|
||||
2 1 43 41
|
||||
2 1 42 44
|
||||
2 1 45 43
|
||||
2 1 44 46
|
||||
2 1 47 45
|
||||
2 1 46 48
|
||||
2 1 49 47
|
||||
2 1 48 50
|
||||
4 1 50 51
|
||||
2 1 51 49
|
||||
|
||||
vertices
|
||||
52
|
||||
2
|
||||
0 0
|
||||
1 0
|
||||
1 1
|
||||
0 1
|
||||
2 0
|
||||
2 1
|
||||
3 0
|
||||
3 1
|
||||
4 0
|
||||
4 1
|
||||
5 0
|
||||
5 1
|
||||
6 0
|
||||
6 1
|
||||
7 0
|
||||
7 1
|
||||
8 0
|
||||
8 1
|
||||
9 0
|
||||
9 1
|
||||
10 0
|
||||
10 1
|
||||
11 0
|
||||
11 1
|
||||
12 0
|
||||
12 1
|
||||
13 0
|
||||
13 1
|
||||
14 0
|
||||
14 1
|
||||
15 0
|
||||
15 1
|
||||
16 0
|
||||
16 1
|
||||
17 0
|
||||
17 1
|
||||
9 2
|
||||
8 2
|
||||
9 3
|
||||
8 3
|
||||
9 4
|
||||
8 4
|
||||
9 5
|
||||
8 5
|
||||
9 6
|
||||
8 6
|
||||
9 7
|
||||
8 7
|
||||
9 8
|
||||
8 8
|
||||
9 9
|
||||
8 9
|
||||
@@ -1,86 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Four segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
4
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
4 1 6 7
|
||||
|
||||
boundary
|
||||
0
|
||||
|
||||
edges
|
||||
4
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
3 6 7
|
||||
|
||||
vertices
|
||||
8
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 3 spans)
|
||||
knotvectors
|
||||
1
|
||||
1 4 0 0 .4 .6 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
0.6 0.4 1.0
|
||||
0.4 0.6 1.0
|
||||
1.0 1.0 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 2 spans)
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 .5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.9 0.0 1.21
|
||||
2.0 0.9 1.22
|
||||
2.0 1.0 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 3 spans)
|
||||
knotvectors
|
||||
1
|
||||
3 6 0 0 0 0 .33 .66 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
2.1 0.2 1.31
|
||||
3.5 0.4 1.32
|
||||
2.5 0.6 1.33
|
||||
2.9 1.0 1.34
|
||||
3.0 1.0 1.0
|
||||
|
||||
# Patch 3: quartic (order 4, 1 span)
|
||||
knotvectors
|
||||
1
|
||||
4 5 0 0 0 0 0 1 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
3.0 0.0 1.0
|
||||
3.45 0.5 1.41
|
||||
3.50 1.0 1.42
|
||||
3.75 0.8 1.43
|
||||
4.0 0.0 1.0
|
||||
@@ -1,79 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Three segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 2 control points)
|
||||
knotvectors
|
||||
1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
1.0 1.0 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 3 control points)
|
||||
knotvectors
|
||||
1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.02 1.02 1.2
|
||||
2.0 1.0 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
2.03 0.83 1.31
|
||||
2.33 1.03 1.32
|
||||
3.0 1.0 1.0
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
# Edge 0: linear (order 1, 2 control points)
|
||||
# Edge 1: quadratic (order 2, 3 control points)
|
||||
# Edge 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
3
|
||||
1 2 0 0 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1.2
|
||||
1.31
|
||||
1.32
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS
|
||||
VDim: 2
|
||||
Ordering: 1
|
||||
|
||||
0.0 0.0
|
||||
1.0 1.0
|
||||
1.0 0.0
|
||||
2.0 1.0
|
||||
2.0 0.0
|
||||
3.0 1.0
|
||||
1.02 1.02
|
||||
2.03 0.83
|
||||
2.33 1.03
|
||||
@@ -1,79 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Three segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 2 control points)
|
||||
knotvectors
|
||||
1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 0.01 1.0
|
||||
1.0 1.0 1.01 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 3 control points)
|
||||
knotvectors
|
||||
1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 0.02 1.0
|
||||
1.02 1.02 0.52 1.2
|
||||
2.0 1.0 1.02 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 0.03 1.0
|
||||
2.03 0.83 0.33 1.31
|
||||
2.33 1.03 0.63 1.32
|
||||
3.0 1.0 1.03 1.0
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
# Edge 0: linear (order 1, 2 control points)
|
||||
# Edge 1: quadratic (order 2, 3 control points)
|
||||
# Edge 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
3
|
||||
1 2 0 0 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1.2
|
||||
1.31
|
||||
1.32
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS
|
||||
VDim: 3
|
||||
Ordering: 1
|
||||
|
||||
0.0 0.0 0.01
|
||||
1.0 1.0 1.01
|
||||
1.0 0.0 0.02
|
||||
2.0 1.0 1.02
|
||||
2.0 0.0 0.03
|
||||
3.0 1.0 1.03
|
||||
1.02 1.02 0.52
|
||||
2.03 0.83 0.33
|
||||
2.33 1.03 0.63
|
||||
@@ -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.9.1
|
||||
PROJECT_NUMBER = v4.8.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
|
||||
@@ -973,13 +973,10 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/adjoint \
|
||||
@MFEM_SOURCE_DIR@/miniapps/autodiff \
|
||||
@MFEM_SOURCE_DIR@/miniapps/common \
|
||||
@MFEM_SOURCE_DIR@/miniapps/contact \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dfem \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
|
||||
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
|
||||
@MFEM_SOURCE_DIR@/miniapps/fluids/navier \
|
||||
@MFEM_SOURCE_DIR@/miniapps/fluids/schrodinger-flow \
|
||||
@MFEM_SOURCE_DIR@/miniapps/gslib \
|
||||
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
|
||||
@MFEM_SOURCE_DIR@/miniapps/hooke \
|
||||
@@ -990,6 +987,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/meshing \
|
||||
@MFEM_SOURCE_DIR@/miniapps/mtop \
|
||||
@MFEM_SOURCE_DIR@/miniapps/multidomain \
|
||||
@MFEM_SOURCE_DIR@/miniapps/navier \
|
||||
@MFEM_SOURCE_DIR@/miniapps/nurbs \
|
||||
@MFEM_SOURCE_DIR@/miniapps/parelag \
|
||||
@MFEM_SOURCE_DIR@/miniapps/performance \
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace mfem {
|
||||
* - <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
|
||||
* - <a class="el" href="ex41_8cpp_source.html">Example 41</a>: DG/CG IMEX time-dependent advection-diffusion
|
||||
* - <a class="el" href="ex41p_8cpp_source.html">Example 41p</a>: parallel DG/CG IMEX time-dependent advection-diffusion
|
||||
*
|
||||
* <H4>AmgX Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -190,8 +188,6 @@ namespace mfem {
|
||||
* <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__ex10_8cpp_source.html">10</a>,
|
||||
* <a class="el" href="nurbs__ex10p_8cpp_source.html">10p</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.
|
||||
@@ -200,7 +196,6 @@ namespace mfem {
|
||||
* - <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
|
||||
* - <a class="el" href="nurbs__mesh_info_8cpp_source.html">NURBS Mesh info</a>: print the info of a NURBS mesh
|
||||
*
|
||||
* <H3>Miniapps</H3>
|
||||
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
|
||||
@@ -239,8 +234,7 @@ namespace mfem {
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson 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">Tribol</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="contact_8cpp_source.html">Contact</a>: Frictionless contact examples using <a class="el" href="classmfem_1_1IPSolver.html#details">IP optimization</a> and the <a class="el" href="classmfem_1_1AMGFSolver.html#details">AMGF solver</a>
|
||||
* - <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
|
||||
|
||||
@@ -46,7 +46,6 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex38.cpp
|
||||
ex39.cpp
|
||||
ex40.cpp
|
||||
ex41.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -90,7 +89,6 @@ if (MFEM_USE_MPI)
|
||||
ex37p.cpp
|
||||
ex39p.cpp
|
||||
ex40p.cpp
|
||||
ex41p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -133,8 +131,6 @@ if (MFEM_ENABLE_TESTING)
|
||||
list(APPEND THIS_TEST_OPTIONS "-dg")
|
||||
elseif(${TEST_NAME} MATCHES "ex37p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-mi" "3")
|
||||
elseif(${TEST_NAME} MATCHES "ex41p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-tf" "1.0")
|
||||
endif()
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
@@ -215,7 +211,7 @@ if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME ex1p_ceed_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex1p> "-no-vis" "-d" "ceed-cpu" "-pa" "-a"
|
||||
$<TARGET_FILE:ex1p> "-no-vis" "-d ceed-cpu" "-pa" "-a"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -64,7 +64,7 @@ PARALLEL_NAME := Parallel AMGX example
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not build)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
clean: clean-build
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
|
||||
@@ -64,12 +64,12 @@ ex1p-test-par: ex1p
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
clean: clean-build clean-exec $(SUBDIRS_CLEAN)
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh mesh.*
|
||||
@rm -f sol.*
|
||||
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
|
||||
|
||||
+21
-34
@@ -50,10 +50,6 @@
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
//
|
||||
// Device simplices sample runs:
|
||||
// ex1 -pa -d gpu -m ../data/inline-tet.mesh
|
||||
// ex1 -pa -d gpu -m ../data/inline-tri.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
@@ -142,25 +138,25 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange finite elements of the specified order.
|
||||
// - If order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// - If the mesh is simplicial and partial assembly is requested,
|
||||
// we use the positive basis, which supports device execution.
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
auto basis_type = (pa && mesh.IsSimplexMesh()) ?
|
||||
BasisType::Positive : BasisType::GaussLobatto;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim, basis_type);
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (mesh.GetNodes())
|
||||
{
|
||||
fec = mesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim, basis_type);
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
FiniteElementSpace fespace(&mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
@@ -228,29 +224,17 @@ int main(int argc, char *argv[])
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
// Use cuDSS to solve the system.
|
||||
CuDSSSolver cudss_solver;
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -289,14 +273,17 @@ int main(int argc, char *argv[])
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
if (order > 0) { delete fec; }
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,9 +5,9 @@
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 464 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 82
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3877 -o 2 -sys
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
|
||||
|
||||
+6
-29
@@ -105,7 +105,6 @@ int main(int argc, char *argv[])
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -127,9 +126,6 @@ int main(int argc, char *argv[])
|
||||
"Alpha coefficient.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"Kappa coefficient offset.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -183,11 +179,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 7. Initialize the conduction operator and the visualization.
|
||||
ConductionOperator oper(fespace, alpha, kappa, u);
|
||||
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
oper.SetImplicitVariableType(imp_var);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
{
|
||||
@@ -325,14 +316,11 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
}
|
||||
|
||||
void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &u, Vector &k)
|
||||
const Vector &u, Vector &du_dt)
|
||||
{
|
||||
// Solve the equation:
|
||||
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
|
||||
// or
|
||||
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
|
||||
// where K is linearized by using u from the previous timestep, and
|
||||
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
@@ -340,20 +328,9 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
|
||||
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u_s
|
||||
Mmat.Mult(u, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
}
|
||||
T_solver.Mult(z, k);
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
T_solver.Mult(z, du_dt);
|
||||
}
|
||||
|
||||
void ConductionOperator::SetParameters(const Vector &u)
|
||||
|
||||
+6
-29
@@ -115,7 +115,6 @@ int main(int argc, char *argv[])
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
bool adios2 = false;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -139,9 +138,6 @@ int main(int argc, char *argv[])
|
||||
"Alpha coefficient.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"Kappa coefficient offset.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -216,11 +212,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 9. Initialize the conduction operator and the VisIt visualization.
|
||||
ConductionOperator oper(fespace, alpha, kappa, u);
|
||||
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
oper.SetImplicitVariableType(imp_var);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
{
|
||||
@@ -416,14 +407,11 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
}
|
||||
|
||||
void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &u, Vector &k)
|
||||
const Vector &u, Vector &du_dt)
|
||||
{
|
||||
// Solve the equation:
|
||||
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
|
||||
// or
|
||||
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
|
||||
// where K is linearized by using u from the previous timestep, and
|
||||
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
@@ -431,20 +419,9 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
|
||||
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
Mmat.Mult(u, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
}
|
||||
T_solver.Mult(z, k);
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
T_solver.Mult(z, du_dt);
|
||||
}
|
||||
|
||||
void ConductionOperator::SetParameters(const Vector &u)
|
||||
|
||||
+34
-60
@@ -42,11 +42,7 @@
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/beam-tet.mesh
|
||||
//
|
||||
// Device simplices sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tet.mesh
|
||||
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tri.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Poisson problem
|
||||
@@ -87,9 +83,6 @@ int main(int argc, char *argv[])
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool algebraic_ceed = false;
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
bool cudss_solver = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -109,10 +102,6 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&algebraic_ceed, "-a", "--algebraic",
|
||||
"-no-a", "--no-algebraic",
|
||||
"Use algebraic Ceed solver");
|
||||
#endif
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
args.AddOption(&cudss_solver, "-cudss", "--cudss-solver", "-no-cudss",
|
||||
"--no-cudss-solver", "Use the cuDSS Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -169,20 +158,19 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order.
|
||||
// - If order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// - If the mesh is simplicial and partial assembly is requested,
|
||||
// we use the positive basis, which supports device execution.
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
auto basis_type = (pa && pmesh.IsSimplexMesh()) ?
|
||||
BasisType::Positive : BasisType::GaussLobatto;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim, basis_type);
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
@@ -190,7 +178,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim, basis_type);
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
@@ -259,51 +248,33 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (!pa && (Device::Allows(Backend::CUDA_MASK) && cudss_solver))
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
// Solve using a direct solver with cuDSS
|
||||
CuDSSSolver cudss_solver(MPI_COMM_WORLD);
|
||||
cudss_solver.SetMatrixSymType(
|
||||
CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE);
|
||||
cudss_solver.SetMatrixViewType(CuDSSSolver::UPPER);
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec)
|
||||
{
|
||||
cg.SetPreconditioner(*prec);
|
||||
}
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
@@ -337,7 +308,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (order > 0) { delete fec; }
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
LinearForm b(&fespace);
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
|
||||
// 6. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
ParLinearForm b(&fespace);
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
|
||||
// 6. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
|
||||
@@ -471,13 +471,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
ofstream sol_r_ofs("sol_r.gf");
|
||||
ofstream sol_i_ofs("sol_i.gf");
|
||||
ofstream sol_z_ofs("sol_z.gf");
|
||||
sol_r_ofs.precision(8);
|
||||
sol_i_ofs.precision(8);
|
||||
sol_z_ofs.precision(8);
|
||||
u.real().Save(sol_r_ofs);
|
||||
u.imag().Save(sol_i_ofs);
|
||||
u.Save(sol_z_ofs);
|
||||
}
|
||||
|
||||
// 14. Send the solution by socket to a GLVis server.
|
||||
|
||||
+10
-32
@@ -302,21 +302,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
|
||||
<< "window_title 'Exact: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
|
||||
<< "window_title 'Exact: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
|
||||
// 11. Set up the parallel sesquilinear form a(.,.) on the finite element
|
||||
@@ -513,11 +507,10 @@ int main(int argc, char *argv[])
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_r_name, sol_i_name, sol_z_name;
|
||||
ostringstream mesh_name, sol_r_name, sol_i_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_r_name << "sol_r." << setfill('0') << setw(6) << myid;
|
||||
sol_i_name << "sol_i." << setfill('0') << setw(6) << myid;
|
||||
sol_z_name << "sol_z." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
@@ -525,13 +518,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
ofstream sol_r_ofs(sol_r_name.str().c_str());
|
||||
ofstream sol_i_ofs(sol_i_name.str().c_str());
|
||||
ofstream sol_z_ofs(sol_z_name.str().c_str());
|
||||
sol_r_ofs.precision(8);
|
||||
sol_i_ofs.precision(8);
|
||||
sol_z_ofs.precision(8);
|
||||
u.real().Save(sol_r_ofs);
|
||||
u.imag().Save(sol_i_ofs);
|
||||
u.Save(sol_z_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
@@ -540,21 +530,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u.real()
|
||||
<< "window_title 'Solution: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u.imag()
|
||||
<< "window_title 'Solution: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
if (visualization && exact_sol)
|
||||
{
|
||||
@@ -563,21 +547,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
|
||||
<< "window_title 'Error: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
|
||||
<< "window_title 'Error: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
|
||||
@@ -95,15 +95,6 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (amg_elast && !static_cond && reorder_space)
|
||||
{
|
||||
if (myid == 0)
|
||||
cerr << "\nThe AMG elasticity solver requires ordering byVDIM! "
|
||||
<< "Ignoring the specified option -nodes/--by-nodes.\n"
|
||||
<< endl;
|
||||
reorder_space = false;
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
|
||||
+52
-11
@@ -5,8 +5,8 @@
|
||||
// Sample runs:
|
||||
// ex37 -alpha 10
|
||||
// ex37 -alpha 10 -pv
|
||||
// ex37 -lambda 0.1 -mu 0.1 -growth 1
|
||||
// ex37 -o 2 -alpha 10.0 -mi 50 -vf 0.4 -ntol 1e-5 -growth 1.5
|
||||
// ex37 -lambda 0.1 -mu 0.1
|
||||
// ex37 -o 2 -alpha 5.0 -mi 50 -vf 0.4 -ntol 1e-5
|
||||
// ex37 -r 6 -o 1 -alpha 25.0 -epsilon 0.02 -mi 50 -ntol 1e-5
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve a
|
||||
@@ -55,6 +55,53 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
/**
|
||||
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
|
||||
* ∫_Ω ρ dx = θ vol(Ω) as follows:
|
||||
*
|
||||
* 1. Compute the root of the R → R function
|
||||
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
|
||||
* 2. Set ψ ← ψ + c.
|
||||
*
|
||||
* @param psi a GridFunction to be updated
|
||||
* @param target_volume θ vol(Ω)
|
||||
* @param tol Newton iteration tolerance
|
||||
* @param max_its Newton maximum iteration number
|
||||
* @return real_t Final volume, ∫_Ω sigmoid(ψ)
|
||||
*/
|
||||
real_t proj(GridFunction &psi, real_t target_volume, real_t tol=1e-12,
|
||||
int max_its=10)
|
||||
{
|
||||
MappedGridFunctionCoefficient sigmoid_psi(&psi, sigmoid);
|
||||
MappedGridFunctionCoefficient der_sigmoid_psi(&psi, der_sigmoid);
|
||||
|
||||
LinearForm int_sigmoid_psi(psi.FESpace());
|
||||
int_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
|
||||
LinearForm int_der_sigmoid_psi(psi.FESpace());
|
||||
int_der_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(
|
||||
der_sigmoid_psi));
|
||||
bool done = false;
|
||||
for (int k=0; k<max_its; k++) // Newton iteration
|
||||
{
|
||||
int_sigmoid_psi.Assemble(); // Recompute f(c) with updated ψ
|
||||
const real_t f = int_sigmoid_psi.Sum() - target_volume;
|
||||
|
||||
int_der_sigmoid_psi.Assemble(); // Recompute df(c) with updated ψ
|
||||
const real_t df = int_der_sigmoid_psi.Sum();
|
||||
|
||||
const real_t dc = -f/df;
|
||||
psi += dc;
|
||||
if (abs(dc) < tol) { done = true; break; }
|
||||
}
|
||||
if (!done)
|
||||
{
|
||||
mfem_warning("Projection reached maximum iteration without converging. "
|
||||
"Result may not be accurate.");
|
||||
}
|
||||
int_sigmoid_psi.Assemble();
|
||||
return int_sigmoid_psi.Sum();
|
||||
}
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ALGORITHM PREAMBLE
|
||||
@@ -133,11 +180,10 @@ int main(int argc, char *argv[])
|
||||
int ref_levels = 5;
|
||||
int order = 2;
|
||||
real_t alpha = 1.0;
|
||||
real_t growth = 2;
|
||||
real_t epsilon = 0.01;
|
||||
real_t vol_fraction = 0.5;
|
||||
int max_it = 1e3;
|
||||
real_t itol = 1e-2;
|
||||
real_t itol = 1e-1;
|
||||
real_t ntol = 1e-4;
|
||||
real_t rho_min = 1e-6;
|
||||
real_t lambda = 1.0;
|
||||
@@ -152,8 +198,6 @@ int main(int argc, char *argv[])
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&alpha, "-alpha", "--alpha-step-length",
|
||||
"Step length for gradient descent.");
|
||||
args.AddOption(&growth, "-growth", "--alpha-growth-rate",
|
||||
"Growth rate of step length for gradient descent.");
|
||||
args.AddOption(&epsilon, "-epsilon", "--epsilon-thickness",
|
||||
"Length scale for ρ.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
@@ -288,7 +332,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
FilterSolver->SetEssentialBoundary(ess_bdr_filter);
|
||||
FilterSolver->SetupFEM();
|
||||
FilterSolver->AssembleDiffusionBilinear();
|
||||
|
||||
BilinearForm mass(&control_fes);
|
||||
mass.AddDomainIntegrator(new InverseIntegrator(new MassIntegrator(one)));
|
||||
@@ -342,7 +385,7 @@ int main(int argc, char *argv[])
|
||||
// 11. Iterate:
|
||||
for (int k = 1; k <= max_it; k++)
|
||||
{
|
||||
if (k > 1) { alpha = std::pow((real_t) k,growth); }
|
||||
if (k > 1) { alpha *= ((real_t) k) / ((real_t) k-1); }
|
||||
|
||||
mfem::out << "\nStep = " << k << std::endl;
|
||||
|
||||
@@ -379,9 +422,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Step 5 - Update design variable ψ ← proj(ψ - αG)
|
||||
psi.Add(-alpha, grad);
|
||||
GridFunction alpha_grad(grad);
|
||||
alpha_grad *= alpha;
|
||||
const real_t material_volume = proj(psi, alpha_grad, target_volume);
|
||||
const real_t material_volume = proj(psi, target_volume);
|
||||
|
||||
// Compute ||ρ - ρ_old|| in control fes.
|
||||
real_t norm_increment = zerogf.ComputeL1Error(succ_diff_rho);
|
||||
|
||||
+23
-183
@@ -137,7 +137,7 @@ public:
|
||||
exponent(exponent_), rho_min(rho_min_)
|
||||
{
|
||||
MFEM_ASSERT(rho_min_ >= 0.0, "rho_min must be >= 0");
|
||||
MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be < 1");
|
||||
MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be > 1");
|
||||
MFEM_ASSERT(u, "displacement field is not set");
|
||||
MFEM_ASSERT(rho_filter, "density field is not set");
|
||||
}
|
||||
@@ -231,12 +231,9 @@ private:
|
||||
FiniteElementCollection * fec = nullptr;
|
||||
FiniteElementSpace * fes = nullptr;
|
||||
Array<int> ess_bdr;
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> neumann_bdr;
|
||||
GridFunction * u = nullptr;
|
||||
LinearForm * b = nullptr;
|
||||
BilinearForm * a = nullptr;
|
||||
OperatorPtr A;
|
||||
bool parallel;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh * pmesh = nullptr;
|
||||
@@ -270,8 +267,6 @@ public:
|
||||
void ResetFEM();
|
||||
void SetupFEM();
|
||||
|
||||
void UpdateEssentialTDofs();
|
||||
void AssembleDiffusionBilinear(bool update_ess_tdofs=true);
|
||||
void Solve();
|
||||
GridFunction * GetFEMSolution();
|
||||
LinearForm * GetLinearForm() {return b;}
|
||||
@@ -376,130 +371,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
|
||||
* ∫_Ω ρ dx = θ vol(Ω) as follows:
|
||||
*
|
||||
* 1. Compute the root of the R → R function
|
||||
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
|
||||
* using the Illinois method
|
||||
* 2. Set ψ ← ψ + c.
|
||||
*
|
||||
* @param psi a GridFunction to be updated
|
||||
* @param alpha_grad alpha multiplied by gradient
|
||||
* @param target_volume θ vol(Ω)
|
||||
* @param tol Illinois iteration tolerance
|
||||
* @param max_its Illinois maximum iteration number
|
||||
* @return real_t Final volume (∫_Ω sigmoid(ψ) dx)
|
||||
*/
|
||||
real_t proj(GridFunction &psi, GridFunction &alpha_grad, real_t target_volume,
|
||||
real_t tol = 1e-12, int max_its = 100)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
FiniteElementSpace *fes = psi.FESpace();
|
||||
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace*>(fes);
|
||||
#endif
|
||||
ConstantCoefficient zero_cf(0.0);
|
||||
real_t a = -alpha_grad.ComputeMaxError(zero_cf);
|
||||
real_t b = -a;
|
||||
real_t y = 0.0;
|
||||
|
||||
MappedGridFunctionCoefficient sigmoid_psi(
|
||||
&psi, [&y](const real_t x) { return sigmoid(x + y); });
|
||||
std::unique_ptr<LinearForm> int_sigmoid_psi;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParGridFunction *par_psi = dynamic_cast<ParGridFunction *>(&psi);
|
||||
if (par_psi)
|
||||
{
|
||||
int_sigmoid_psi.reset(new ParLinearForm(par_psi->ParFESpace()));
|
||||
}
|
||||
else
|
||||
{
|
||||
int_sigmoid_psi.reset(new LinearForm(psi.FESpace()));
|
||||
}
|
||||
#else
|
||||
int_sigmoid_psi.reset(new LinearForm(psi.FESpace()));
|
||||
#endif
|
||||
int_sigmoid_psi->AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
|
||||
|
||||
y = a;
|
||||
int_sigmoid_psi->Assemble();
|
||||
real_t f_a = int_sigmoid_psi->Sum(); // f_a := f(a) + θ vol(Ω)
|
||||
|
||||
y = b;
|
||||
int_sigmoid_psi->Assemble();
|
||||
real_t f_b = int_sigmoid_psi->Sum(); // f_b := f(b) + θ vol(Ω)
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (pfes)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f_a, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f_b, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
f_a -= target_volume; // f_a := f(a)
|
||||
f_b -= target_volume; // f_b := f(b)
|
||||
real_t c = 0.0;
|
||||
real_t f_c = 0.0;
|
||||
int side = 0;
|
||||
|
||||
bool done = false;
|
||||
for (int k=0; k < max_its; k++)
|
||||
{
|
||||
c = (f_a * b - f_b * a) / (f_a - f_b);
|
||||
|
||||
if (abs(b - a) < tol * abs(b + a)) { done = true; break; }
|
||||
|
||||
y = c;
|
||||
int_sigmoid_psi->Assemble();
|
||||
f_c = int_sigmoid_psi->Sum(); // f_c := f(c) + θ vol(Ω)
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (pfes)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f_c, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
f_c -= target_volume; // f_c := f(c)
|
||||
|
||||
if (f_c * f_b > 0)
|
||||
{
|
||||
b = c;
|
||||
f_b = f_c;
|
||||
if (side == -1) { f_a /= 2.0; }
|
||||
side = -1;
|
||||
}
|
||||
else if (f_c * f_a > 0)
|
||||
{
|
||||
a = c;
|
||||
f_a = f_c;
|
||||
if (side == 1) { f_b /= 2.0; }
|
||||
side = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
done = true; break;
|
||||
}
|
||||
}
|
||||
if (!done)
|
||||
{
|
||||
mfem_warning("Projection reached maximum iteration without converging. "
|
||||
"Result may not be accurate.");
|
||||
}
|
||||
y = 0.0;
|
||||
psi += c;
|
||||
int_sigmoid_psi->Assemble();
|
||||
real_t material_volume = int_sigmoid_psi->Sum();
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (pfes)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE, &material_volume, 1,
|
||||
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
return material_volume;
|
||||
}
|
||||
|
||||
// Poisson solver
|
||||
|
||||
@@ -551,8 +422,12 @@ void DiffusionSolver::SetupFEM()
|
||||
}
|
||||
}
|
||||
|
||||
void DiffusionSolver::UpdateEssentialTDofs()
|
||||
void DiffusionSolver::Solve()
|
||||
{
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
Array<int> ess_tdof_list;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
@@ -565,39 +440,7 @@ void DiffusionSolver::UpdateEssentialTDofs()
|
||||
#else
|
||||
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DiffusionSolver::AssembleDiffusionBilinear(bool update_ess_tdofs)
|
||||
{
|
||||
if (update_ess_tdofs)
|
||||
{
|
||||
UpdateEssentialTDofs();
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
a = new ParBilinearForm(pfes);
|
||||
}
|
||||
else
|
||||
{
|
||||
a = new BilinearForm(fes);
|
||||
}
|
||||
#else
|
||||
a = new BilinearForm(fes);
|
||||
#endif
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf));
|
||||
if (masscf)
|
||||
{
|
||||
a->AddDomainIntegrator(new MassIntegrator(*masscf));
|
||||
}
|
||||
a->Assemble();
|
||||
a->FormSystemMatrix(ess_tdof_list, A);
|
||||
}
|
||||
|
||||
void DiffusionSolver::Solve()
|
||||
{
|
||||
Vector B, X;
|
||||
|
||||
*u=0.0;
|
||||
if (b)
|
||||
{
|
||||
delete b;
|
||||
@@ -632,33 +475,31 @@ void DiffusionSolver::Solve()
|
||||
|
||||
b->Assemble();
|
||||
|
||||
*u=0.0;
|
||||
if (essbdr_cf)
|
||||
{
|
||||
u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr);
|
||||
}
|
||||
BilinearForm * a = nullptr;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
X.SetSize(pfes->TrueVSize());
|
||||
B.SetSize(pfes->TrueVSize());
|
||||
dynamic_cast<ParGridFunction*>(u)->ParallelAssemble(X);
|
||||
dynamic_cast<ParLinearForm*>(b)->ParallelAssemble(B);
|
||||
dynamic_cast<ParBilinearForm*>(a)->ParallelEliminateTDofsInRHS(
|
||||
ess_tdof_list, X, B);
|
||||
a = new ParBilinearForm(pfes);
|
||||
}
|
||||
else
|
||||
{
|
||||
X.NewDataAndSize(u->GetData(), u->Size());
|
||||
B.NewDataAndSize(b->GetData(), b->Size());
|
||||
a->EliminateVDofsInRHS(ess_tdof_list, X, B);
|
||||
a = new BilinearForm(fes);
|
||||
}
|
||||
#else
|
||||
X.NewDataAndSize(u->GetData(), u->Size());
|
||||
B.NewDataAndSize(b->GetData(), b->Size());
|
||||
a->EliminateVDofsInRHS(ess_tdof_list, X, B);
|
||||
a = new BilinearForm(fes);
|
||||
#endif
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf));
|
||||
if (masscf)
|
||||
{
|
||||
a->AddDomainIntegrator(new MassIntegrator(*masscf));
|
||||
}
|
||||
a->Assemble();
|
||||
if (essbdr_cf)
|
||||
{
|
||||
u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr);
|
||||
}
|
||||
a->FormLinearSystem(ess_tdof_list, *u, *b, A, X, B);
|
||||
|
||||
CGSolver * cg = nullptr;
|
||||
Solver * M = nullptr;
|
||||
@@ -687,6 +528,7 @@ void DiffusionSolver::Solve()
|
||||
delete M;
|
||||
delete cg;
|
||||
a->RecoverFEMSolution(X, *b, *u);
|
||||
delete a;
|
||||
}
|
||||
|
||||
GridFunction * DiffusionSolver::GetFEMSolution()
|
||||
@@ -718,8 +560,6 @@ DiffusionSolver::~DiffusionSolver()
|
||||
#endif
|
||||
delete fec; fec = nullptr;
|
||||
delete b;
|
||||
A.Clear();
|
||||
delete a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+60
-11
@@ -4,8 +4,8 @@
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex37p -alpha 10 -pv
|
||||
// mpirun -np 4 ex37p -lambda 0.1 -mu 0.1 -growth 1
|
||||
// mpirun -np 4 ex37p -o 2 -alpha 10.0 -mi 50 -vf 0.4 -ntol 1e-5 -growth 1.5
|
||||
// mpirun -np 4 ex37p -lambda 0.1 -mu 0.1
|
||||
// mpirun -np 4 ex37p -o 2 -alpha 5.0 -mi 50 -vf 0.4 -ntol 1e-5
|
||||
// mpirun -np 4 ex37p -r 6 -o 2 -alpha 10.0 -epsilon 0.02 -mi 50 -ntol 1e-5
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve a
|
||||
@@ -54,6 +54,61 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
/**
|
||||
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
|
||||
* ∫_Ω ρ dx = θ vol(Ω) as follows:
|
||||
*
|
||||
* 1. Compute the root of the R → R function
|
||||
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
|
||||
* 2. Set ψ ← ψ + c.
|
||||
*
|
||||
* @param psi a GridFunction to be updated
|
||||
* @param target_volume θ vol(Ω)
|
||||
* @param tol Newton iteration tolerance
|
||||
* @param max_its Newton maximum iteration number
|
||||
* @return real_t Final volume, ∫_Ω sigmoid(ψ)
|
||||
*/
|
||||
real_t proj(ParGridFunction &psi, real_t target_volume, real_t tol=1e-12,
|
||||
int max_its=10)
|
||||
{
|
||||
MappedGridFunctionCoefficient sigmoid_psi(&psi, sigmoid);
|
||||
MappedGridFunctionCoefficient der_sigmoid_psi(&psi, der_sigmoid);
|
||||
|
||||
ParLinearForm int_sigmoid_psi(psi.ParFESpace());
|
||||
int_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
|
||||
ParLinearForm int_der_sigmoid_psi(psi.ParFESpace());
|
||||
int_der_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(
|
||||
der_sigmoid_psi));
|
||||
bool done = false;
|
||||
for (int k=0; k<max_its; k++) // Newton iteration
|
||||
{
|
||||
int_sigmoid_psi.Assemble(); // Recompute f(c) with updated ψ
|
||||
real_t f = int_sigmoid_psi.Sum();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
f -= target_volume;
|
||||
|
||||
int_der_sigmoid_psi.Assemble(); // Recompute df(c) with updated ψ
|
||||
real_t df = int_der_sigmoid_psi.Sum();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &df, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
const real_t dc = -f/df;
|
||||
psi += dc;
|
||||
if (abs(dc) < tol) { done = true; break; }
|
||||
}
|
||||
if (!done)
|
||||
{
|
||||
mfem_warning("Projection reached maximum iteration without converging. "
|
||||
"Result may not be accurate.");
|
||||
}
|
||||
int_sigmoid_psi.Assemble();
|
||||
real_t material_volume = int_sigmoid_psi.Sum();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &material_volume, 1,
|
||||
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
|
||||
return material_volume;
|
||||
}
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ALGORITHM PREAMBLE
|
||||
@@ -138,11 +193,10 @@ int main(int argc, char *argv[])
|
||||
int ref_levels = 5;
|
||||
int order = 2;
|
||||
real_t alpha = 1.0;
|
||||
real_t growth = 2;
|
||||
real_t epsilon = 0.01;
|
||||
real_t vol_fraction = 0.5;
|
||||
int max_it = 1e3;
|
||||
real_t itol = 1e-2;
|
||||
real_t itol = 1e-1;
|
||||
real_t ntol = 1e-4;
|
||||
real_t rho_min = 1e-6;
|
||||
real_t lambda = 1.0;
|
||||
@@ -157,8 +211,6 @@ int main(int argc, char *argv[])
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&alpha, "-alpha", "--alpha-step-length",
|
||||
"Step length for gradient descent.");
|
||||
args.AddOption(&growth, "-growth", "--alpha-growth-rate",
|
||||
"Growth rate of step length for gradient descent.");
|
||||
args.AddOption(&epsilon, "-epsilon", "--epsilon-thickness",
|
||||
"Length scale for ρ.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
@@ -307,7 +359,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
FilterSolver->SetEssentialBoundary(ess_bdr_filter);
|
||||
FilterSolver->SetupFEM();
|
||||
FilterSolver->AssembleDiffusionBilinear();
|
||||
|
||||
ParBilinearForm mass(&control_fes);
|
||||
mass.AddDomainIntegrator(new InverseIntegrator(new MassIntegrator(one)));
|
||||
@@ -361,7 +412,7 @@ int main(int argc, char *argv[])
|
||||
// 11. Iterate:
|
||||
for (int k = 1; k <= max_it; k++)
|
||||
{
|
||||
if (k > 1) { alpha = std::pow((real_t) k,growth); }
|
||||
if (k > 1) { alpha *= ((real_t) k) / ((real_t) k-1); }
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
@@ -401,9 +452,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Step 5 - Update design variable ψ ← proj(ψ - αG)
|
||||
psi.Add(-alpha, grad);
|
||||
ParGridFunction alpha_grad(grad);
|
||||
alpha_grad *= alpha;
|
||||
const real_t material_volume = proj(psi, alpha_grad, target_volume);
|
||||
const real_t material_volume = proj(psi, target_volume);
|
||||
|
||||
// Compute ||ρ - ρ_old|| in control fes.
|
||||
real_t norm_increment = zerogf.ComputeL1Error(succ_diff_rho);
|
||||
|
||||
+1
-5
@@ -9,7 +9,6 @@
|
||||
// ex4 -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// ex4 -m ../data/escher.mesh
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-q2.vtk
|
||||
// ex4 -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// ex4 -m ../data/square-disc-nurbs.mesh
|
||||
@@ -19,7 +18,6 @@
|
||||
// ex4 -m ../data/amr-quad.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/ref-prism.mesh -o 1
|
||||
// ex4 -m ../data/octahedron.mesh -o 1
|
||||
@@ -27,8 +25,6 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex4 -m ../data/star.mesh -pa -d cuda
|
||||
// ex4 -m ../data/star.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/amr-quad.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-omp
|
||||
// ex4 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -197,7 +193,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa && (!ea || hybridization))
|
||||
if (!pa)
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
|
||||
@@ -1,589 +0,0 @@
|
||||
// MFEM Example 41
|
||||
//
|
||||
// Compile with: make ex41
|
||||
//
|
||||
// Sample runs:
|
||||
// ex41
|
||||
// ex41 -cg
|
||||
// ex41 -m ../data/periodic-hexagon.mesh -p 0 -r 2 -dt 0.005 -tf 10
|
||||
// ex41 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9
|
||||
// ex41 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.001 -tf 9
|
||||
// ex41 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/disc-nurbs.mesh -p 2 -r 3 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/periodic-square.mesh -p 3 -r 4 -dt 0.0025 -tf 9 -vs 20
|
||||
// ex41 -m ../data/periodic-cube.mesh -p 0 -r 2 -o 2 -dt 0.01 -tf 8
|
||||
//
|
||||
// Device sample runs:
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection-diffusion
|
||||
// equation du/dt + v.grad(u) - a div(grad(u)) = 0, where v is a
|
||||
// given fluid velocity, a is the diffusion coefficient, and
|
||||
// u0(x)=u(0,x) is a given initial condition.
|
||||
//
|
||||
// The example demonstrates the use of Discontinuous Galerkin (DG)
|
||||
// bilinear forms in MFEM (face integrators), and the use of IMEX
|
||||
// ODE time integrators.
|
||||
//
|
||||
// The option to use continuous finite elements is available too.
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
// Velocity coefficient
|
||||
template<int problem=0>
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
real_t d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial condition
|
||||
template<int problem=0>
|
||||
real_t u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
real_t rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const real_t s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( std::erfc(w*(X(0)-cx-rx))*std::erfc(-w*(X(0)-cx+rx)) *
|
||||
std::erfc(w*(X(1)-cy-ry))*std::erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
real_t x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = std::hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const real_t f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/// Solver for the implicit part of the ODE (the diffusion term).
|
||||
/// Solves systems of the form: (M + dt*S) k = rhs.
|
||||
class Implicit_Solver : public Solver
|
||||
{
|
||||
private:
|
||||
SparseMatrix &M, &S, A;
|
||||
CGSolver linear_solver;
|
||||
BlockILU prec;
|
||||
real_t dt;
|
||||
public:
|
||||
Implicit_Solver(SparseMatrix &M_, SparseMatrix &S_,
|
||||
const FiniteElementSpace &fes)
|
||||
: M(M_),
|
||||
S(S_),
|
||||
prec(fes.GetTypicalFE()->GetDof(),
|
||||
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
|
||||
dt(1.0)
|
||||
{
|
||||
linear_solver.iterative_mode = false;
|
||||
linear_solver.SetRelTol(1e-9);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(100);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
linear_solver.SetPreconditioner(prec);
|
||||
}
|
||||
|
||||
void SetTimeStep(real_t dt_)
|
||||
{
|
||||
real_t ddt = dt-dt_;
|
||||
|
||||
real_t epsilon;
|
||||
epsilon = std::numeric_limits<real_t>::epsilon();
|
||||
epsilon*=10;
|
||||
|
||||
if (std::abs(ddt) > epsilon)
|
||||
{
|
||||
dt = dt_;
|
||||
// Form operator A = M + dt*S
|
||||
A = S;
|
||||
A *= dt;
|
||||
A += M;
|
||||
|
||||
// this will also call SetOperator on the preconditioner
|
||||
linear_solver.SetOperator(A);
|
||||
}
|
||||
}
|
||||
|
||||
void SetOperator(const Operator &op) override
|
||||
{
|
||||
linear_solver.SetOperator(op);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
linear_solver.Mult(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The weak
|
||||
form of the advection-diffusion equation is M du/dt = K u - S u + b,
|
||||
where M is the mass matrix, K and S are the advection and diffusion
|
||||
matrices, and b describes the flow on the boundary. In the case of IMEX
|
||||
evolution, the diffusion term is treated implicitly, and the advection
|
||||
term is treated explicitly. */
|
||||
class IMEX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
BilinearForm &M, &K, &S;
|
||||
const Vector &b;
|
||||
unique_ptr<Solver> M_prec;
|
||||
CGSolver M_solver;
|
||||
unique_ptr<Implicit_Solver> implicit_solver;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
public:
|
||||
IMEX_Evolution(BilinearForm &M_, BilinearForm &K_, BilinearForm &S_,
|
||||
const Vector &b_);
|
||||
|
||||
/// Evaluate k1=M^{-1}*G1(u,t); -> k1 = M^{-1}*(K*u + b)
|
||||
void Mult1(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Evaluate k2: M*k2 = G2(u+k2*dt,t); -> (M+S*dt)*k2=-S*u
|
||||
void ImplicitSolve2(const real_t dt, const Vector &x, Vector &k);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_1 == GetEvalMode())
|
||||
{
|
||||
Mult1(x,y);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::Mult() is not overridden!");
|
||||
}
|
||||
}
|
||||
|
||||
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_2 == GetEvalMode())
|
||||
{
|
||||
ImplicitSolve2(dt,x,k);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::ImplicitSolve() is not overridden!");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
int problem = 0;
|
||||
const char *mesh_file = "../data/periodic-square.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 3;
|
||||
int ode_solver_type = 64; //IMEXRK3(3,4,3)
|
||||
real_t t_final = 10.0;
|
||||
real_t dt = 0.01;
|
||||
bool paraview = false;
|
||||
bool cg = false;
|
||||
int vis_steps = 50;
|
||||
real_t diffusion_term = 0.01;
|
||||
real_t kappa = -1.0;
|
||||
real_t sigma = -1.0;
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
bool binary = false;
|
||||
int precision = 8;
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly.");
|
||||
args.AddOption(&order, "-o", "--order", "Order of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::IMEXTypes.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step", "Time step.");
|
||||
args.AddOption(&diffusion_term, "-dc", "--diffusion-coeff",
|
||||
"Diffusion coefficient in the PDE.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview-datafiles", "-no-paraview",
|
||||
"--no-paraview-datafiles",
|
||||
"Save data files for ParaView (paraview.org) visualization.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
|
||||
"--ascii-datafiles",
|
||||
"Use binary (Sidre) or ascii format for VisIt data files.");
|
||||
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
|
||||
"--no-visit-datafiles",
|
||||
"Save data files for VisIt (visit.llnl.gov) visualization.");
|
||||
args.AddOption(&cg, "-cg", "--continuous-galerkin", "-dg",
|
||||
"--discontinuous-galerkin",
|
||||
"Use Continuous-Galerkin Finite elements (Default is DG)");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the given mesh file. We can handle geometrically
|
||||
// periodic meshes in this code.
|
||||
Mesh mesh(mesh_file);
|
||||
const int dim = mesh.Dimension();
|
||||
|
||||
// 3. Define the IMEX (Split) ODE solver used for time integration. The IMEX
|
||||
// solvers currently available are: 61 - Forward Backward Euler,
|
||||
// 62 - IMEXRK2(2,2,2), 63 - IMEXRK2(2,3,2), and 64 - IMEX_DIRK_RK3.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectIMEX(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
// command-line parameter.
|
||||
for (int lev = 0; lev < ref_levels; lev++) {mesh.UniformRefinement();}
|
||||
if (mesh.NURBSext) {mesh.SetCurvature(max(order, 1));}
|
||||
mesh.GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
// 5. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
FiniteElementCollection *fec = NULL;
|
||||
if (cg)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new DG_FECollection(order, dim, BasisType::GaussLobatto);
|
||||
}
|
||||
FiniteElementSpace fes(&mesh, fec);
|
||||
|
||||
cout << "Number of unknowns: " << fes.GetVSize() << endl;
|
||||
|
||||
// 6. Set up and assemble the bilinear and linear forms corresponding to the
|
||||
// DG discretization. The DGTraceIntegrator involves integrals over mesh
|
||||
// interior faces.
|
||||
std::unique_ptr<VectorFunctionCoefficient> velocity;
|
||||
if (0==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<3>));
|
||||
}
|
||||
|
||||
ConstantCoefficient diff_coeff(diffusion_term);
|
||||
|
||||
BilinearForm m(&fes);
|
||||
BilinearForm k(&fes);
|
||||
BilinearForm s(&fes);
|
||||
|
||||
Vector b(fes.GetTrueVSize());
|
||||
b = 0.0; //The inflow on the boundaries is set to zero.
|
||||
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
|
||||
constexpr real_t alpha = -1.0;
|
||||
k.AddDomainIntegrator(new ConvectionIntegrator(*velocity, alpha));
|
||||
|
||||
s.AddDomainIntegrator(new DiffusionIntegrator(diff_coeff));
|
||||
if (!cg)
|
||||
{
|
||||
k.AddInteriorFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity,
|
||||
alpha));
|
||||
k.AddBdrFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity, alpha));
|
||||
s.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma,
|
||||
kappa));
|
||||
s.AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma, kappa));
|
||||
}
|
||||
|
||||
|
||||
int skip_zeros = 0;
|
||||
m.Assemble(skip_zeros);
|
||||
k.Assemble(skip_zeros);
|
||||
s.Assemble(skip_zeros);
|
||||
|
||||
m.Finalize(skip_zeros);
|
||||
k.Finalize(skip_zeros);
|
||||
s.Finalize(skip_zeros);
|
||||
|
||||
// 7. Define the initial conditions.
|
||||
std::unique_ptr<FunctionCoefficient> u0;
|
||||
if (0==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<3>));
|
||||
}
|
||||
|
||||
GridFunction u(&fes);
|
||||
u.ProjectCoefficient(*u0);
|
||||
|
||||
// Create data collection for solution output: either VisItDataCollection for
|
||||
// ascii data files, or SidreDataCollection for binary data files.
|
||||
DataCollection *dc = NULL;
|
||||
if (visit)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
dc = new SidreDataCollection("Example41", &mesh);
|
||||
#else
|
||||
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = new VisItDataCollection("Example41", &mesh);
|
||||
dc->SetPrecision(precision);
|
||||
}
|
||||
dc->RegisterField("solution", &u);
|
||||
dc->SetCycle(0);
|
||||
dc->SetTime(0.0);
|
||||
dc->Save();
|
||||
}
|
||||
|
||||
// 8. Set up paraview visualization, if desired.
|
||||
unique_ptr<ParaViewDataCollection> pv;
|
||||
if (paraview)
|
||||
{
|
||||
pv = make_unique<ParaViewDataCollection>("Example41", &mesh);
|
||||
pv->SetPrefixPath("ParaView");
|
||||
pv->RegisterField("solution", &u);
|
||||
pv->SetLevelsOfDetail(order);
|
||||
pv->SetDataFormat(VTKFormat::BINARY);
|
||||
pv->SetHighOrderOutput(true);
|
||||
pv->SetCycle(0);
|
||||
pv->SetTime(0.0);
|
||||
pv->Save();
|
||||
}
|
||||
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
visualization = false;
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sout.precision(precision);
|
||||
sout << "solution\n" << mesh << u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Define the time-dependent evolution operator describing the ODE
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
IMEX_Evolution adv(m, k, s, b);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
ode_solver->Init(adv);
|
||||
|
||||
bool done = false;
|
||||
for (int ti = 0; !done; )
|
||||
{
|
||||
real_t dt_real = min(dt, t_final - t);
|
||||
ode_solver->Step(u, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
if (done || ti % vis_steps == 0)
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
if (paraview)
|
||||
{
|
||||
pv->SetCycle(ti);
|
||||
pv->SetTime(t);
|
||||
pv->Save();
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
sout << "solution\n" << mesh << u << flush;
|
||||
}
|
||||
if (visit)
|
||||
{
|
||||
dc->SetCycle(ti);
|
||||
dc->SetTime(t);
|
||||
dc->Save();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
delete fec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Implementation of class IMEX_Evolution
|
||||
IMEX_Evolution::IMEX_Evolution(BilinearForm &M_, BilinearForm &K_,
|
||||
BilinearForm &S_, const Vector &b_)
|
||||
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
|
||||
M(M_), K(K_), S(S_), b(b_), z(height)
|
||||
{
|
||||
Array<int> ess_tdof_list;
|
||||
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
|
||||
{
|
||||
M_prec = make_unique<DSmoother>(M.SpMat());
|
||||
M_solver.SetOperator(M.SpMat());
|
||||
implicit_solver = make_unique<Implicit_Solver>(M.SpMat(), S.SpMat(),
|
||||
*M.FESpace());
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Implicit time integration is not supported with partial assembly");
|
||||
}
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::Mult1(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Perform the explicit step
|
||||
// y = M^{-1} (K x + b)
|
||||
K.Mult(x, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ImplicitSolve2(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
// Perform the implicit step
|
||||
// solve for k, k = -(M+dt S)^{-1} S x
|
||||
MFEM_VERIFY(implicit_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
S.Mult(x, z);
|
||||
z.Neg();
|
||||
implicit_solver->SetTimeStep(dt);
|
||||
implicit_solver->Mult(z, k);
|
||||
}
|
||||
@@ -1,737 +0,0 @@
|
||||
// MFEM Example 41 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex41p
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex41p
|
||||
// mpirun -np 4 ex41p -cg
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-hexagon.mesh -p 0 -dt 0.005 -tf 10
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-square.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/star-q3.mesh -p 1 -rp 1 -dt 0.001 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/disc-nurbs.mesh -p 2 -rp 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-square.mesh -rp 2 -dt 0.0025 -tf 9 -vs 20
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-cube.mesh -p 0 -rs 2 -o 2 -dt 0.01 -tf 8
|
||||
//
|
||||
// Device sample runs:
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection-diffusion
|
||||
// equation du/dt + v.grad(u) - a div(grad(u)) = 0, where v is a
|
||||
// given fluid velocity, a is the diffusion coefficient, and
|
||||
// u0(x)=u(0,x) is a given initial condition.
|
||||
//
|
||||
// The example demonstrates the use of Discontinuous Galerkin (DG)
|
||||
// bilinear forms in MFEM (face integrators), DG-LOR Preconditioning
|
||||
// and the use of IMEX ODE time integrators.
|
||||
//
|
||||
// The Option to use Continuous Finite Elements is available too.
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
// Velocity coefficient
|
||||
template<int problem=0>
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
real_t d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initial condition
|
||||
template<int problem=0>
|
||||
real_t u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
real_t rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const real_t s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( std::erfc(w*(X(0)-cx-rx))*std::erfc(-w*(X(0)-cx+rx)) *
|
||||
std::erfc(w*(X(1)-cy-ry))*std::erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
real_t x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = std::hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const real_t f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
class Implicit_Solver : public Solver
|
||||
{
|
||||
private:
|
||||
HypreParMatrix &M, &S;
|
||||
HypreParMatrix *A;
|
||||
CGSolver linear_solver;
|
||||
real_t dt;
|
||||
SparseMatrix M_diag;
|
||||
public:
|
||||
Implicit_Solver(HypreParMatrix &M_, HypreParMatrix &S_,
|
||||
const FiniteElementSpace &fes)
|
||||
: M(M_),
|
||||
S(S_),
|
||||
A(nullptr),
|
||||
linear_solver(M.GetComm()),
|
||||
dt(1.0)
|
||||
{
|
||||
linear_solver.iterative_mode = false;
|
||||
linear_solver.SetRelTol(1e-9);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(100);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
|
||||
M.GetDiag(M_diag);
|
||||
}
|
||||
|
||||
void SetTimeStep(real_t dt_)
|
||||
{
|
||||
real_t ddt = dt-dt_;
|
||||
|
||||
// syncronize ddt across all processes
|
||||
MPI_Comm comm = M.GetComm();
|
||||
int myrank;
|
||||
MPI_Comm_rank(comm, &myrank);
|
||||
MPI_Bcast(&ddt, 1, MPI_DOUBLE, 0, comm);
|
||||
|
||||
real_t epsilon;
|
||||
epsilon = std::numeric_limits<real_t>::epsilon();
|
||||
// allow for some tolerance in the time stepping process
|
||||
epsilon*=10;
|
||||
|
||||
if (fabs(ddt) > epsilon)
|
||||
{
|
||||
if (0==myrank)
|
||||
{
|
||||
cout << "Updating Implicit_Solver time step from " << dt
|
||||
<< " to " << dt_ << endl;
|
||||
}
|
||||
|
||||
delete A;
|
||||
dt = dt_;
|
||||
// Form operator A = M + dt*S
|
||||
A = Add(dt, S, 1.0, M);
|
||||
linear_solver.SetOperator(*A);
|
||||
}
|
||||
}
|
||||
|
||||
void SetOperator(const Operator &op) override
|
||||
{
|
||||
linear_solver.SetOperator(op);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
linear_solver.Mult(x, y);
|
||||
}
|
||||
|
||||
void SetPreconditioner(Solver &precond)
|
||||
{
|
||||
linear_solver.SetPreconditioner(precond);
|
||||
}
|
||||
|
||||
~Implicit_Solver() override
|
||||
{
|
||||
delete A;
|
||||
}
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
|
||||
form of the advection-diffusion equation is (M + dt S) du/dt = Su - K u + b
|
||||
, where M and K are the mass and advection matrices, and b describes the
|
||||
flow on the boundary. In the case of IMEX evolution, the diffusion term is
|
||||
treated implicitly, and the advection term is treated explicitly. */
|
||||
class IMEX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
OperatorHandle M, K, S, A;
|
||||
const Vector &b;
|
||||
Solver *M_prec;
|
||||
CGSolver M_solver;
|
||||
Implicit_Solver *implicit_solver;
|
||||
LORSolver<HypreBoomerAMG>* lor_solver;
|
||||
|
||||
mutable Vector z;
|
||||
mutable Vector w;
|
||||
|
||||
public:
|
||||
IMEX_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, ParBilinearForm &S_,
|
||||
const Vector &b_, ParBilinearForm &A_);
|
||||
|
||||
virtual
|
||||
~IMEX_Evolution()
|
||||
{
|
||||
delete implicit_solver;
|
||||
delete lor_solver;
|
||||
delete M_prec;
|
||||
}
|
||||
|
||||
void Mult1(const Vector &x, Vector &y) const;
|
||||
|
||||
void ImplicitSolve2(const real_t dt, const Vector &x, Vector &k);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_1 == GetEvalMode())
|
||||
{
|
||||
Mult1(x,y);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::Mult() is not overridden!");
|
||||
}
|
||||
}
|
||||
|
||||
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_2 == GetEvalMode())
|
||||
{
|
||||
ImplicitSolve2(dt,x,k);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::ImplicitSolve() is not overridden!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
int problem = 0;
|
||||
const char *mesh_file = "../data/periodic-square.mesh";
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 0;
|
||||
int order = 3;
|
||||
int ode_solver_type = 64; // 61 - Forward Backward Euler
|
||||
// 62 - IMEXRK2(2,2,2)
|
||||
// 63 - IMEXRK2(2,3,2)
|
||||
// 64 - IMEXRK3(3,4,3)
|
||||
real_t t_final = 10.0;
|
||||
real_t dt = 0.01;
|
||||
bool paraview = false;
|
||||
bool cg = false;
|
||||
int vis_steps = 50;
|
||||
bool adios2 = false;
|
||||
bool binary = false;
|
||||
real_t diffusion_term = 0.01;
|
||||
real_t kappa = -1.0;
|
||||
real_t sigma = -1.0;
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int precision = 16;
|
||||
cout.precision(precision);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::IMEXTypes.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&diffusion_term, "-dc", "--diffusion-coeff",
|
||||
"Diffusion coefficient in the PDE.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview-datafiles", "-no-paraview",
|
||||
"--no-paraview-datafiles",
|
||||
"Save data files for ParaView (paraview.org) visualization.");
|
||||
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
|
||||
"--no-visit-datafiles",
|
||||
"Save data files for VisIt (visit.llnl.gov) visualization.");
|
||||
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
|
||||
"--no-adios2-streams",
|
||||
"Save data using adios2 streams.");
|
||||
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
|
||||
"--ascii-datafiles",
|
||||
"Use binary (Sidre) or ascii format for VisIt data files.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&cg, "-cg", "--continuous-galerkin", "-dg",
|
||||
"--discontinuous-galerkin",
|
||||
"Use Continuous-Galerkin Finite elements (Default is DG)");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (Mpi::Root())
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle geometrically
|
||||
// periodic meshes in this code.
|
||||
Mesh *mesh = new Mesh(mesh_file);
|
||||
const int dim = mesh->Dimension();
|
||||
|
||||
// 4. Define the IMEX (Split) ODE solver used for time integration. The IMEX
|
||||
// solvers currently available are: 55 - Forward Backward Euler,
|
||||
// 56 - IMEXRK2(2,2,2), 57 - IMEXRK2(2,3,2), and
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectIMEX(ode_solver_type);
|
||||
|
||||
// 5. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
// command-line parameter.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++) { mesh->UniformRefinement(); }
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
}
|
||||
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
|
||||
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 7. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
FiniteElementCollection *fec = NULL;
|
||||
if (cg)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new DG_FECollection(order, dim, BasisType::GaussLobatto);
|
||||
}
|
||||
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, fec);
|
||||
|
||||
HYPRE_BigInt global_vSize = fes->GlobalTrueVSize();
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "Number of unknowns: " << global_vSize << endl;
|
||||
}
|
||||
|
||||
// 8. Set up and assemble the bilinear and linear forms corresponding to the
|
||||
// DG discretization. The DGTraceIntegrator involves integrals over mesh
|
||||
// interior faces.
|
||||
std::unique_ptr<VectorFunctionCoefficient> velocity;
|
||||
if (0==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<3>));
|
||||
}
|
||||
ConstantCoefficient diff_coeff(diffusion_term);
|
||||
ConstantCoefficient dt_diff_coeff(dt*diffusion_term);
|
||||
|
||||
ParBilinearForm *m = new ParBilinearForm(fes);
|
||||
ParBilinearForm *k = new ParBilinearForm(fes);
|
||||
ParBilinearForm *s = new ParBilinearForm(fes);
|
||||
|
||||
m->AddDomainIntegrator(new MassIntegrator());
|
||||
|
||||
constexpr real_t alpha = -1.0;
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(*velocity, alpha));
|
||||
|
||||
s->AddDomainIntegrator(new DiffusionIntegrator(diff_coeff));
|
||||
|
||||
// For the preconditioner - create billinear form corresponding to
|
||||
// operator (M + dt S)
|
||||
ParBilinearForm *a = new ParBilinearForm(fes);
|
||||
a->AddDomainIntegrator(new MassIntegrator);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(dt_diff_coeff));
|
||||
if (!cg)
|
||||
{
|
||||
k->AddInteriorFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity,
|
||||
alpha));
|
||||
k->AddBdrFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity, alpha));
|
||||
s->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma,
|
||||
kappa));
|
||||
s->AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma, kappa));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(dt_diff_coeff, sigma,
|
||||
kappa));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(dt_diff_coeff, sigma, kappa));
|
||||
}
|
||||
|
||||
int skip_zeros = 0;
|
||||
m->Assemble(skip_zeros);
|
||||
k->Assemble(skip_zeros);
|
||||
s->Assemble(skip_zeros);
|
||||
a->Assemble();
|
||||
|
||||
m->Finalize(skip_zeros);
|
||||
k->Finalize(skip_zeros);
|
||||
s->Finalize(skip_zeros);
|
||||
a->Finalize(skip_zeros);
|
||||
HypreParVector b(fes);
|
||||
b = 0.0;
|
||||
|
||||
// 9. Define the initial conditions. Set up visualization (if desired).
|
||||
std::unique_ptr<FunctionCoefficient> u0;
|
||||
if (0==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<3>));
|
||||
}
|
||||
ParGridFunction *u = new ParGridFunction(fes);
|
||||
u->ProjectCoefficient(*u0);
|
||||
HypreParVector *U = u->GetTrueDofs();
|
||||
|
||||
DataCollection *dc = NULL;
|
||||
if (visit)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
dc = new SidreDataCollection("Example41-Parallel", pmesh);
|
||||
#else
|
||||
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = new VisItDataCollection("Example41-Parallel", pmesh);
|
||||
dc->SetPrecision(precision);
|
||||
// To save the mesh using MFEM's parallel mesh format:
|
||||
// dc->SetFormat(DataCollection::PARALLEL_FORMAT);
|
||||
}
|
||||
dc->RegisterField("solution", u);
|
||||
dc->SetCycle(0);
|
||||
dc->SetTime(0.0);
|
||||
dc->Save();
|
||||
}
|
||||
ParaViewDataCollection *pd = NULL;
|
||||
if (paraview)
|
||||
{
|
||||
pd = new ParaViewDataCollection("Example41P", pmesh);
|
||||
pd->SetPrefixPath("ParaView");
|
||||
pd->RegisterField("solution", u);
|
||||
pd->SetLevelsOfDetail(order);
|
||||
pd->SetDataFormat(VTKFormat::BINARY);
|
||||
pd->SetHighOrderOutput(true);
|
||||
pd->SetCycle(0);
|
||||
pd->SetTime(0.0);
|
||||
pd->Save();
|
||||
}
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
}
|
||||
visualization = false;
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout.precision(precision);
|
||||
sout << "solution\n" << *pmesh << *u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
ADIOS2DataCollection *adios2_dc = NULL;
|
||||
if (adios2)
|
||||
{
|
||||
std::string postfix(mesh_file);
|
||||
postfix.erase(0, std::string("../data/").size() );
|
||||
postfix += "_o" + std::to_string(order);
|
||||
const std::string collection_name = "ex41-p-" + postfix + ".bp";
|
||||
|
||||
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
|
||||
// output data substreams are half the number of mpi processes
|
||||
adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
|
||||
// adios2_dc->SetLevelsOfDetail(2);
|
||||
adios2_dc->RegisterField("solution", u);
|
||||
adios2_dc->SetCycle(0);
|
||||
adios2_dc->SetTime(0.0);
|
||||
adios2_dc->Save();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 10. Define the time-dependent evolution operator describing the
|
||||
// ODE right-hand side, and perform time-integration (looping
|
||||
// over the time iterations, ti, with a time-step dt).
|
||||
IMEX_Evolution adv(*m, *k, *s, b, *a);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
ode_solver->Init(adv);
|
||||
|
||||
|
||||
bool done = false;
|
||||
for (int ti = 0; !done; )
|
||||
{
|
||||
real_t dt_real = min(dt, t_final - t);
|
||||
ode_solver->Step(*U, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
if (done || ti % vis_steps == 0)
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
}
|
||||
*u = *U;
|
||||
if (visualization)
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << *pmesh << *u << flush;
|
||||
}
|
||||
if (paraview)
|
||||
{
|
||||
pd->SetCycle(ti);
|
||||
pd->SetTime(t);
|
||||
pd->Save();
|
||||
}
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
// transient solutions can be visualized with ParaView
|
||||
if (adios2)
|
||||
{
|
||||
adios2_dc->SetCycle(ti);
|
||||
adios2_dc->SetTime(t);
|
||||
adios2_dc->Save();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Free the used memory.
|
||||
delete pd;
|
||||
delete U;
|
||||
delete u;
|
||||
delete a;
|
||||
delete s;
|
||||
delete k;
|
||||
delete m;
|
||||
delete fes;
|
||||
delete pmesh;
|
||||
delete dc;
|
||||
delete fec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implementation of class IMEX_Evolution
|
||||
IMEX_Evolution::IMEX_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
ParBilinearForm &S_, const Vector &b_, ParBilinearForm &A_)
|
||||
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()), b(b_),
|
||||
M_solver(M_.ParFESpace()->GetComm()), z(height), w(height)
|
||||
{
|
||||
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
|
||||
{
|
||||
M.Reset(M_.ParallelAssemble(), true);
|
||||
K.Reset(K_.ParallelAssemble(), true);
|
||||
S.Reset(S_.ParallelAssemble(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
M.Reset(&M_, false);
|
||||
K.Reset(&K_, false);
|
||||
S.Reset(&S_, false);
|
||||
}
|
||||
|
||||
M_solver.SetOperator(*M);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
if (M_.GetAssemblyLevel() == AssemblyLevel::LEGACY)
|
||||
{
|
||||
A.Reset(A_.ParallelAssemble(), true);
|
||||
HypreParMatrix &M_mat = *M.As<HypreParMatrix>();
|
||||
HypreParMatrix &S_mat = *S.As<HypreParMatrix>();
|
||||
HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
|
||||
M_prec = hypre_prec;
|
||||
|
||||
implicit_solver = new Implicit_Solver(M_mat, S_mat, *M_.FESpace());
|
||||
lor_solver = new LORSolver<HypreBoomerAMG>(A_, ess_tdof_list);
|
||||
lor_solver->GetSolver().SetSystemsOptions(A_.ParFESpace()->GetVDim(), true);
|
||||
implicit_solver -> SetPreconditioner(*lor_solver);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Implicit time integration is not supported with partial assembly");
|
||||
}
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::Mult1(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Perform the explicit step
|
||||
// y = M^{-1} (K x + b)
|
||||
K->Mult(x, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ImplicitSolve2(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
// Perform the implicit step
|
||||
// solve for k, k = -(M+dt S)^{-1} S x
|
||||
MFEM_VERIFY(implicit_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
S->Mult(x, z);
|
||||
z*= -1.0;
|
||||
implicit_solver->SetTimeStep(dt);
|
||||
implicit_solver->Mult(z, k);
|
||||
}
|
||||
+1
-6
@@ -9,7 +9,6 @@
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
|
||||
@@ -18,18 +17,14 @@
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -235,7 +230,7 @@ int main(int argc, char *argv[])
|
||||
pcg->SetMaxIter(2000);
|
||||
pcg->SetPrintLevel(1);
|
||||
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
|
||||
else if (pa || ea) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
|
||||
+1
-20
@@ -160,7 +160,6 @@ int main(int argc, char *argv[])
|
||||
bool paraview = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -188,9 +187,6 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -370,11 +366,6 @@ int main(int argc, char *argv[])
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(m, k, b);
|
||||
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
adv.SetImplicitVariableType(imp_var);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -468,17 +459,7 @@ void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
MFEM_VERIFY(dg_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
M.Mult(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
K.Mult(x, z);
|
||||
}
|
||||
K.Mult(x, z);
|
||||
z += b;
|
||||
dg_solver->SetTimeStep(dt);
|
||||
dg_solver->Mult(z, k);
|
||||
|
||||
+1
-20
@@ -257,7 +257,6 @@ int main(int argc, char *argv[])
|
||||
bool adios2 = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
PrecType prec_type = PrecType::AIR;
|
||||
#else
|
||||
@@ -291,9 +290,6 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
|
||||
"implicit solves. 0 for ILU, 1 for pAIR-AMG.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -540,11 +536,6 @@ int main(int argc, char *argv[])
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(*m, *k, *B, prec_type);
|
||||
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
adv.SetImplicitVariableType(imp_var);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -685,17 +676,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
// (M - dt*K) d = K*u + b
|
||||
void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
M->Mult(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
K->Mult(x, z);
|
||||
}
|
||||
K->Mult(x, z);
|
||||
z += b;
|
||||
dg_solver->SetTimeStep(dt);
|
||||
dg_solver->Mult(z, k);
|
||||
|
||||
@@ -14,12 +14,6 @@ list(APPEND GINKGO_EXAMPLES_SRCS
|
||||
ex1.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI AND GINKGO_BUILD_MPI)
|
||||
list(APPEND GINKGO_EXAMPLES_SRCS
|
||||
ex1p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::IcPreconditioner ginkgo_precond(exec, "paric", 30);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, ginkgo_precond);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
@@ -225,7 +225,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
@@ -283,7 +283,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
|
||||
@@ -1,436 +0,0 @@
|
||||
// MFEM Example 1 - Parallel Version
|
||||
// GINKGO Modification
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d cuda
|
||||
// mpirun -np 4 ex1p -fa -d cuda
|
||||
// mpirun -np 4 ex1p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex1p -pa -d raja-omp
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -o 4 -a
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/fichera-mixed.mesh
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-hip
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef MFEM_USE_GINKGO
|
||||
#error This example requires that MFEM is built with MFEM_USE_GINKGO=YES
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
int solver_config = 0;
|
||||
int print_lvl = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&solver_config, "-s", "--solver-config",
|
||||
"Solver and preconditioner combination: \n\t"
|
||||
" 0 - Ginkgo solver and Ginkgo preconditioner, \n\t"
|
||||
" 1 - Ginkgo solver and MFEM preconditioner, \n\t"
|
||||
" 2 - MFEM solver and Ginkgo preconditioner, \n\t"
|
||||
" 3 - MFEM solver and MFEM preconditioner.");
|
||||
args.AddOption(&print_lvl, "-pl", "--print-level",
|
||||
"Print level for iterative solver (1 prints every iteration).");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.SetGPUAwareMPI(true);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm b(&fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid
|
||||
// function corresponding to fespace. Initialize x with initial guess of
|
||||
// zero, which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the
|
||||
// Diffusion domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (fa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
// Sort the matrix column indices when running on GPU or with OpenMP (i.e.
|
||||
// when Device::IsEnabled() returns true). This makes the results
|
||||
// bit-for-bit deterministic at the cost of somewhat longer run time.
|
||||
a.EnableSparseMatrixSorting(Device::IsEnabled());
|
||||
}
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
switch (solver_config)
|
||||
{
|
||||
// Solve the linear system with CG + Schwarz (with IC) from Ginkgo
|
||||
case 0:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
Ginkgo::IcPreconditioner local_solver(exec, "exact");
|
||||
Ginkgo::SchwarzPreconditioner gko_M(exec, MPI_COMM_WORLD, local_solver);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// Solve the linear system with CG from Ginkgo + MFEM preconditioner
|
||||
case 1:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
//Create MFEM preconditioner and wrap it for Ginkgo's use.
|
||||
HypreBoomerAMG M((HypreParMatrix&)(*A));
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// Ginkgo Schwarz preconditioner (local ParIC) + MFEM CG solver
|
||||
case 2:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + Ginkgo preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
Ginkgo::IcPreconditioner local_M(exec, "exact");
|
||||
Ginkgo::SchwarzPreconditioner M(exec, MPI_COMM_WORLD, local_M);
|
||||
M.SetOperator(*(A.Ptr())); // Generate the preconditioner for the matrix A.
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// MFEM solver + MFEM preconditioner
|
||||
case 3:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
|
||||
HypreBoomerAMG M((HypreParMatrix&)(*A));
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
} // End switch on solver_config
|
||||
}
|
||||
// Partial assembly mode. Cannot use Ginkgo preconditioners, but can use Ginkgo
|
||||
// solvers.
|
||||
else
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
// Use Jacobi preconditioning in partial assembly mode.
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
switch (solver_config)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
|
||||
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Use Ginkgo solver with MFEM preconditioner
|
||||
case 1:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
// Wrap MFEM preconditioner for Ginkgo's use.
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// No Ginkgo preconditioners work with matrix-free; error
|
||||
case 2:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
|
||||
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Use MFEM solver and preconditioner
|
||||
case 3:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
} // End switch on solver_config
|
||||
}
|
||||
else // CG with no preconditioning
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + no preconditioner...\n"; }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh.Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -20,8 +20,9 @@ CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
# Currently there are only serial Ginkgo examples
|
||||
SEQ_EXAMPLES = ex1
|
||||
PAR_EXAMPLES = ex1p
|
||||
PAR_EXAMPLES =
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
@@ -76,4 +77,4 @@ clean-build:
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh sol.gf mesh.* sol.*
|
||||
@rm -f refined.mesh sol.gf
|
||||
|
||||
+6
-15
@@ -22,11 +22,11 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
|
||||
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
|
||||
ex37p ex39p ex40p ex41p
|
||||
ex37p ex39p ex40p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
|
||||
ex22p ex24p ex25p ex26p ex34p ex35p
|
||||
@@ -71,7 +71,6 @@ endif
|
||||
|
||||
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
|
||||
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
|
||||
SUBDIRS_TEST_NOCLEAN = $(addsuffix /test-noclean,$(SUBDIRS))
|
||||
SUBDIRS_CLEAN = $(addsuffix /clean,$(SUBDIRS))
|
||||
SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
|
||||
|
||||
@@ -88,9 +87,8 @@ SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
|
||||
|
||||
all: $(EXAMPLES) $(SUBDIRS_ALL)
|
||||
|
||||
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) \
|
||||
$(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
|
||||
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) $(SUBDIRS_CLEAN):
|
||||
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
|
||||
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN):
|
||||
$(MAKE) -C $(@D) $(@F)
|
||||
$(SUBDIRS_TPRINT):
|
||||
@$(MAKE) -C $(@D) $(@F)
|
||||
@@ -109,7 +107,6 @@ endif
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
test: $(SUBDIRS_TEST)
|
||||
test-noclean: $(SUBDIRS_TEST_NOCLEAN)
|
||||
test-print: $(SUBDIRS_TPRINT)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
@@ -160,12 +157,6 @@ ex37-test-seq: ex37
|
||||
@$(call mfem-test,$<,, Serial example,-mi 3)
|
||||
ex37p-test-par: ex37p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-mi 3)
|
||||
ex39-test-seq: ex39
|
||||
@$(call mfem-test,$<,, Serial example,-m ../data/compass.mesh)
|
||||
ex41-test-seq: ex41
|
||||
@$(call mfem-test,$<,, Serial example,-tf 1.0)
|
||||
ex41p-test-par: ex41p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-tf 1.0)
|
||||
# Testing: optional tests
|
||||
ifeq ($(MFEM_USE_STRUMPACK),YES)
|
||||
ex11p-test-strumpack: ex11p
|
||||
@@ -204,8 +195,8 @@ clean-build:
|
||||
clean-exec:
|
||||
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh ex6p-checkpoint.*
|
||||
@rm -rf Example5* Example9* Example15* Example16* Example23* ParaView
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* sol_z.*
|
||||
@rm -f order.* ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* order.*
|
||||
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
|
||||
@rm -f deformed.* velocity.* elastic_energy.* mode_* mode_deriv_* flux.*
|
||||
@rm -f ex5-p-*.bp ex9-p-*.bp ex12-p-*.bp ex16-p-*.bp
|
||||
@rm -f ex16.mesh ex16-mesh.* ex16-init.* ex16-final.*
|
||||
|
||||
+27
-57
@@ -128,62 +128,43 @@ set(SRCS
|
||||
normal_deriv_restriction.cpp
|
||||
staticcond.cpp
|
||||
tmop.cpp
|
||||
tmop/pa.cpp
|
||||
tmop/assemble/diag2_limit.cpp
|
||||
tmop/assemble/diag2.cpp
|
||||
tmop/assemble/grad2_limit.cpp
|
||||
tmop/assemble/grad2.cpp
|
||||
tmop/assemble/diag3_limit.cpp
|
||||
tmop/assemble/diag3.cpp
|
||||
tmop/assemble/grad3_limit.cpp
|
||||
tmop/assemble/grad3.cpp
|
||||
tmop/metrics/001.cpp
|
||||
tmop/metrics/002.cpp
|
||||
tmop/metrics/007.cpp
|
||||
tmop/metrics/056.cpp
|
||||
tmop/metrics/077.cpp
|
||||
tmop/metrics/080.cpp
|
||||
tmop/metrics/094.cpp
|
||||
tmop/metrics/302.cpp
|
||||
tmop/metrics/303.cpp
|
||||
tmop/metrics/315.cpp
|
||||
tmop/metrics/318.cpp
|
||||
tmop/metrics/321.cpp
|
||||
tmop/metrics/332.cpp
|
||||
tmop/metrics/338.cpp
|
||||
tmop/mult/grad2_limit.cpp
|
||||
tmop/mult/grad2.cpp
|
||||
tmop/mult/mult2_limit.cpp
|
||||
tmop/mult/mult2.cpp
|
||||
tmop/mult/grad3_limit.cpp
|
||||
tmop/mult/grad3.cpp
|
||||
tmop/mult/mult3_limit.cpp
|
||||
tmop/mult/mult3.cpp
|
||||
tmop/tools/det2_jpr.cpp
|
||||
tmop/tools/det3_jpr.cpp
|
||||
tmop/tools/discrete.cpp
|
||||
tmop/tools/energy2_limit.cpp
|
||||
tmop/tools/energy2.cpp
|
||||
tmop/tools/energy3_limit.cpp
|
||||
tmop/tools/energy3.cpp
|
||||
tmop/tools/target2.cpp
|
||||
tmop/tools/target3.cpp
|
||||
tmop/tmop_pa.cpp
|
||||
tmop/tmop_pa_da3.cpp
|
||||
tmop/tmop_pa_h2d.cpp
|
||||
tmop/tmop_pa_h2d_c0.cpp
|
||||
tmop/tmop_pa_h2m.cpp
|
||||
tmop/tmop_pa_h2m_c0.cpp
|
||||
tmop/tmop_pa_h2s.cpp
|
||||
tmop/tmop_pa_h2s_c0.cpp
|
||||
tmop/tmop_pa_h3d.cpp
|
||||
tmop/tmop_pa_h3d_c0.cpp
|
||||
tmop/tmop_pa_h3m.cpp
|
||||
tmop/tmop_pa_h3m_c0.cpp
|
||||
tmop/tmop_pa_h3s.cpp
|
||||
tmop/tmop_pa_h3s_c0.cpp
|
||||
tmop/tmop_pa_jp2.cpp
|
||||
tmop/tmop_pa_jp3.cpp
|
||||
tmop/tmop_pa_p2.cpp
|
||||
tmop/tmop_pa_p2_c0.cpp
|
||||
tmop/tmop_pa_p3.cpp
|
||||
tmop/tmop_pa_p3_c0.cpp
|
||||
tmop/tmop_pa_tc2.cpp
|
||||
tmop/tmop_pa_tc3.cpp
|
||||
tmop/tmop_pa_w2.cpp
|
||||
tmop/tmop_pa_w2_c0.cpp
|
||||
tmop/tmop_pa_w3.cpp
|
||||
tmop/tmop_pa_w3_c0.cpp
|
||||
tmop_tools.cpp
|
||||
tmop_amr.cpp
|
||||
gslib.cpp
|
||||
gslib/findptsedge_local_2.cpp
|
||||
gslib/findptsedge_local_3.cpp
|
||||
gslib/findptssurf_local_3.cpp
|
||||
gslib/findpts_local_2.cpp
|
||||
gslib/findpts_local_3.cpp
|
||||
gslib/interpolate_local_1.cpp
|
||||
gslib/interpolate_local_2.cpp
|
||||
gslib/interpolate_local_3.cpp
|
||||
transfer.cpp
|
||||
hyperbolic.cpp
|
||||
integrator.cpp
|
||||
bounds.cpp
|
||||
particleset.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
@@ -195,16 +176,12 @@ set(HDRS
|
||||
integ/bilininteg_dgtrace_kernels.hpp
|
||||
integ/bilininteg_vecdiffusion_kernels.hpp
|
||||
integ/bilininteg_convection_kernels.hpp
|
||||
integ/bilininteg_diffusion_pa_simplices.hpp
|
||||
integ/bilininteg_diffusion_kernels.hpp
|
||||
integ/bilininteg_elasticity_kernels.hpp
|
||||
integ/bilininteg_hcurl_kernels.hpp
|
||||
integ/bilininteg_hdiv_kernels.hpp
|
||||
integ/bilininteg_hcurlhdiv_kernels.hpp
|
||||
integ/bilininteg_mass_kernels.hpp
|
||||
integ/bilininteg_mass_pa_simplices.hpp
|
||||
integ/bilininteg_vecdiffusion_pa.hpp
|
||||
integ/bilininteg_vecmass_pa.hpp
|
||||
coefficient.hpp
|
||||
complex_fem.hpp
|
||||
convergence.hpp
|
||||
@@ -302,21 +279,14 @@ set(HDRS
|
||||
tfespace.hpp
|
||||
tintrules.hpp
|
||||
tmop.hpp
|
||||
tmop/pa.hpp
|
||||
tmop/assemble/grad2.hpp
|
||||
tmop/assemble/grad2.hpp
|
||||
tmop/mult/mult2.hpp
|
||||
tmop/mult/mult3.hpp
|
||||
tmop/tools/energy2.hpp
|
||||
tmop/tmop_pa.hpp
|
||||
tmop_tools.hpp
|
||||
tmop_amr.hpp
|
||||
gslib.hpp
|
||||
gslib/gslib_kernel_helpers.hpp
|
||||
transfer.hpp
|
||||
hyperbolic.hpp
|
||||
integrator.hpp
|
||||
bounds.hpp
|
||||
particleset.hpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_SIDRE)
|
||||
|
||||
+9
-42
@@ -729,8 +729,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
tr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (tr != NULL)
|
||||
{
|
||||
mfem::DofTransformation doftrans;
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
fe1 = fes -> GetFE (tr -> Elem1No);
|
||||
// The fe2 object is really a dummy and not used on the boundaries,
|
||||
// but we can't dereference a NULL pointer, and we don't want to
|
||||
@@ -744,7 +743,6 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
|
||||
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
|
||||
elemmat);
|
||||
doftrans.TransformDual(elemmat);
|
||||
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -827,46 +825,14 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
Vector &b, OperatorHandle &A, Vector &X,
|
||||
Vector &B, int copy_interior)
|
||||
{
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
if (ext)
|
||||
{
|
||||
if (hybridization)
|
||||
{
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
Vector conf_b, conf_x;
|
||||
if (P)
|
||||
{
|
||||
// Nonconforming
|
||||
conf_b.SetSize(P->Width());
|
||||
conf_x.SetSize(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conforming
|
||||
conf_b.MakeRef(b, 0, b.Size());
|
||||
conf_x.MakeRef(x, 0, x.Size());
|
||||
}
|
||||
|
||||
A_constrained->EliminateRHS(conf_x, conf_b);
|
||||
|
||||
if (P)
|
||||
{
|
||||
R->MultTranspose(conf_b, b); // store eliminated rhs in b
|
||||
}
|
||||
|
||||
hybridization->ReduceRHS(conf_b, B);
|
||||
ConstrainedOperator A_constrained(this, ess_tdof_list);
|
||||
A_constrained.EliminateRHS(x, b);
|
||||
hybridization->ReduceRHS(b, B);
|
||||
X.SetSize(B.Size());
|
||||
X = 0.0;
|
||||
}
|
||||
@@ -876,6 +842,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
}
|
||||
return;
|
||||
}
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
// Transform the system and perform the elimination in B, based on the
|
||||
@@ -911,6 +878,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
if (hybridization)
|
||||
{
|
||||
// Reduction to the Lagrange multipliers system
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
Vector conf_b(P->Width()), conf_x(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
@@ -923,6 +891,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
else
|
||||
{
|
||||
// Variational restriction with P
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
B.SetSize(P->Width());
|
||||
P->MultTranspose(b, B);
|
||||
X.SetSize(R->Height());
|
||||
@@ -1725,7 +1694,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < trial_fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
@@ -1734,8 +1702,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
ftr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (ftr != NULL)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs, ran_dof_trans);
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
@@ -1751,7 +1719,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-22
@@ -1345,8 +1345,7 @@ real_t DiffusionIntegrator::ComputeFluxEnergy
|
||||
}
|
||||
|
||||
const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe,
|
||||
const bool stroud)
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe)
|
||||
{
|
||||
int order;
|
||||
if (trial_fe.Space() == FunctionSpace::Pk)
|
||||
@@ -1363,15 +1362,7 @@ const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
if (stroud)
|
||||
{
|
||||
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
MassIntegrator::MassIntegrator(const IntegrationRule *ir)
|
||||
@@ -1458,8 +1449,7 @@ void MassIntegrator::AssembleElementMatrix2(
|
||||
|
||||
const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans,
|
||||
const bool stroud)
|
||||
const ElementTransformation &Trans)
|
||||
{
|
||||
// int order = trial_fe.GetOrder() + test_fe.GetOrder();
|
||||
const int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
|
||||
@@ -1468,15 +1458,7 @@ const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
if (stroud)
|
||||
{
|
||||
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
|
||||
@@ -3084,6 +3066,7 @@ void VectorDiffusionIntegrator::AssembleElementMatrix(
|
||||
|
||||
for (int i = 0; i < ir -> GetNPoints(); i++)
|
||||
{
|
||||
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
el.CalcDShape(ip, dshape);
|
||||
|
||||
|
||||
+44
-85
@@ -2184,22 +2184,11 @@ public:
|
||||
const Vector&, const Vector&,
|
||||
Vector&, const int, const int);
|
||||
|
||||
using ApplySimplexKernelType = void(*)(const int, const bool, const Array<int>&,
|
||||
const Array<int>&,
|
||||
const Array<int>&, const Array<int>&, const Array<int>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Vector&, const Vector&,
|
||||
Vector&, const int, const int);
|
||||
|
||||
using DiagonalKernelType = void(*)(const int, const bool, const Array<real_t>&,
|
||||
const Array<real_t>&, const Vector&, Vector&,
|
||||
const int, const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
|
||||
int));
|
||||
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
@@ -2352,8 +2341,7 @@ public:
|
||||
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const bool stroud = false);
|
||||
const FiniteElement &test_fe);
|
||||
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
@@ -2364,13 +2352,6 @@ public:
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
AddSimplexSpecialization<DIM,D1D,Q1D>();
|
||||
}
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
static void AddSimplexSpecialization()
|
||||
{
|
||||
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
}
|
||||
protected:
|
||||
const IntegrationRule* GetDefaultIntegrationRule(
|
||||
@@ -2407,22 +2388,11 @@ public:
|
||||
const Array<real_t>&, const Vector&,
|
||||
const Vector&, Vector&, const int, const int);
|
||||
|
||||
using ApplySimplexKernelType = void(*)(const int, const Array<int>&,
|
||||
const Array<int>&,
|
||||
const Array<int>&, const Array<int>&, const Array<int>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Vector&, const Vector&, Vector&,
|
||||
const int, const int);
|
||||
|
||||
using DiagonalKernelType = void(*)(const int, const Array<real_t>&,
|
||||
const Vector&, Vector&, const int,
|
||||
const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
|
||||
int));
|
||||
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
@@ -2471,8 +2441,7 @@ public:
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans,
|
||||
const bool stroud = false);
|
||||
const ElementTransformation &Trans);
|
||||
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
@@ -2483,13 +2452,6 @@ public:
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
AddSimplexSpecialization<DIM,D1D,Q1D>();
|
||||
}
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
static void AddSimplexSpecialization()
|
||||
{
|
||||
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -2634,40 +2596,41 @@ public:
|
||||
by scalar FE through standard transformation. */
|
||||
class VectorMassIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
int vdim = -1, Q_order = 0;
|
||||
private:
|
||||
int vdim;
|
||||
Vector shape, te_shape, vec;
|
||||
DenseMatrix partelmat;
|
||||
DenseMatrix mcoeff;
|
||||
int Q_order;
|
||||
|
||||
protected:
|
||||
Coefficient *Q = nullptr;
|
||||
VectorCoefficient *VQ = nullptr;
|
||||
MatrixCoefficient *MQ = nullptr;
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
MatrixCoefficient *MQ;
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int ne, dim, dofs1D, quad1D, coeff_vdim;
|
||||
Vector pa_data;
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
|
||||
public:
|
||||
/// Construct an integrator with coefficient 1.0
|
||||
VectorMassIntegrator() = default;
|
||||
|
||||
VectorMassIntegrator()
|
||||
: vdim(-1), Q_order(0), Q(NULL), VQ(NULL), MQ(NULL) { }
|
||||
/** Construct an integrator with scalar coefficient q. If possible, save
|
||||
memory by using a scalar integrator since the resulting matrix is block
|
||||
diagonal with the same diagonal block repeated. */
|
||||
VectorMassIntegrator(Coefficient &q, int qo = 0): Q_order(qo), Q(&q) { }
|
||||
|
||||
VectorMassIntegrator(Coefficient &q, const IntegrationRule *ir):
|
||||
BilinearFormIntegrator(ir), Q(&q) { }
|
||||
|
||||
VectorMassIntegrator(Coefficient &q, int qo = 0)
|
||||
: vdim(-1), Q_order(qo), Q(&q), VQ(NULL), MQ(NULL) { }
|
||||
VectorMassIntegrator(Coefficient &q, const IntegrationRule *ir)
|
||||
: BilinearFormIntegrator(ir), vdim(-1), Q_order(0), Q(&q), VQ(NULL),
|
||||
MQ(NULL) { }
|
||||
/// Construct an integrator with diagonal coefficient q
|
||||
VectorMassIntegrator(VectorCoefficient &q, int qo = 0):
|
||||
vdim(q.GetVDim()), Q_order(qo), VQ(&q) { }
|
||||
|
||||
VectorMassIntegrator(VectorCoefficient &q, int qo = 0)
|
||||
: vdim(q.GetVDim()), Q_order(qo), Q(NULL), VQ(&q), MQ(NULL) { }
|
||||
/// Construct an integrator with matrix coefficient q
|
||||
VectorMassIntegrator(MatrixCoefficient &q, int qo = 0):
|
||||
vdim(q.GetVDim()), Q_order(qo), MQ(&q) { }
|
||||
VectorMassIntegrator(MatrixCoefficient &q, int qo = 0)
|
||||
: vdim(q.GetVDim()), Q_order(qo), Q(NULL), VQ(NULL), MQ(&q) { }
|
||||
|
||||
int GetVDim() const { return vdim; }
|
||||
void SetVDim(int vdim_) { vdim = vdim_; }
|
||||
@@ -2679,7 +2642,6 @@ public:
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &fes) override;
|
||||
void AssembleMF(const FiniteElementSpace &fes) override;
|
||||
@@ -2688,15 +2650,6 @@ public:
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddMultMF(const Vector &x, Vector &y) const override;
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
using VectorMassAddMultPAType =
|
||||
void(*)(const int, const int,
|
||||
const Array<real_t>&, const Vector&,
|
||||
const Vector&, Vector&, const int, const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(VectorMassAddMultPA,
|
||||
VectorMassAddMultPAType,
|
||||
(int, int, int));
|
||||
};
|
||||
|
||||
|
||||
@@ -2748,7 +2701,7 @@ public:
|
||||
|
||||
|
||||
/** Integrator for $(-Q u, \nabla v)$ for Nedelec ($u$) and $H^1$ ($v$) elements.
|
||||
This is equivalent to a weak divergence of the $H(curl)$ basis functions. */
|
||||
This is equivalent to a weak divergence of the $H(curl$ basis functions. */
|
||||
class VectorFEWeakDivergenceIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
@@ -3167,21 +3120,23 @@ public:
|
||||
to be the spatial dimension (i.e. 2-dimension or 3-dimension). */
|
||||
class VectorDiffusionIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
int vdim = -1;
|
||||
DenseMatrix dshape, dshapedxt, pelmat;
|
||||
DenseMatrix mcoeff;
|
||||
Vector vcoeff;
|
||||
|
||||
protected:
|
||||
Coefficient *Q = nullptr;
|
||||
VectorCoefficient *VQ = nullptr;
|
||||
MatrixCoefficient *MQ = nullptr;
|
||||
Coefficient *Q = NULL;
|
||||
VectorCoefficient *VQ = NULL;
|
||||
MatrixCoefficient *MQ = NULL;
|
||||
|
||||
// PA extension
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int ne, dim, sdim, dofs1D, quad1D, coeff_vdim;
|
||||
int dim, sdim, ne, dofs1D, quad1D;
|
||||
Vector pa_data;
|
||||
|
||||
private:
|
||||
DenseMatrix dshape, dshapedxt, pelmat;
|
||||
int vdim = -1;
|
||||
DenseMatrix mcoeff;
|
||||
Vector vcoeff;
|
||||
|
||||
public:
|
||||
VectorDiffusionIntegrator(const IntegrationRule *ir = nullptr);
|
||||
|
||||
@@ -3234,7 +3189,6 @@ public:
|
||||
void AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
const Vector &elfun, Vector &elvect) override;
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &fes) override;
|
||||
void AssembleMF(const FiniteElementSpace &fes) override;
|
||||
@@ -3244,11 +3198,13 @@ public:
|
||||
void AddMultMF(const Vector &x, Vector &y) const override;
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
/// arguments: ne, coeff_vdim, B, G, pa_data, x, y, d1d, q1d, vdim
|
||||
using ApplyKernelType = void (*)(const int, const int,
|
||||
const Array<real_t> &, const Array<real_t> &,
|
||||
const Vector &, const Vector &, Vector &,
|
||||
const int, const int, const int);
|
||||
/// arguments: ne, B, G, Bt, Gt, pa_data, x, y, d1d, q1d, vdim
|
||||
using ApplyKernelType = void (*)(const int, const Array<real_t> &,
|
||||
const Array<real_t> &,
|
||||
const Array<real_t> &,
|
||||
const Array<real_t> &, const Vector &,
|
||||
const Vector &, Vector &, const int,
|
||||
const int, const int);
|
||||
|
||||
/// arguments: dim, vdim, d1d, q1d
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int, int));
|
||||
@@ -3259,7 +3215,10 @@ public:
|
||||
ApplyPAKernels::Specialization<DIM, VDIM, D1D, Q1D>::Add();
|
||||
}
|
||||
|
||||
// struct Kernels { Kernels(); };
|
||||
struct Kernels
|
||||
{
|
||||
Kernels();
|
||||
};
|
||||
};
|
||||
|
||||
/** Integrator for the linear elasticity form:
|
||||
|
||||
+33
-76
@@ -39,8 +39,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
b_type = b_type_i;
|
||||
cp_type = cp_type_i;
|
||||
tol = tol_i;
|
||||
lbound.SetSize(ncp, nb);
|
||||
ubound.SetSize(ncp, nb);
|
||||
lbound.SetSize(nb, ncp);
|
||||
ubound.SetSize(nb, ncp);
|
||||
nodes.SetSize(nb);
|
||||
weights.SetSize(nb);
|
||||
control_points.SetSize(ncp);
|
||||
@@ -125,25 +125,21 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
}
|
||||
else if (j == ncp-1)
|
||||
{
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
vals(0) = bv(i);
|
||||
vals(1) = bmv(i) + dm*bdmv(i);
|
||||
vals(2) = bpv(i) + dp*bdpv(i);
|
||||
lbound(j,i) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(j,i) = vals.Max()+tol; // tolerance for good measure
|
||||
if (b_type == 2)
|
||||
{
|
||||
lbound(j,i) = std::max(lbound(j,i),0_r);
|
||||
}
|
||||
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,8 +207,7 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
}
|
||||
}
|
||||
|
||||
PLBound::PLBound(const FiniteElementSpace *fes, const int ncp_i,
|
||||
const int cp_type_i)
|
||||
PLBound::PLBound(FiniteElementSpace *fes, int ncp_i, int cp_type_i)
|
||||
{
|
||||
MFEM_VERIFY(!fes->IsVariableOrder(),
|
||||
"Variable order meshes not yet supported.");
|
||||
@@ -269,15 +264,15 @@ PLBound::PLBound(const FiniteElementSpace *fes, const int ncp_i,
|
||||
Setup(nb, ncp, b_type, cp_type, tol);
|
||||
}
|
||||
|
||||
void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
Vector &intmax) const
|
||||
void PLBound::Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
real_t x,w;
|
||||
intmin.SetSize(ncp);
|
||||
intmax.SetSize(ncp);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector coeffm;
|
||||
Vector coeffm(nb);
|
||||
coeffm = 0.0;
|
||||
|
||||
real_t a0 = 0.0;
|
||||
real_t a1 = 0.0;
|
||||
@@ -305,8 +300,6 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
// compute L2 projection for linear bases: a0 + a1*x
|
||||
if (proj)
|
||||
{
|
||||
coeffm.SetSize(nb);
|
||||
coeffm = 0.0;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1;
|
||||
@@ -347,14 +340,13 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t c = coeffm(i);
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
intmin(j) += min(lbound(j,i)*c, ubound(j,i)*c);
|
||||
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
|
||||
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
Vector &intmax) const
|
||||
void PLBound::Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
intmin.SetSize(ncp*ncp);
|
||||
intmax.SetSize(ncp*ncp);
|
||||
@@ -479,10 +471,10 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth row
|
||||
{
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
intmin(k*ncp+i) += vals.Min();
|
||||
intmax(k*ncp+i) += vals.Max();
|
||||
}
|
||||
@@ -490,8 +482,7 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
Vector &intmax) const
|
||||
void PLBound::Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
int nb2 = nb*nb,
|
||||
ncp2 = ncp*ncp,
|
||||
@@ -558,17 +549,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1; // x-coordinate
|
||||
minNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
minBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
maxBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
}
|
||||
// Compute Bernstein coefficients
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, minNodalVals.GetData());
|
||||
lu.Solve(nb, 1, maxNodalVals.GetData());
|
||||
lu.Solve(nb, 1, minBounds.GetData());
|
||||
lu.Solve(nb, 1, maxBounds.GetData());
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
intminT(i*ncp2+j) = minNodalVals(i);
|
||||
intmaxT(i*ncp2+j) = maxNodalVals(i);
|
||||
intminT(i*ncp2+j) = minBounds(i);
|
||||
intmaxT(i*ncp2+j) = maxBounds(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,10 +613,10 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth slice
|
||||
{
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
intmin(k*ncp2+i) += vals.Min();
|
||||
intmax(k*ncp2+i) += vals.Max();
|
||||
}
|
||||
@@ -633,7 +624,7 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::GetNDBounds(const int rdim, const Vector &coeff,
|
||||
void PLBound::GetNDBounds(int rdim, Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
if (rdim == 1)
|
||||
@@ -658,8 +649,7 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
Vector &nodesBern) const
|
||||
{
|
||||
const int nbern = nodesBern.Size();
|
||||
L2_SegmentElement el(nbern-1, 2);
|
||||
// we use L2 to leverage lexicographic order
|
||||
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
|
||||
Array<int> ordering = el.GetLexicographicOrdering();
|
||||
basisMat.SetSize(nbern, nbern);
|
||||
Vector shape(nbern);
|
||||
@@ -672,39 +662,6 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetBoundingMatrix(int dim, bool is_lower) const
|
||||
{
|
||||
if (dim > 1)
|
||||
{
|
||||
const int ncpd = static_cast<int>(std::pow(ncp, dim));
|
||||
const int nbd = static_cast<int>(std::pow(nb, dim));
|
||||
DenseMatrix boundND(ncpd, nbd);
|
||||
Vector phimin, phimax, col;
|
||||
Vector coeffs(nbd);
|
||||
coeffs = 0.0;
|
||||
for (int j = 0; j < nbd; j++)
|
||||
{
|
||||
coeffs(j) = 1.0;
|
||||
boundND.GetColumnReference(j, col);
|
||||
GetNDBounds(dim, coeffs, phimin, phimax);
|
||||
col = is_lower ? phimin : phimax;
|
||||
coeffs(j) = 0.0;
|
||||
}
|
||||
return boundND;
|
||||
}
|
||||
return is_lower ? lbound : ubound;
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetLowerBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, true);
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetUpperBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, false);
|
||||
}
|
||||
|
||||
constexpr int PLBound::min_ncp_gl_x[2][11];
|
||||
constexpr int PLBound::min_ncp_gll_x[2][11];
|
||||
constexpr int PLBound::min_ncp_pos_x[2][11];
|
||||
@@ -755,4 +712,4 @@ void PLBound::Print(std::ostream &outp) const
|
||||
ubound.Print(outp);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+28
-80
@@ -9,8 +9,8 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_BOUNDS
|
||||
#define MFEM_BOUNDS
|
||||
#ifndef MFEM_BOUND
|
||||
#define MFEM_BOUND
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fespace.hpp"
|
||||
@@ -19,18 +19,14 @@ namespace mfem
|
||||
{
|
||||
|
||||
/** @name Piecewise linear bounds of bases
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on
|
||||
the grid function in each element. The bounds for the bases are constructed
|
||||
based on the following parameters:
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
|
||||
|
||||
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
|
||||
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre
|
||||
nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
2 - Positive/Bernstein bases on uniformly distributed nodes,
|
||||
|
||||
(iii) @b ncp: number of control points used to construct the piecewise
|
||||
linear bounds
|
||||
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
|
||||
|
||||
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
|
||||
1 - Chebyshev.
|
||||
@@ -39,9 +35,7 @@ namespace mfem
|
||||
|
||||
If the user does not specify @b ncp and @b cp_type, the minimum value of
|
||||
@b ncp is used that would bound the bases for the @b cp_type. We default
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases.
|
||||
Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and
|
||||
increasing @b ncp results in tighter bounds.
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
|
||||
|
||||
Finally, only tensor-product elements are currently supported.
|
||||
|
||||
@@ -60,7 +54,7 @@ private:
|
||||
bool proj = true; // Use linear projection to compute bounds.
|
||||
real_t tol = 0.0; // offset bounds to avoid round-off errors
|
||||
Vector nodes, weights, control_points;
|
||||
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
|
||||
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
|
||||
// Some auxillary storage for computing the bounds with Bernstein
|
||||
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
|
||||
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
|
||||
@@ -86,9 +80,6 @@ private:
|
||||
{3,5,8,9,11,12,13,13,14,15,16}
|
||||
};
|
||||
|
||||
/// Helper function to extract lower or upper bounding matrix
|
||||
DenseMatrix GetBoundingMatrix(int dim, bool is_lower) const;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
@@ -98,85 +89,42 @@ public:
|
||||
}
|
||||
|
||||
// Constructor
|
||||
PLBound(const FiniteElementSpace *fes,
|
||||
const int ncp_i = -1, const int cp_type_i = 0);
|
||||
PLBound(FiniteElementSpace *fes, int ncp_i = -1, int cp_type_i = 0);
|
||||
|
||||
/// Get minimum number of control points needed to bound the given bases
|
||||
// Get minimum number of control points needed to bound the given bases
|
||||
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
int cp_type_i) const;
|
||||
|
||||
/// Print information about the bounds
|
||||
// Print information about the bounds
|
||||
void Print(std::ostream &outp = mfem::out) const;
|
||||
|
||||
/** @brief Enable (default) or disable linear projection before bounding.
|
||||
*
|
||||
* @details This projection increases the computational cost but results in
|
||||
* tighter bounds.
|
||||
*/
|
||||
// Enable (default) or disable linear projection before bounding.
|
||||
// This projection increases the computational cost but results in tighter
|
||||
// bounds.
|
||||
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
|
||||
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D/2D/3D.
|
||||
*
|
||||
* @param[in] rdim The spatial dimension of the element (1, 2, or 3).
|
||||
* @param[in] coeff The vector of lexicographically-ordered coefficients.
|
||||
* Should be of size nb^rdim, where nb is the number of
|
||||
* bases/nodes in 1D. These coefficients must correspond
|
||||
* to the bases type and number of bases, used in the
|
||||
* constructor of PLBound.
|
||||
*
|
||||
* @param[out] intmin The vector of minimum bound for all control points.
|
||||
* @param[out] intmax The vector of maximum bound for all control points.
|
||||
* Both intmin and intmax are of size ncp^rdim, where
|
||||
* ncp is the number of control points in 1D, and are
|
||||
* ordered lexicographically.
|
||||
*/
|
||||
void GetNDBounds(const int rdim, const Vector &coeff,
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D/2D/3D.
|
||||
void GetNDBounds(int rdim, Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Get number of control points used to compute the bounds.
|
||||
int GetNControlPoints() const { return ncp; }
|
||||
|
||||
/// Get 1D control point locations (lexicographic order) in [0,1].
|
||||
const Vector &GetControlPoints() const { return control_points; }
|
||||
|
||||
/** @brief Get lower and upper bounding matrix (ncp^dim x nb^dim)
|
||||
*
|
||||
* @details The matrices can be used to compute the bounds at control points
|
||||
* by a simple matrix-vector product with the
|
||||
* lexicographically-ordered nodal coefficients.
|
||||
* The resulting output is also lexicographically-ordered.
|
||||
*
|
||||
* @note These matrices do not account for the linear projection step that
|
||||
* is optionally done in GetNDBounds before bounding the function.
|
||||
*/
|
||||
///@{
|
||||
DenseMatrix GetLowerBoundMatrix(int dim = 1) const;
|
||||
DenseMatrix GetUpperBoundMatrix(int dim = 1) const;
|
||||
///@}
|
||||
|
||||
private:
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D.
|
||||
void Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 2D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 2D.
|
||||
void Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 3D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 3D.
|
||||
void Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
* for Bernstein bases.
|
||||
*/
|
||||
/// Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
/// for Bernstein bases.
|
||||
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
|
||||
|
||||
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
@@ -185,4 +133,4 @@ private:
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_BOUNDS
|
||||
#endif // MFEM_BOUND
|
||||
|
||||
+3
-149
@@ -54,8 +54,6 @@ void Coefficient::Project(QuadratureFunction &qf)
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
Vector values;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -329,8 +327,6 @@ void VectorCoefficient::Project(QuadratureFunction &qf)
|
||||
const int ne = qspace.GetNE();
|
||||
DenseMatrix values;
|
||||
Vector col;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -699,8 +695,6 @@ void MatrixCoefficient::Project(QuadratureFunction &qf, bool transpose)
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
DenseMatrix values, matrix;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -1091,29 +1085,6 @@ void SumCoefficient::SetTime(real_t t)
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void SumCoefficient::Project(QuadratureFunction &qf)
|
||||
{
|
||||
if (a == nullptr)
|
||||
{
|
||||
// qf = alpha*aConst + beta * b
|
||||
const real_t d_alpha_a = aConst*alpha;
|
||||
const real_t d_beta = beta;
|
||||
b->Project(qf);
|
||||
auto d_qf = qf.ReadWrite();
|
||||
mfem::forall(qf.Size(), [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
d_qf[i] = d_alpha_a + d_beta*d_qf[i];
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
a->Project(qf);
|
||||
QuadratureFunction qf_b(*qf.GetSpace());
|
||||
b->Project(qf_b);
|
||||
add(alpha, qf, beta, qf_b, qf);
|
||||
}
|
||||
}
|
||||
|
||||
void ProductCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
@@ -1121,23 +1092,6 @@ void ProductCoefficient::SetTime(real_t t)
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void ProductCoefficient::Project(QuadratureFunction &qf)
|
||||
{
|
||||
if (a == nullptr)
|
||||
{
|
||||
// qf = aConst * b
|
||||
b->Project(qf);
|
||||
qf *= aConst;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->Project(qf);
|
||||
QuadratureFunction qf_b(qf.GetSpace());
|
||||
b->Project(qf_b);
|
||||
qf *= qf_b;
|
||||
}
|
||||
}
|
||||
|
||||
void RatioCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
@@ -1145,38 +1099,6 @@ void RatioCoefficient::SetTime(real_t t)
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void RatioCoefficient::Project(QuadratureFunction &qf)
|
||||
{
|
||||
if (b == nullptr)
|
||||
{
|
||||
if (a == nullptr)
|
||||
{
|
||||
qf = aConst / bConst;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->Project(qf);
|
||||
qf *= 1.0/bConst;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a == nullptr)
|
||||
{
|
||||
b->Project(qf);
|
||||
qf.Reciprocal();
|
||||
qf *= aConst;
|
||||
}
|
||||
else
|
||||
{
|
||||
a->Project(qf);
|
||||
QuadratureFunction qf_b(qf.GetSpace());
|
||||
b->Project(qf_b);
|
||||
qf /= qf_b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PowerCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
@@ -1308,73 +1230,6 @@ real_t TraceCoefficient::Eval(ElementTransformation &T,
|
||||
return ma.Trace();
|
||||
}
|
||||
|
||||
VectorComponentCoefficient::VectorComponentCoefficient(VectorCoefficient &A,
|
||||
int c)
|
||||
: a(&A), va(A.GetVDim())
|
||||
{
|
||||
SetComponent(c);
|
||||
}
|
||||
|
||||
void VectorComponentCoefficient::SetComponent(int c)
|
||||
{
|
||||
MFEM_ASSERT(c < a->GetVDim() && c >= 0,
|
||||
"VectorComponentCoefficient: "
|
||||
"Index not in range.");
|
||||
|
||||
component = c;
|
||||
}
|
||||
|
||||
void VectorComponentCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t VectorComponentCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(va, T, ip);
|
||||
return va[component];
|
||||
}
|
||||
|
||||
MatrixComponentCoefficient::MatrixComponentCoefficient(MatrixCoefficient &A,
|
||||
int ri, int ci)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth())
|
||||
{
|
||||
SetRowIndex(ri);
|
||||
SetColumnIndex(ci);
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetRowIndex(int ri)
|
||||
{
|
||||
MFEM_ASSERT(ri < a->GetHeight() && ri >= 0,
|
||||
"MatrixComponentCoefficient: "
|
||||
"Row index not in range.");
|
||||
|
||||
row_idx = ri;
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetColumnIndex(int ci)
|
||||
{
|
||||
MFEM_ASSERT(ci < a->GetWidth() && ci >= 0,
|
||||
"MatrixComponentCoefficient: "
|
||||
"Column index not in range.");
|
||||
col_idx = ci;
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t MatrixComponentCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(ma, T, ip);
|
||||
return ma(row_idx,col_idx);
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(int dim)
|
||||
: VectorCoefficient(dim),
|
||||
ACoef(NULL), BCoef(NULL),
|
||||
@@ -2100,7 +1955,7 @@ void CoefficientVector::Project(MatrixCoefficient &coeff, bool transpose)
|
||||
{
|
||||
if (auto *const_coeff = dynamic_cast<MatrixConstantCoefficient*>(&coeff))
|
||||
{
|
||||
SetConstant(const_coeff->GetMatrix(), transpose);
|
||||
SetConstant(const_coeff->GetMatrix());
|
||||
}
|
||||
else if (auto *const_sym_coeff =
|
||||
dynamic_cast<SymmetricMatrixConstantCoefficient*>(&coeff))
|
||||
@@ -2161,7 +2016,7 @@ void CoefficientVector::SetConstant(const Vector &constant)
|
||||
}
|
||||
}
|
||||
|
||||
void CoefficientVector::SetConstant(const DenseMatrix &constant, bool transpose)
|
||||
void CoefficientVector::SetConstant(const DenseMatrix &constant)
|
||||
{
|
||||
const int nq = (storage & CoefficientStorage::CONSTANTS) ? 1 : qs.GetSize();
|
||||
const int width = constant.Width();
|
||||
@@ -2174,8 +2029,7 @@ void CoefficientVector::SetConstant(const DenseMatrix &constant, bool transpose)
|
||||
{
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
const real_t val = transpose ? constant(j,i) : constant(i,j);
|
||||
(*this)[i + j*height + iq*vdim] = val;
|
||||
(*this)[i + j*height + iq*vdim] = constant(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-96
@@ -52,9 +52,6 @@ public:
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/// Returns dimension of the vector.
|
||||
int GetVDim() { return 1; }
|
||||
|
||||
/** @brief Evaluate the coefficient in the element described by @a T at the
|
||||
point @a ip. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
@@ -117,10 +114,11 @@ public:
|
||||
/// Construct the constant coefficient using a vector of constants.
|
||||
/** @a c should be a vector defined by attributes, so for region with
|
||||
attribute @a i @a c[i-1] is the coefficient in that region */
|
||||
PWConstCoefficient(const Vector &c) { UpdateConstants(c); }
|
||||
PWConstCoefficient(Vector &c)
|
||||
{ constants.SetSize(c.Size()); constants=c; }
|
||||
|
||||
/// Update the constants with vector @a c.
|
||||
void UpdateConstants(const Vector &c) { constants = c; }
|
||||
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
|
||||
|
||||
/// Return a reference to the i-th constant
|
||||
real_t &operator()(int i) { return constants(i-1); }
|
||||
@@ -1334,8 +1332,8 @@ public:
|
||||
/// Get the coefficient located at (i,j) in the matrix.
|
||||
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
|
||||
|
||||
/** @brief Set the coefficient located at (i,j) in the matrix. By default
|
||||
this will take ownership of the Coefficient passed in, but this
|
||||
/** @brief Set the coefficient located at (i,j) in the matrix. By default by
|
||||
default this will take ownership of the Coefficient passed in, but this
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, int j, Coefficient * c, bool own=true);
|
||||
|
||||
@@ -1458,9 +1456,6 @@ public:
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// @copydoc Coefficient::Project(QuadratureFunction &)
|
||||
void Project(QuadratureFunction &qf) override;
|
||||
|
||||
/// Reset the first term in the linear combination as a constant
|
||||
void SetAConst(real_t A) { a = NULL; aConst = A; }
|
||||
/// Return the first term in the linear combination
|
||||
@@ -1642,9 +1637,6 @@ public:
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// @copydoc Coefficient::Project(QuadratureFunction &)
|
||||
void Project(QuadratureFunction &qf) override;
|
||||
|
||||
/// Reset the first term in the product as a constant
|
||||
void SetAConst(real_t A) { a = NULL; aConst = A; }
|
||||
/// Return the first term in the product
|
||||
@@ -1693,9 +1685,6 @@ public:
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// @copydoc Coefficient::Project(QuadratureFunction &)
|
||||
void Project(QuadratureFunction &qf) override;
|
||||
|
||||
/// Reset the numerator in the ratio as a constant
|
||||
void SetAConst(real_t A) { a = NULL; aConst = A; }
|
||||
/// Return the numerator of the ratio
|
||||
@@ -1875,85 +1864,6 @@ public:
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as component of a vector coefficient
|
||||
class VectorComponentCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
VectorCoefficient *a = nullptr;
|
||||
|
||||
mutable Vector va;
|
||||
int component;
|
||||
|
||||
public:
|
||||
/// Construct with a vector coefficient.
|
||||
VectorComponentCoefficient(VectorCoefficient &A)
|
||||
: a(&A), va(A.GetVDim()), component(0) {};
|
||||
|
||||
VectorComponentCoefficient(VectorCoefficient &A, int c);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Reset the vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
|
||||
/// Return the vector coefficient
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Set the component
|
||||
void SetComponent(int c);
|
||||
|
||||
/// Return the component
|
||||
int GetComponent() const { return component; }
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as component of a matrix coefficient
|
||||
class MatrixComponentCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
MatrixCoefficient *a = nullptr;
|
||||
|
||||
mutable DenseMatrix ma;
|
||||
int row_idx,col_idx;
|
||||
|
||||
public:
|
||||
MatrixComponentCoefficient(MatrixCoefficient &A)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth()), row_idx(0), col_idx(0) {};
|
||||
|
||||
/// Construct with the matrix coefficient.
|
||||
MatrixComponentCoefficient(MatrixCoefficient &A, int ri, int ci);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the index
|
||||
void SetRowIndex(int ri);
|
||||
|
||||
/// Return the index
|
||||
int GetRowIndex() const { return row_idx; }
|
||||
|
||||
/// Reset the index
|
||||
void SetColumnIndex(int ci);
|
||||
|
||||
/// Return the index
|
||||
int GetColumnIndex() const { return col_idx; }
|
||||
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Vector coefficient defined as the linear combination of two vectors
|
||||
class VectorSumCoefficient : public VectorCoefficient
|
||||
{
|
||||
@@ -2601,7 +2511,7 @@ public:
|
||||
void SetConstant(const Vector &constant);
|
||||
|
||||
/// Set this vector to the given constant matrix.
|
||||
void SetConstant(const DenseMatrix &constant, bool transpose=false);
|
||||
void SetConstant(const DenseMatrix &constant);
|
||||
|
||||
/// Set this vector to the given constant symmetric matrix.
|
||||
void SetConstant(const DenseSymmetricMatrix &constant);
|
||||
|
||||
+8
-343
@@ -11,15 +11,14 @@
|
||||
|
||||
#include "complex_fem.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include "../general/text.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *f)
|
||||
: Vector(2*(f->GetVSize())), fes(f), fec_owned(NULL)
|
||||
ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *fes)
|
||||
: Vector(2*(fes->GetVSize()))
|
||||
{
|
||||
UseDevice(true);
|
||||
this->Vector::operator=(0.0);
|
||||
@@ -29,88 +28,12 @@ ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *f)
|
||||
|
||||
gfi = new GridFunction();
|
||||
gfi->MakeRef(fes, *this, fes->GetVSize());
|
||||
|
||||
fes_sequence = fes->GetSequence();
|
||||
}
|
||||
|
||||
ComplexGridFunction::ComplexGridFunction(Mesh *m, std::istream &input)
|
||||
: Vector(), fes(NULL), fec_owned(NULL)
|
||||
{
|
||||
string buff;
|
||||
|
||||
// Grid functions are stored on the device
|
||||
UseDevice(true);
|
||||
|
||||
input >> std::ws;
|
||||
getline(input, buff); // 'ComplexGridFunction'
|
||||
filter_dos(buff);
|
||||
if (buff != "ComplexGridFunction")
|
||||
{
|
||||
MFEM_ABORT("unrecognized file header: " << buff);
|
||||
}
|
||||
|
||||
fes = new FiniteElementSpace;
|
||||
fec_owned = fes->Load(m, input);
|
||||
|
||||
skip_comment_lines(input, '#');
|
||||
istream::int_type next_char = input.peek();
|
||||
if (next_char == 'N') // First letter of "NURBS_patches"
|
||||
{
|
||||
getline(input, buff);
|
||||
filter_dos(buff);
|
||||
if (buff == "NURBS_patches")
|
||||
{
|
||||
MFEM_ABORT("NURBS not yet supported with ComplexGridFunction objects");
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("unknown section: " << buff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector::Load(input, 2*fes->GetVSize());
|
||||
|
||||
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
|
||||
if (fes->Nonconforming() &&
|
||||
fes->GetMesh()->ncmesh->IsLegacyLoaded())
|
||||
{
|
||||
// LegacyNCReorder();
|
||||
MFEM_ABORT("LegacyNCReorder not supported for "
|
||||
"ComplexGridFunction objects");
|
||||
}
|
||||
}
|
||||
|
||||
gfr = new GridFunction();
|
||||
gfr->MakeRef(fes, *this, 0);
|
||||
|
||||
gfi = new GridFunction();
|
||||
gfi->MakeRef(fes, *this, fes->GetVSize());
|
||||
|
||||
fes_sequence = fes->GetSequence();
|
||||
}
|
||||
|
||||
void ComplexGridFunction::Destroy()
|
||||
{
|
||||
delete gfr; delete gfi;
|
||||
|
||||
if (fec_owned)
|
||||
{
|
||||
delete fes;
|
||||
delete fec_owned;
|
||||
fec_owned = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::Update()
|
||||
{
|
||||
if (fes->GetSequence() == fes_sequence)
|
||||
{
|
||||
return; // space and grid function are in sync, no-op
|
||||
}
|
||||
fes_sequence = fes->GetSequence();
|
||||
|
||||
FiniteElementSpace *fes = gfr->FESpace();
|
||||
const int vsize = fes->GetVSize();
|
||||
|
||||
const Operator *T = fes->GetUpdateOperator();
|
||||
@@ -161,17 +84,6 @@ ComplexGridFunction::Update()
|
||||
}
|
||||
}
|
||||
|
||||
int ComplexGridFunction::VectorDim() const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return fes->GetVDim();
|
||||
}
|
||||
return fes->GetVDim()*std::max(fes->GetMesh()->SpaceDimension(),
|
||||
fe->GetRangeDim());
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
|
||||
Coefficient &imag_coeff)
|
||||
@@ -237,110 +149,6 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeLpError(const real_t p,
|
||||
Coefficient &exsolr,
|
||||
Coefficient &exsoli,
|
||||
Coefficient *weight,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
real_t error = 0.0;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Vector valsr;
|
||||
Vector valsi;
|
||||
|
||||
const GridFunction& gf_r = real();
|
||||
const GridFunction& gf_i = imag();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
if (elems != NULL && (*elems)[i] == 0) { continue; }
|
||||
fe = fes->GetFE(i);
|
||||
const IntegrationRule *ir;
|
||||
if (irs)
|
||||
{
|
||||
ir = irs[fe->GetGeomType()];
|
||||
}
|
||||
else
|
||||
{
|
||||
int intorder = 2*fe->GetOrder() + 3;
|
||||
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
|
||||
}
|
||||
real_t elem_error = 0.0;
|
||||
gf_r.GetValues(i, *ir, valsr);
|
||||
gf_i.GetValues(i, *ir, valsi);
|
||||
T = fes->GetElementTransformation(i);
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
real_t diffr = valsr(j) - exsolr.Eval(*T, ip);
|
||||
real_t diffi = valsi(j) - exsoli.Eval(*T, ip);
|
||||
real_t diff = hypot(diffr, diffi);
|
||||
if (p < infinity())
|
||||
{
|
||||
diff = pow(diff, p);
|
||||
if (weight)
|
||||
{
|
||||
diff *= weight->Eval(*T, ip);
|
||||
}
|
||||
elem_error += ip.weight * T->Weight() * diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (weight)
|
||||
{
|
||||
diff *= weight->Eval(*T, ip);
|
||||
}
|
||||
error = std::max(error, diff);
|
||||
}
|
||||
}
|
||||
if (p < infinity())
|
||||
{
|
||||
// negative quadrature weights may cause the error to be negative
|
||||
error += fabs(elem_error);
|
||||
}
|
||||
}
|
||||
|
||||
if (p < infinity())
|
||||
{
|
||||
error = pow(error, 1./p);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void ComplexGridFunction::Save(std::ostream &os) const
|
||||
{
|
||||
os << "ComplexGridFunction\n";
|
||||
fes->Save(os);
|
||||
os << '\n';
|
||||
if (fes->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
Vector::Print(os, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector::Print(os, fes->GetVDim());
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
|
||||
void ComplexGridFunction::Save(const char *fname, int precision) const
|
||||
{
|
||||
ofstream ofs(fname);
|
||||
ofs.precision(precision);
|
||||
Save(ofs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const ComplexGridFunction &sol)
|
||||
{
|
||||
sol.Save(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *fes,
|
||||
ComplexOperator::Convention convention)
|
||||
@@ -846,8 +654,8 @@ SesquilinearForm::Update(FiniteElementSpace *nfes)
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pf)
|
||||
: Vector(2*(pf->GetVSize())), pfes(pf), fec_owned(NULL)
|
||||
ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pfes)
|
||||
: Vector(2*(pfes->GetVSize()))
|
||||
{
|
||||
UseDevice(true);
|
||||
this->Vector::operator=(0.0);
|
||||
@@ -857,99 +665,12 @@ ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pf)
|
||||
|
||||
pgfi = new ParGridFunction();
|
||||
pgfi->MakeRef(pfes, *this, pfes->GetVSize());
|
||||
|
||||
fes_sequence = pfes->GetSequence();
|
||||
}
|
||||
|
||||
ParComplexGridFunction::ParComplexGridFunction(ParMesh *m, std::istream &input)
|
||||
: Vector(), pfes(NULL), fec_owned(NULL)
|
||||
{
|
||||
string buff;
|
||||
|
||||
// Grid functions are stored on the device
|
||||
UseDevice(true);
|
||||
|
||||
input >> std::ws;
|
||||
getline(input, buff); // 'ParComplexGridFunction'
|
||||
filter_dos(buff);
|
||||
if (buff != "ParComplexGridFunction")
|
||||
{
|
||||
MFEM_ABORT("unrecognized file header: " << buff);
|
||||
}
|
||||
|
||||
FiniteElementSpace *fes = new FiniteElementSpace;
|
||||
fec_owned = fes->Load(m, input);
|
||||
|
||||
pfes = new ParFiniteElementSpace(m, fec_owned, fes->GetVDim(),
|
||||
fes->GetOrdering());
|
||||
|
||||
delete fes;
|
||||
|
||||
skip_comment_lines(input, '#');
|
||||
istream::int_type next_char = input.peek();
|
||||
if (next_char == 'N') // First letter of "NURBS_patches"
|
||||
{
|
||||
getline(input, buff);
|
||||
filter_dos(buff);
|
||||
if (buff == "NURBS_patches")
|
||||
{
|
||||
MFEM_ABORT("NURBS not yet supported with ComplexGridFunction objects");
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("unknown section: " << buff);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int vsize = pfes->GetVSize();
|
||||
Vector::Load(input, 2*vsize);
|
||||
|
||||
real_t *h_data = HostReadWrite();
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
|
||||
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
|
||||
if (pfes->Nonconforming() &&
|
||||
pfes->GetMesh()->ncmesh->IsLegacyLoaded())
|
||||
{
|
||||
// LegacyNCReorder();
|
||||
MFEM_ABORT("LegacyNCReorder not supported for "
|
||||
"ComplexGridFunction objects");
|
||||
}
|
||||
}
|
||||
|
||||
pgfr = new ParGridFunction();
|
||||
pgfr->MakeRef(pfes, *this, 0);
|
||||
|
||||
pgfi = new ParGridFunction();
|
||||
pgfi->MakeRef(pfes, *this, pfes->GetVSize());
|
||||
|
||||
fes_sequence = pfes->GetSequence();
|
||||
}
|
||||
|
||||
void ParComplexGridFunction::Destroy()
|
||||
{
|
||||
delete pgfr; delete pgfi;
|
||||
|
||||
if (fec_owned)
|
||||
{
|
||||
delete pfes;
|
||||
delete fec_owned;
|
||||
fec_owned = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::Update()
|
||||
{
|
||||
if (pfes->GetSequence() == fes_sequence)
|
||||
{
|
||||
return; // space and grid function are in sync, no-op
|
||||
}
|
||||
fes_sequence = pfes->GetSequence();
|
||||
|
||||
ParFiniteElementSpace *pfes = pgfr->ParFESpace();
|
||||
const int vsize = pfes->GetVSize();
|
||||
|
||||
const Operator *T = pfes->GetUpdateOperator();
|
||||
@@ -998,17 +719,6 @@ ParComplexGridFunction::Update()
|
||||
}
|
||||
}
|
||||
|
||||
int ParComplexGridFunction::VectorDim() const
|
||||
{
|
||||
const FiniteElement *fe = pfes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return pfes->GetVDim();
|
||||
}
|
||||
return pfes->GetVDim()*std::max(pfes->GetMesh()->SpaceDimension(),
|
||||
fe->GetRangeDim());
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
|
||||
Coefficient &imag_coeff)
|
||||
@@ -1079,6 +789,7 @@ ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
void
|
||||
ParComplexGridFunction::Distribute(const Vector *tv)
|
||||
{
|
||||
ParFiniteElementSpace *pfes = pgfr->ParFESpace();
|
||||
const int tvsize = pfes->GetTrueVSize();
|
||||
|
||||
tv->Read();
|
||||
@@ -1096,6 +807,7 @@ ParComplexGridFunction::Distribute(const Vector *tv)
|
||||
void
|
||||
ParComplexGridFunction::ParallelProject(Vector &tv) const
|
||||
{
|
||||
ParFiniteElementSpace *pfes = pgfr->ParFESpace();
|
||||
const int tvsize = pfes->GetTrueVSize();
|
||||
|
||||
tv.Write();
|
||||
@@ -1113,53 +825,6 @@ ParComplexGridFunction::ParallelProject(Vector &tv) const
|
||||
tvi.SyncAliasMemory(tv);
|
||||
}
|
||||
|
||||
void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
{
|
||||
os << "ParComplexGridFunction\n";
|
||||
pfes->Save(os);
|
||||
os << '\n';
|
||||
|
||||
int vsize = pfes->GetVSize();
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t *h_data = const_cast<real_t*>(HostRead());
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
if (pfes->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
Vector::Print(os, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector::Print(os, pfes->GetVDim());
|
||||
}
|
||||
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
os.flush();
|
||||
}
|
||||
|
||||
void ParComplexGridFunction::Save(const char *fname, int precision) const
|
||||
{
|
||||
int rank = pfes->GetMyRank();
|
||||
ostringstream fname_with_suffix;
|
||||
fname_with_suffix << fname << "." << setfill('0') << setw(6) << rank;
|
||||
ofstream ofs(fname_with_suffix.str().c_str());
|
||||
ofs.precision(precision);
|
||||
Save(ofs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const ParComplexGridFunction &sol)
|
||||
{
|
||||
sol.Save(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
|
||||
ComplexOperator::Convention
|
||||
|
||||
+16
-247
@@ -35,72 +35,15 @@ private:
|
||||
GridFunction * gfi;
|
||||
|
||||
protected:
|
||||
/// FE space on which the grid function lives. Owned if #fec_owned
|
||||
/// is not NULL.
|
||||
FiniteElementSpace *fes;
|
||||
|
||||
/** @brief Used when the grid function is read from a file. It can also be
|
||||
set explicitly, see MakeOwner().
|
||||
|
||||
If not NULL, this pointer is owned by the ComplexGridFunction. */
|
||||
FiniteElementCollection *fec_owned;
|
||||
|
||||
long fes_sequence; // see FiniteElementSpace::sequence, Mesh::sequence
|
||||
|
||||
void Destroy();
|
||||
void Destroy() { delete gfr; delete gfi; }
|
||||
|
||||
public:
|
||||
/** @brief Construct a ComplexGridFunction associated with the
|
||||
FiniteElementSpace @a *f. */
|
||||
ComplexGridFunction(FiniteElementSpace *f);
|
||||
|
||||
/** @brief Construct a ComplexGridFunction on the given Mesh, using the data
|
||||
from @a input.
|
||||
|
||||
The content of @a input should be in the format created by the method
|
||||
Save(). The reconstructed FiniteElementSpace and FiniteElementCollection
|
||||
are owned by the ComplexGridFunction. */
|
||||
ComplexGridFunction(Mesh *m, std::istream &input);
|
||||
|
||||
void Update();
|
||||
|
||||
/** Return update counter, similar to Mesh::GetSequence(). Used to
|
||||
check if it is up to date with the space. */
|
||||
long GetSequence() const { return fes_sequence; }
|
||||
|
||||
/// Make the ComplexGridFunction the owner of #fec_owned and #fes.
|
||||
/** If the new FiniteElementCollection, @a fec_, is NULL, ownership
|
||||
of #fec_owned and #fes is taken away. */
|
||||
void MakeOwner(FiniteElementCollection *fec_) { fec_owned = fec_; }
|
||||
|
||||
/// Returns a pointer to the FiniteElementCollection used to
|
||||
/// construct this ComplexGridFunction if this class owns that
|
||||
/// object. Otherwise this function will return NULL.
|
||||
FiniteElementCollection *OwnFEC() { return fec_owned; }
|
||||
|
||||
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the
|
||||
/// underlying #fes
|
||||
int VectorDim() const;
|
||||
|
||||
/// Copy assignment. Only the data of the base class Vector is copied.
|
||||
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
|
||||
have the same size.
|
||||
|
||||
@note Defining this method overwrites the implicitly defined copy
|
||||
assignment operator. */
|
||||
ComplexGridFunction &operator=(const ComplexGridFunction &rhs)
|
||||
{ return operator=((const Vector &)rhs); }
|
||||
|
||||
/// Copy the data from @a v.
|
||||
/** The size of @a v must be equal to double of the size of the associated
|
||||
FiniteElementSpace #fes. */
|
||||
ComplexGridFunction &operator=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(fes && v.Size() == 2*fes->GetVSize(), "");
|
||||
Vector::operator=(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assign constant values to the ComplexGridFunction data.
|
||||
ComplexGridFunction &operator=(const std::complex<real_t> & value)
|
||||
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
|
||||
@@ -120,8 +63,8 @@ public:
|
||||
VectorCoefficient &imag_coeff,
|
||||
Array<int> &attr);
|
||||
|
||||
FiniteElementSpace *FESpace() { return fes; }
|
||||
const FiniteElementSpace *FESpace() const { return fes; }
|
||||
FiniteElementSpace *FESpace() { return gfr->FESpace(); }
|
||||
const FiniteElementSpace *FESpace() const { return gfr->FESpace(); }
|
||||
|
||||
GridFunction & real() { return *gfr; }
|
||||
GridFunction & imag() { return *gfi; }
|
||||
@@ -136,121 +79,11 @@ public:
|
||||
/// @a gfr and @a gfi to match the ComplexGridFunction.
|
||||
void SyncAlias() { gfr->SyncAliasMemory(*this); gfi->SyncAliasMemory(*this); }
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_L2 for complex-valued scalar fields
|
||||
///
|
||||
/// @see GridFunction::ComputeL2Error(Coefficient &exsol,
|
||||
/// const IntegrationRule *irs[],
|
||||
/// const Array<int> *elems) const
|
||||
/// for more detailed documentation.
|
||||
virtual real_t ComputeL2Error(Coefficient &exsolr, Coefficient &exsoli,
|
||||
const IntegrationRule *irs[] = NULL) const
|
||||
{
|
||||
real_t err_r = gfr->ComputeL2Error(exsolr, irs);
|
||||
real_t err_i = gfi->ComputeL2Error(exsoli, irs);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_L2 for complex-valued vector fields
|
||||
///
|
||||
/// @see GridFunction::ComputeL2Error(VectorCoefficient &exsol,
|
||||
/// const IntegrationRule *irs[],
|
||||
/// const Array<int> *elems) const
|
||||
/// for more detailed documentation.
|
||||
virtual real_t ComputeL2Error(VectorCoefficient &exsolr,
|
||||
VectorCoefficient &exsoli,
|
||||
const IntegrationRule *irs[] = NULL,
|
||||
Array<int> *elems = NULL) const
|
||||
{
|
||||
real_t err_r = gfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(exsoli, irs, elems);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
/// @brief Returns Max|u_ex - u_h| error for complex-valued H1 or L2 elements
|
||||
///
|
||||
/// Compute the $L_\infty$ error across the entire domain.
|
||||
///
|
||||
/// @param[in] exsolr Coefficient object reproducing the real part of the
|
||||
/// anticipated values of the scalar field, Re(u_ex).
|
||||
/// @param[in] exsoli Coefficient object reproducing the imaginary part of
|
||||
/// the anticipated values of the scalar field, Im(u_ex).
|
||||
/// @param[in] irs Optional pointer to an array of custom integration
|
||||
/// rules e.g. higher order than the default rules. If
|
||||
/// present the array will be indexed by
|
||||
/// Geometry::Type.
|
||||
///
|
||||
/// @note Uses ComputeLpError internally. See the ComputeLpError
|
||||
/// documentation for generalizations of this error computation.
|
||||
///
|
||||
/// @note If an array of integration rules is provided through @a irs, be
|
||||
/// sure to include valid rules for each element type that may occur
|
||||
/// in the list of elements.
|
||||
///
|
||||
virtual real_t ComputeMaxError(Coefficient &exsolr,
|
||||
Coefficient &exsoli,
|
||||
const IntegrationRule *irs[] = NULL) const
|
||||
{
|
||||
return ComputeLpError(infinity(), exsolr, exsoli, NULL, irs);
|
||||
}
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_Lp for complex-valued H1 or L2 elements
|
||||
///
|
||||
/// Computes:
|
||||
/// $$(\sum_{elems} \int_{elem} w \, |u_{ex} - u_h|^p)^{1/p}$$
|
||||
/// Where:
|
||||
/// $$|u_{ex} - u_h| = \sqrt{Re(u_{ex} - u_h)^2 + Im(u_{ex} - u_h)^2}$$
|
||||
///
|
||||
/// @param[in] p Real value indicating the exponent of the $L^p$ norm.
|
||||
/// To avoid domain errors p should have a positive value,
|
||||
/// either finite or infinite.
|
||||
/// @param[in] exsolr Coefficient object reproducing the real part of the
|
||||
/// anticipated values of the scalar field, Re(u_ex).
|
||||
/// @param[in] exsoli Coefficient object reproducing the imaginary part of
|
||||
/// the anticipated values of the scalar field, Im(u_ex).
|
||||
/// @param[in] weight Optional pointer to a Coefficient object reproducing
|
||||
/// a weighting function, w.
|
||||
/// @param[in] irs Optional pointer to an array of custom integration
|
||||
/// rules e.g. higher order than the default rules. If
|
||||
/// present the array will be indexed by Geometry::Type.
|
||||
/// @param[in] elems Optional pointer to a marker array, with a length
|
||||
/// equal to the number of local elements, indicating
|
||||
/// which elements to integrate over. Only those elements
|
||||
/// corresponding to non-zero entries in @a elems will
|
||||
/// contribute to the computed L2 error.
|
||||
///
|
||||
/// @note If an array of integration rules is provided through @a irs, be
|
||||
/// sure to include valid rules for each element type that may occur
|
||||
/// in the list of elements.
|
||||
///
|
||||
/// @note Quadratures with negative weights (as in some simplex integration
|
||||
/// rules in MFEM) can produce negative integrals even with
|
||||
/// non-negative integrands. To avoid returning negative errors this
|
||||
/// function uses the absolute values of the element-wise integrals.
|
||||
/// This may lead to results which are not entirely consistent with
|
||||
/// such integration rules.
|
||||
virtual real_t ComputeLpError(const real_t p,
|
||||
Coefficient &exsolr,
|
||||
Coefficient &exsoli,
|
||||
Coefficient *weight = NULL,
|
||||
const IntegrationRule *irs[] = NULL,
|
||||
const Array<int> *elems = NULL) const;
|
||||
|
||||
/// Save the ComplexGridFunction to an output stream.
|
||||
virtual void Save(std::ostream &out) const;
|
||||
|
||||
/// Save the ComplexGridFunction to a file
|
||||
/** The given @a precision will be used for ASCII output. */
|
||||
virtual void Save(const char *fname, int precision=16) const;
|
||||
|
||||
/// Destroys the grid function.
|
||||
virtual ~ComplexGridFunction() { Destroy(); }
|
||||
|
||||
};
|
||||
|
||||
/** Overload operator<< for std::ostream and ComplexGridFunction; not valid
|
||||
for the class ParComplexGridFunction */
|
||||
std::ostream &operator<<(std::ostream &out, const ComplexGridFunction &sol);
|
||||
|
||||
/** Class for a complex-valued linear form
|
||||
|
||||
The @a convention argument in the class's constructor is documented in the
|
||||
@@ -512,23 +345,12 @@ public:
|
||||
class ParComplexGridFunction : public Vector
|
||||
{
|
||||
private:
|
||||
|
||||
ParGridFunction * pgfr;
|
||||
ParGridFunction * pgfi;
|
||||
|
||||
protected:
|
||||
/// FE space on which the grid function lives. Owned if #fec_owned
|
||||
/// is not NULL.
|
||||
ParFiniteElementSpace *pfes;
|
||||
|
||||
/** @brief Used when the grid function is read from a file. It can also be
|
||||
set explicitly, see MakeOwner().
|
||||
|
||||
If not NULL, this pointer is owned by the ParComplexGridFunction. */
|
||||
FiniteElementCollection *fec_owned;
|
||||
|
||||
long fes_sequence; // see FiniteElementSpace::sequence, Mesh::sequence
|
||||
|
||||
void Destroy();
|
||||
void Destroy() { delete pgfr; delete pgfi; }
|
||||
|
||||
public:
|
||||
|
||||
@@ -536,33 +358,8 @@ public:
|
||||
ParFiniteElementSpace @a *pf. */
|
||||
ParComplexGridFunction(ParFiniteElementSpace *pf);
|
||||
|
||||
/** @brief Construct a ParComplexGridFunction on a given ParMesh,
|
||||
@a pmesh, reading from an std::istream.
|
||||
|
||||
In the process, a ParFiniteElementSpace and a FiniteElementCollection are
|
||||
constructed. The new ParComplexGridFunction assumes ownership of both. */
|
||||
ParComplexGridFunction(ParMesh *pmesh, std::istream &input);
|
||||
|
||||
void Update();
|
||||
|
||||
/** Return update counter, similar to Mesh::GetSequence(). Used to
|
||||
check if it is up to date with the space. */
|
||||
long GetSequence() const { return fes_sequence; }
|
||||
|
||||
/// Make the ParComplexGridFunction the owner of #fec_owned and #pfes.
|
||||
/** If the new FiniteElementCollection, @a fec_, is NULL, ownership
|
||||
of #fec_owned and #pfes is taken away. */
|
||||
void MakeOwner(FiniteElementCollection *fec_) { fec_owned = fec_; }
|
||||
|
||||
/// Returns a pointer to the FiniteElementCollection used to
|
||||
/// construct this ParComplexGridFunction if this class owns that
|
||||
/// object. Otherwise this function will return NULL.
|
||||
FiniteElementCollection *OwnFEC() { return fec_owned; }
|
||||
|
||||
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the
|
||||
/// underlying #pfes
|
||||
int VectorDim() const;
|
||||
|
||||
/// Assign constant values to the ParComplexGridFunction data.
|
||||
ParComplexGridFunction &operator=(const std::complex<real_t> & value)
|
||||
{ *pgfr = value.real(); *pgfi = value.imag(); return *this; }
|
||||
@@ -588,11 +385,11 @@ public:
|
||||
/// Returns the vector restricted to the true dofs.
|
||||
void ParallelProject(Vector &tv) const;
|
||||
|
||||
FiniteElementSpace *FESpace() { return pfes; }
|
||||
const FiniteElementSpace *FESpace() const { return pfes; }
|
||||
FiniteElementSpace *FESpace() { return pgfr->FESpace(); }
|
||||
const FiniteElementSpace *FESpace() const { return pgfr->FESpace(); }
|
||||
|
||||
ParFiniteElementSpace *ParFESpace() { return pfes; }
|
||||
const ParFiniteElementSpace *ParFESpace() const { return pfes; }
|
||||
ParFiniteElementSpace *ParFESpace() { return pgfr->ParFESpace(); }
|
||||
const ParFiniteElementSpace *ParFESpace() const { return pgfr->ParFESpace(); }
|
||||
|
||||
ParGridFunction & real() { return *pgfr; }
|
||||
ParGridFunction & imag() { return *pgfi; }
|
||||
@@ -605,32 +402,17 @@ public:
|
||||
|
||||
/// Update the alias memory location of the real and imaginary
|
||||
/// ParGridFunction @a pgfr and @a pgfi to match the ParComplexGridFunction.
|
||||
void SyncAlias()
|
||||
{ pgfr->SyncAliasMemory(*this); pgfi->SyncAliasMemory(*this); }
|
||||
void SyncAlias() { pgfr->SyncAliasMemory(*this); pgfi->SyncAliasMemory(*this); }
|
||||
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_L2 in parallel for complex-valued
|
||||
/// scalar fields
|
||||
///
|
||||
/// @see GridFunction::ComputeL2Error(Coefficient &exsol,
|
||||
/// const IntegrationRule *irs[],
|
||||
/// const Array<int> *elems) const
|
||||
/// for more detailed documentation.
|
||||
virtual real_t ComputeL2Error(Coefficient &exsolr, Coefficient &exsoli,
|
||||
const IntegrationRule *irs[] = NULL,
|
||||
Array<int> *elems = NULL) const
|
||||
const IntegrationRule *irs[] = NULL) const
|
||||
{
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = pgfi->ComputeL2Error(exsoli, irs, elems);
|
||||
return hypot(err_r, err_i);
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs);
|
||||
real_t err_i = pgfi->ComputeL2Error(exsoli, irs);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_L2 in parallel for complex-valued
|
||||
/// vector fields
|
||||
///
|
||||
/// @see GridFunction::ComputeL2Error(VectorCoefficient &exsol,
|
||||
/// const IntegrationRule *irs[],
|
||||
/// const Array<int> *elems) const
|
||||
/// for more detailed documentation.
|
||||
virtual real_t ComputeL2Error(VectorCoefficient &exsolr,
|
||||
VectorCoefficient &exsoli,
|
||||
const IntegrationRule *irs[] = NULL,
|
||||
@@ -638,28 +420,15 @@ public:
|
||||
{
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = pgfi->ComputeL2Error(exsoli, irs, elems);
|
||||
return hypot(err_r, err_i);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
/// Save the local portion of the ParComplexGridFunction
|
||||
/** This differs from the serial ComplexGridFunction::Save in that it
|
||||
takes into account the signs of the local dofs. */
|
||||
void Save(std::ostream &out) const;
|
||||
|
||||
/// Save the ParComplexGridFunction to files
|
||||
/** Saves one file for each MPI rank. The files will be given suffixes
|
||||
according to the MPI rank. The given @a precision will be used for ASCII
|
||||
output. */
|
||||
void Save(const char *fname, int precision=16) const;
|
||||
|
||||
/// Destroys grid function.
|
||||
virtual ~ParComplexGridFunction() { Destroy(); }
|
||||
|
||||
};
|
||||
|
||||
/** Overload operator<< for std::ostream and ParComplexGridFunction */
|
||||
std::ostream &operator<<(std::ostream &out, const ParComplexGridFunction &sol);
|
||||
|
||||
/** Class for a complex-valued, parallel linear form
|
||||
|
||||
The @a convention argument in the class's constructor is documented in the
|
||||
|
||||
+47
-273
@@ -70,8 +70,8 @@ ConduitDataCollection::~ConduitDataCollection()
|
||||
void ConduitDataCollection::Save()
|
||||
{
|
||||
std::string dir_name = MeshDirectoryName();
|
||||
int err_ = create_directory(dir_name, mesh, myid);
|
||||
if (err_)
|
||||
int err = create_directory(dir_name, mesh, myid);
|
||||
if (err)
|
||||
{
|
||||
MFEM_ABORT("Error creating directory: " << dir_name);
|
||||
}
|
||||
@@ -88,7 +88,6 @@ void ConduitDataCollection::Save()
|
||||
<< verify_info.to_json());
|
||||
}
|
||||
|
||||
// wrap all grid functions
|
||||
FieldMapConstIterator itr;
|
||||
for ( itr = field_map.begin(); itr != field_map.end(); itr++)
|
||||
{
|
||||
@@ -104,20 +103,6 @@ void ConduitDataCollection::Save()
|
||||
}
|
||||
}
|
||||
|
||||
// wrap all quadrature functions
|
||||
QFieldMapConstIterator qf_itr;
|
||||
for ( qf_itr = q_field_map.begin(); qf_itr != q_field_map.end(); qf_itr++)
|
||||
{
|
||||
std::string name = qf_itr->first;
|
||||
QuadratureFunction *qf = qf_itr->second;
|
||||
QuadratureFunctionToBlueprintField(qf,
|
||||
n_mesh["fields"][name]);
|
||||
}
|
||||
|
||||
// TODO: in parallel, we need to call ParFiniteElementSpace::ApplyDofSigns
|
||||
// for all ParGridFunction objects before and after saving, see
|
||||
// ParGridFunction::Save.
|
||||
|
||||
// save mesh data
|
||||
SaveMeshAndFields(myid,
|
||||
n_mesh,
|
||||
@@ -172,16 +157,6 @@ ConduitDataCollection::SetProtocol(const std::string &protocol)
|
||||
relay_protocol = protocol;
|
||||
}
|
||||
|
||||
// Conduit data type id for the MFEM precision
|
||||
constexpr conduit::index_t mfem_precision_conduit_id =
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
CONDUIT_NATIVE_DOUBLE_ID;
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
CONDUIT_NATIVE_FLOAT_ID;
|
||||
#else
|
||||
#error Unknown MFEM precision
|
||||
#endif
|
||||
|
||||
//------------------------------
|
||||
// begin static public methods
|
||||
//------------------------------
|
||||
@@ -231,41 +206,42 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
|
||||
// get the number of points
|
||||
int num_verts = n_coordset_vals[0].dtype().number_of_elements();
|
||||
// get vals for points
|
||||
const real_t *verts_ptr = NULL;
|
||||
const double *verts_ptr = NULL;
|
||||
|
||||
// the mfem mesh constructor needs coords with interleaved (aos) type
|
||||
// ordering, even for 1d + 2d we always need 3 real_t (double/float) b/c it
|
||||
// uses Array<Vertex> and Vertex is a pod of 3 real_t. we check for this
|
||||
// ordering, even for 1d + 2d we always need 3 doubles b/c it uses
|
||||
// Array<Vertex> and Vertex is a pod of 3 doubles. we check for this
|
||||
// case, if we don't have it we convert the data
|
||||
|
||||
if (ndims == 3 &&
|
||||
n_coordset_vals[0].dtype().id() == mfem_precision_conduit_id &&
|
||||
n_coordset_vals[0].dtype().is_double() &&
|
||||
blueprint::mcarray::is_interleaved(n_coordset_vals) )
|
||||
{
|
||||
// already interleaved mcarray of 3 real_t (double/float),
|
||||
// already interleaved mcarray of 3 doubles,
|
||||
// return ptr to beginning
|
||||
verts_ptr = n_coordset_vals[0].value();
|
||||
}
|
||||
else
|
||||
{
|
||||
Node n_tmp;
|
||||
// check all vals, if we don't have real_t (double/float) convert
|
||||
// to real_t
|
||||
// check all vals, if we don't have doubles convert
|
||||
// to doubles
|
||||
NodeConstIterator itr = n_coordset_vals.children();
|
||||
while (itr.has_next())
|
||||
{
|
||||
const Node &c_vals = itr.next();
|
||||
std::string c_name = itr.name();
|
||||
|
||||
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
|
||||
if ( c_vals.dtype().is_double() )
|
||||
{
|
||||
// zero copy current coords
|
||||
n_tmp[c_name].set_external(c_vals);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert
|
||||
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
|
||||
c_vals.to_double_array(n_tmp[c_name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,13 +250,13 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
|
||||
if (ndims < 3)
|
||||
{
|
||||
// add dummy z
|
||||
n_tmp["z"].set(DataType(mfem_precision_conduit_id, num_verts));
|
||||
n_tmp["z"].set(DataType::c_double(num_verts));
|
||||
}
|
||||
|
||||
if (ndims < 2)
|
||||
{
|
||||
// add dummy y
|
||||
n_tmp["y"].set(DataType(mfem_precision_conduit_id, num_verts));
|
||||
n_tmp["y"].set(DataType::c_double(num_verts));
|
||||
}
|
||||
|
||||
Node &n_conv_coords_vals = n_conv["coordsets"][coords_name]["values"];
|
||||
@@ -476,7 +452,7 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
|
||||
// if nodes gf is attached later, it resets the space dim based
|
||||
// on the gf's fes.
|
||||
Mesh *mesh = new Mesh(// from coordset
|
||||
const_cast<real_t*>(verts_ptr),
|
||||
const_cast<double*>(verts_ptr),
|
||||
num_verts,
|
||||
// from topology
|
||||
const_cast<int*>(elem_indices),
|
||||
@@ -543,7 +519,7 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
// can't return a gf that zero copies the conduit data
|
||||
Node n_conv;
|
||||
|
||||
const real_t *vals_ptr = NULL;
|
||||
const double *vals_ptr = NULL;
|
||||
|
||||
int vdim = 1;
|
||||
|
||||
@@ -553,10 +529,10 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
{
|
||||
vdim = n_field["values"].number_of_children();
|
||||
|
||||
// need to check that we have real_t (double/float) and
|
||||
// need to check that we have doubles and
|
||||
// cover supported layouts
|
||||
|
||||
if ( n_field["values"][0].dtype().id() == mfem_precision_conduit_id )
|
||||
if ( n_field["values"][0].dtype().is_double() )
|
||||
{
|
||||
// check for contig
|
||||
if (n_field["values"].is_contiguous())
|
||||
@@ -580,26 +556,27 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
vals_ptr = n_conv["values"].child(0).value();
|
||||
}
|
||||
}
|
||||
else // convert to real_t (double/float) and use contig
|
||||
else // convert to doubles and use contig
|
||||
{
|
||||
Node n_tmp;
|
||||
// check all vals, if we don't have real_t (double/float) convert
|
||||
// to real_t
|
||||
// check all vals, if we don't have doubles convert
|
||||
// to doubles
|
||||
NodeConstIterator itr = n_field["values"].children();
|
||||
while (itr.has_next())
|
||||
{
|
||||
const Node &c_vals = itr.next();
|
||||
std::string c_name = itr.name();
|
||||
|
||||
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
|
||||
if ( c_vals.dtype().is_double() )
|
||||
{
|
||||
// zero copy current coords
|
||||
n_tmp[c_name].set_external(c_vals);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert
|
||||
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
|
||||
c_vals.to_double_array(n_tmp[c_name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,15 +589,14 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n_field["values"].dtype().id() == mfem_precision_conduit_id &&
|
||||
if (n_field["values"].dtype().is_double() &&
|
||||
n_field["values"].is_compact())
|
||||
{
|
||||
vals_ptr = n_field["values"].value();
|
||||
}
|
||||
else
|
||||
{
|
||||
n_field["values"].to_data_type(mfem_precision_conduit_id,
|
||||
n_conv["values"]);
|
||||
n_field["values"].to_double_array(n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].value();
|
||||
}
|
||||
}
|
||||
@@ -644,14 +620,14 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
|
||||
if (zero_copy)
|
||||
{
|
||||
res = new GridFunction(fes,const_cast<real_t*>(vals_ptr));
|
||||
res = new GridFunction(fes,const_cast<double*>(vals_ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy case, this constructor will alloc the space for the GF data
|
||||
res = new GridFunction(fes);
|
||||
// create an mfem vector that wraps the conduit data
|
||||
Vector vals_vec(const_cast<real_t*>(vals_ptr),fes->GetVSize());
|
||||
Vector vals_vec(const_cast<double*>(vals_ptr),fes->GetVSize());
|
||||
// copy values into the result
|
||||
(*res) = vals_vec;
|
||||
}
|
||||
@@ -663,155 +639,6 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
mfem::QuadratureFunction *
|
||||
ConduitDataCollection::BlueprintFieldToQuadratureFunction(Mesh *mesh,
|
||||
const Node &n_field,
|
||||
bool zero_copy)
|
||||
{
|
||||
// n_conv holds converted data (when necessary for mfem api)
|
||||
// if n_conv is used ( !n_conv.dtype().empty() ) we
|
||||
// know that some data allocation was necessary, so we
|
||||
// can't return a qf that zero copies the conduit data
|
||||
Node n_conv;
|
||||
|
||||
const real_t *vals_ptr = NULL;
|
||||
int vdim = 1;
|
||||
|
||||
if (n_field["values"].dtype().is_object())
|
||||
{
|
||||
vdim = n_field["values"].number_of_children();
|
||||
|
||||
// need to check that we have real_t (double/float) and
|
||||
// cover supported layouts
|
||||
if ( n_field["values"][0].dtype().id() == mfem_precision_conduit_id )
|
||||
{
|
||||
// quad funcs use what mfem calls byVDIM
|
||||
// and what conduit calls interleaved
|
||||
// check for interleaved
|
||||
if (blueprint::mcarray::is_interleaved(n_field["values"]))
|
||||
{
|
||||
// conduit mcarray interleaved == mfem byVDIM
|
||||
vals_ptr = n_field["values"].child(0).value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// for mcarray generic case -- default to byVDIM
|
||||
// aka interleaved
|
||||
blueprint::mcarray::to_interleaved(n_field["values"],
|
||||
n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].child(0).value();
|
||||
}
|
||||
}
|
||||
else // convert to real_t (double/float) and use interleaved
|
||||
{
|
||||
Node n_tmp;
|
||||
// check all vals, if we don't have real_t (double/float) convert
|
||||
// to real_t
|
||||
NodeConstIterator itr = n_field["values"].children();
|
||||
while (itr.has_next())
|
||||
{
|
||||
const Node &c_vals = itr.next();
|
||||
std::string c_name = itr.name();
|
||||
|
||||
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
|
||||
{
|
||||
// zero copy current coords
|
||||
n_tmp[c_name].set_external(c_vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert
|
||||
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
|
||||
}
|
||||
}
|
||||
|
||||
// for mcarray generic case -- default to byVDIM
|
||||
// aka interleaved
|
||||
blueprint::mcarray::to_interleaved(n_tmp,
|
||||
n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].child(0).value();
|
||||
}
|
||||
}
|
||||
else // scalar case
|
||||
{
|
||||
if (n_field["values"].dtype().id() == mfem_precision_conduit_id &&
|
||||
n_field["values"].is_compact())
|
||||
{
|
||||
vals_ptr = n_field["values"].value();
|
||||
}
|
||||
else
|
||||
{
|
||||
n_field["values"].to_data_type(mfem_precision_conduit_id,
|
||||
n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].value();
|
||||
}
|
||||
}
|
||||
|
||||
if (zero_copy && !n_conv.dtype().is_empty())
|
||||
{
|
||||
//Info: "Cannot zero-copy since data conversions were necessary"
|
||||
zero_copy = false;
|
||||
}
|
||||
|
||||
// we need basis name to create the proper mfem quad space and quad func
|
||||
// the pattern used to encode the quad space params is:
|
||||
// QF_{ORDER}_{VDIM}
|
||||
// ORDER is the degree of the polynomials for the quad rule
|
||||
// VDIM is the number of components at each quad point (scalar, vector, etc)
|
||||
|
||||
int qf_order = 0;
|
||||
int qf_vdim = 0;
|
||||
std::string qf_name = n_field["basis"].as_string();
|
||||
const char *qf_name_cstr = qf_name.c_str();
|
||||
if (!strncmp(qf_name_cstr, "QF_", 3))
|
||||
{
|
||||
// parse {ORDER}
|
||||
qf_order = atoi(qf_name_cstr + 3);
|
||||
// find second `_`
|
||||
const char *qf_vdim_cstr = strstr(qf_name_cstr+3,"_");
|
||||
if (qf_vdim_cstr == NULL)
|
||||
{
|
||||
MFEM_ABORT("Error parsing quadrature function description string: "
|
||||
<< qf_name << std::endl
|
||||
<< "Expected: QF_{ORDER}_{VDIM}");
|
||||
}
|
||||
// parse {VDIM}
|
||||
qf_vdim = atoi(qf_vdim_cstr+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Error parsing quadrature function description string: "
|
||||
<< qf_name << std::endl
|
||||
<< "Expected: QF_{ORDER}_{VDIM}");
|
||||
}
|
||||
MFEM_VERIFY(qf_vdim == vdim, "vector dimension mismatch: vdim = " << vdim
|
||||
<< ", qf_vdim = " << qf_vdim);
|
||||
|
||||
mfem::QuadratureSpace *quad_space = new mfem::QuadratureSpace(mesh, qf_order);
|
||||
mfem::QuadratureFunction *res = new mfem::QuadratureFunction();
|
||||
|
||||
if (zero_copy)
|
||||
{
|
||||
res->SetSpace(quad_space, const_cast<real_t*>(vals_ptr), vdim);
|
||||
res->SetOwnsSpace(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
res->SetSpace(quad_space, vdim);
|
||||
res->SetOwnsSpace(true);
|
||||
// copy case, this constructor will alloc the space for the quad data
|
||||
// create an mfem vector that wraps the conduit data
|
||||
Vector vals_vec(const_cast<real_t*>(vals_ptr),res->Size());
|
||||
// copy values into the result
|
||||
(*res) = vals_vec;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
void
|
||||
ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
@@ -829,20 +656,20 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
// Setup main coordset
|
||||
////////////////////////////////////////////
|
||||
|
||||
// Assumes mfem::Vertex has the layout of a real_t (double/float) array.
|
||||
// Assumes mfem::Vertex has the layout of a double array.
|
||||
|
||||
// this logic assumes an mfem vertex is always 3 real_t (double/float) wide
|
||||
// this logic assumes an mfem vertex is always 3 doubles wide
|
||||
int stride = sizeof(mfem::Vertex);
|
||||
int num_vertices = mesh->GetNV();
|
||||
|
||||
MFEM_ASSERT( ( stride == 3 * sizeof(real_t) ),
|
||||
MFEM_ASSERT( ( stride == 3 * sizeof(double) ),
|
||||
"Unexpected stride for Vertex");
|
||||
|
||||
Node &n_mesh_coords = n_mesh["coordsets"][coordset_name];
|
||||
n_mesh_coords["type"] = "explicit";
|
||||
|
||||
|
||||
real_t *coords_ptr = mesh->GetVertex(0);
|
||||
double *coords_ptr = mesh->GetVertex(0);
|
||||
|
||||
n_mesh_coords["values/x"].set_external(coords_ptr,
|
||||
num_vertices,
|
||||
@@ -853,14 +680,14 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
{
|
||||
n_mesh_coords["values/y"].set_external(coords_ptr,
|
||||
num_vertices,
|
||||
sizeof(real_t),
|
||||
sizeof(double),
|
||||
stride);
|
||||
}
|
||||
if (dim >= 3)
|
||||
{
|
||||
n_mesh_coords["values/z"].set_external(coords_ptr,
|
||||
num_vertices,
|
||||
sizeof(real_t) * 2,
|
||||
sizeof(double) * 2,
|
||||
stride);
|
||||
}
|
||||
|
||||
@@ -1115,59 +942,6 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
void
|
||||
ConduitDataCollection::QuadratureFunctionToBlueprintField(
|
||||
mfem::QuadratureFunction *qf,
|
||||
Node &n_field,
|
||||
const std::string &main_topology_name)
|
||||
{
|
||||
// For quadrature functions, use basis pattern:
|
||||
// QF_{ORDER}_{VDIM}
|
||||
|
||||
int qf_vdim = qf->GetVDim();
|
||||
int qf_order = qf->GetSpace()->GetOrder();
|
||||
int qf_size = qf->GetSpace()->GetSize();
|
||||
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "QF_" << qf_order << "_" << qf_vdim;
|
||||
|
||||
n_field["basis"] = oss.str();
|
||||
n_field["topology"] = main_topology_name;
|
||||
}
|
||||
|
||||
if (qf_vdim == 1) // scalar case
|
||||
{
|
||||
n_field["values"].set_external(const_cast<real_t *>(qf->HostRead()),
|
||||
qf_size);
|
||||
}
|
||||
else // vector case
|
||||
{
|
||||
// deal with striding of all components
|
||||
// quadrature functions are always byVDIM
|
||||
// or what conduit calls interleaved
|
||||
|
||||
index_t offset = 0;
|
||||
index_t stride = sizeof(real_t) * qf_vdim;
|
||||
|
||||
for (int d = 0; d < qf_vdim; d++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "v" << d;
|
||||
std::string comp_name = oss.str();
|
||||
n_field["values"][comp_name].set_external(const_cast<real_t *>(qf->HostRead()),
|
||||
qf_size,
|
||||
offset,
|
||||
stride);
|
||||
offset += sizeof(real_t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------
|
||||
// end static public methods
|
||||
//------------------------------
|
||||
@@ -1193,7 +967,7 @@ ConduitDataCollection::RootFileName()
|
||||
//---------------------------------------------------------------------------//
|
||||
std::string
|
||||
ConduitDataCollection::MeshFileName(int domain_id,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
std::string res = prefix_path +
|
||||
name +
|
||||
@@ -1202,7 +976,7 @@ ConduitDataCollection::MeshFileName(int domain_id,
|
||||
"/domain_" +
|
||||
to_padded_string(domain_id, pad_digits_rank) +
|
||||
"." +
|
||||
relay_protocol_;
|
||||
relay_protocol;
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -1220,7 +994,7 @@ ConduitDataCollection::MeshDirectoryName()
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
std::string
|
||||
ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
|
||||
ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << name
|
||||
@@ -1229,7 +1003,7 @@ ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
|
||||
<< "/domain_%0"
|
||||
<< pad_digits_rank
|
||||
<< "d."
|
||||
<< relay_protocol_;
|
||||
<< relay_protocol;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
@@ -1239,14 +1013,14 @@ ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
|
||||
void
|
||||
ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
const Node &n_mesh,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
// default to json root file, except for hdf5 case
|
||||
std::string root_proto = "json";
|
||||
|
||||
if (relay_protocol_ == "hdf5")
|
||||
if (relay_protocol == "hdf5")
|
||||
{
|
||||
root_proto = relay_protocol_;
|
||||
root_proto = relay_protocol;
|
||||
}
|
||||
|
||||
Node n_root;
|
||||
@@ -1277,14 +1051,14 @@ ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
}
|
||||
}
|
||||
// add extra header info
|
||||
n_root["protocol/name"] = relay_protocol_;
|
||||
n_root["protocol/name"] = relay_protocol;
|
||||
n_root["protocol/version"] = "0.3.1";
|
||||
|
||||
|
||||
// we will save one file per domain, so trees == files
|
||||
n_root["number_of_files"] = num_domains;
|
||||
n_root["number_of_trees"] = num_domains;
|
||||
n_root["file_pattern"] = MeshFilePattern(relay_protocol_);
|
||||
n_root["file_pattern"] = MeshFilePattern(relay_protocol);
|
||||
n_root["tree_pattern"] = "";
|
||||
|
||||
// Add the time, time step, and cycle
|
||||
@@ -1299,9 +1073,9 @@ ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
void
|
||||
ConduitDataCollection::SaveMeshAndFields(int domain_id,
|
||||
const Node &n_mesh,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
relay::io::save(n_mesh, MeshFileName(domain_id, relay_protocol_));
|
||||
relay::io::save(n_mesh, MeshFileName(domain_id, relay_protocol));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
@@ -1398,13 +1172,13 @@ ConduitDataCollection::LoadRootFile(Node &root_out)
|
||||
//---------------------------------------------------------------------------//
|
||||
void
|
||||
ConduitDataCollection::LoadMeshAndFields(int domain_id,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
// Note: This path doesn't use any info from the root file
|
||||
// it uses the implicit mfem ConduitDataCollection layout
|
||||
|
||||
Node n_mesh;
|
||||
relay::io::load( MeshFileName(domain_id, relay_protocol_), n_mesh);
|
||||
relay::io::load( MeshFileName(domain_id, relay_protocol), n_mesh);
|
||||
|
||||
|
||||
Node verify_info;
|
||||
|
||||
@@ -50,11 +50,11 @@ namespace mfem
|
||||
Those that construct MFEM objects from Conduit Nodes (Conduit Blueprint to
|
||||
MFEM) provide a zero-copy option. Zero-copy is only possible if the
|
||||
blueprint data matches the data types provided by the MFEM API, for example:
|
||||
ints for connectivity arrays, real_t (double/float) for field value arrays,
|
||||
allocations that match MFEM's striding options, etc. If these constraints
|
||||
are not met, MFEM objects that own the data are created and returned. In
|
||||
either case pointers to new MFEM object instances are returned, the
|
||||
zero-copy only applies to data backing the MFEM object instances.
|
||||
ints for connectivity arrays, doubles for field value arrays, allocations
|
||||
that match MFEM's striding options, etc. If these constraints are not met,
|
||||
MFEM objects that own the data are created and returned. In either case
|
||||
pointers to new MFEM object instances are returned, the zero-copy only
|
||||
applies to data backing the MFEM object instances.
|
||||
|
||||
@note QuadratureFunction%s (q-fields) are not supported.
|
||||
|
||||
@@ -183,21 +183,6 @@ public:
|
||||
conduit::Node &out,
|
||||
const std::string &main_topology_name = "main");
|
||||
|
||||
/// Describes a MFEM quadrature function using the mesh blueprint
|
||||
/** Sets up passed conduit::Node out to describe the given quadrature function
|
||||
using the mesh field blueprint.
|
||||
|
||||
Zero-copies as much data as possible.
|
||||
|
||||
@a main_toplogy_name is used to set the associated topology name.
|
||||
With the default setting, the resulting field is associated with the
|
||||
topology `main`.
|
||||
*/
|
||||
static void QuadratureFunctionToBlueprintField(QuadratureFunction *qf,
|
||||
conduit::Node &out,
|
||||
const std::string &main_topology_name = "main");
|
||||
|
||||
|
||||
/// Constructs and MFEM mesh from a Conduit Blueprint Description
|
||||
/** @a main_topology_name is used to select which topology to use, when
|
||||
empty ("") the first topology entry will be used.
|
||||
@@ -205,7 +190,7 @@ public:
|
||||
If zero_copy == true, tries to construct a mesh that points to the data
|
||||
described by the conduit node. This is only possible if the data in the
|
||||
node matches the data types needed for the MFEM API (ints for
|
||||
connectivity, real_t for field values, etc). If these constraints are
|
||||
connectivity, doubles for field values, etc). If these constraints are
|
||||
not met, a mesh that owns the data is created and returned.
|
||||
*/
|
||||
static Mesh *BlueprintMeshToMesh(const conduit::Node &n_mesh,
|
||||
@@ -215,7 +200,7 @@ public:
|
||||
/// Constructs and MFEM Grid Function from a Conduit Blueprint Description
|
||||
/** If zero_copy == true, tries to construct a grid function that points to
|
||||
the data described by the conduit node. This is only possible if the data
|
||||
in the node matches the data types needed for the MFEM API (real_t for
|
||||
in the node matches the data types needed for the MFEM API (doubles for
|
||||
field values, allocated in soa or aos ordering, etc). If these
|
||||
constraints are not met, a grid function that owns the data is created
|
||||
and returned.
|
||||
@@ -223,17 +208,6 @@ public:
|
||||
static GridFunction *BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
const conduit::Node &n_field,
|
||||
bool zero_copy = false);
|
||||
/// Constructs and MFEM Quadrature Function from a Conduit Blueprint Description
|
||||
/** If zero_copy == true, tries to construct a quadrature function that points to
|
||||
the data described by the conduit node. This is only possible if the data
|
||||
in the node matches the data types needed for the MFEM API (real_t for
|
||||
field values, allocated in an interleavred/byVDIM order, etc). If these
|
||||
constraints are not met, a grid function that owns the data is created
|
||||
and returned.
|
||||
*/
|
||||
static QuadratureFunction *BlueprintFieldToQuadratureFunction(Mesh *mesh,
|
||||
const conduit::Node &n_field,
|
||||
bool zero_copy = false);
|
||||
|
||||
private:
|
||||
/// Converts from MFEM element type enum to mesh bp shape name
|
||||
|
||||
+22
-230
@@ -310,9 +310,9 @@ void DataCollection::SaveField(const std::string &field_name)
|
||||
}
|
||||
}
|
||||
|
||||
void DataCollection::SaveQField(const std::string &field_name)
|
||||
void DataCollection::SaveQField(const std::string &q_field_name)
|
||||
{
|
||||
QFieldMapIterator it = q_field_map.find(field_name);
|
||||
QFieldMapIterator it = q_field_map.find(q_field_name);
|
||||
if (it != q_field_map.end())
|
||||
{
|
||||
SaveOneQField(it);
|
||||
@@ -430,9 +430,7 @@ void VisItDataCollection::RegisterField(const std::string& name,
|
||||
}
|
||||
|
||||
DataCollection::RegisterField(name, gf);
|
||||
field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim(), LOD,
|
||||
gf->FESpace()->FEColl()->Name(),
|
||||
gf->FESpace()->FEColl()->GetOrder());
|
||||
field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim(), LOD);
|
||||
visit_levels_of_detail = std::max(visit_levels_of_detail, LOD);
|
||||
}
|
||||
|
||||
@@ -451,14 +449,7 @@ void VisItDataCollection::RegisterQField(const std::string& name,
|
||||
}
|
||||
|
||||
DataCollection::RegisterQField(name, qf);
|
||||
// For quadrature functions, use basis pattern:
|
||||
// QF_{ORDER}_{VDIM}
|
||||
int qf_vdim = qf->GetVDim();
|
||||
int qf_order = qf->GetSpace()->GetOrder();
|
||||
std::ostringstream oss;
|
||||
oss << "QF_" << qf_order << "_" << qf_vdim;
|
||||
field_info_map[name] = VisItFieldInfo("quadrature", qf->GetVDim(), LOD,
|
||||
oss.str(), qf_order);
|
||||
field_info_map[name] = VisItFieldInfo("elements", 1, LOD);
|
||||
visit_levels_of_detail = std::max(visit_levels_of_detail, LOD);
|
||||
}
|
||||
|
||||
@@ -492,8 +483,6 @@ void VisItDataCollection::SaveRootFile()
|
||||
to_padded_string(cycle, pad_digits_cycle) +
|
||||
".mfem_root";
|
||||
std::ofstream root_file(root_name);
|
||||
MFEM_VERIFY(root_file.is_open(),
|
||||
"Failed to open ofstream " << root_name);
|
||||
root_file << GetVisItRootString();
|
||||
if (!root_file)
|
||||
{
|
||||
@@ -634,8 +623,7 @@ void VisItDataCollection::LoadFields()
|
||||
{
|
||||
field_map.Register(it->first, new GridFunction(mesh, file), own_data);
|
||||
}
|
||||
else if ((it->second).association == "elements" || // old style
|
||||
(it->second).association == "quadrature") // new style
|
||||
else if ((it->second).association == "elements")
|
||||
{
|
||||
q_field_map.Register(it->first, new QuadratureFunction(mesh, file), own_data);
|
||||
}
|
||||
@@ -649,8 +637,7 @@ void VisItDataCollection::LoadFields()
|
||||
it->first,
|
||||
new ParGridFunction(dynamic_cast<ParMesh*>(mesh), file), own_data);
|
||||
}
|
||||
else if ((it->second).association == "elements" || // old style
|
||||
(it->second).association == "quadrature") // new style
|
||||
else if ((it->second).association == "elements")
|
||||
{
|
||||
q_field_map.Register(it->first, new QuadratureFunction(mesh, file), own_data);
|
||||
}
|
||||
@@ -689,8 +676,6 @@ std::string VisItDataCollection::GetVisItRootString()
|
||||
ftags["assoc"] = picojson::value((it->second).association);
|
||||
ftags["comps"] = picojson::value(to_string((it->second).num_components));
|
||||
ftags["lod"] = picojson::value(to_string((it->second).lod));
|
||||
ftags["basis"] = picojson::value((it->second).basis);
|
||||
ftags["order"] = picojson::value(to_string((it->second).order));
|
||||
field["path"] = picojson::value(path_str + it->first + file_ext_format);
|
||||
field["tags"] = picojson::value(ftags);
|
||||
fields[it->first] = picojson::value(field);
|
||||
@@ -767,31 +752,9 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
|
||||
it != fields_obj.end(); ++it)
|
||||
{
|
||||
picojson::value tags = it->second.get("tags");
|
||||
|
||||
// defaults that allow us to parse older mfem_root files
|
||||
int lod = 1;
|
||||
std::string basis = "";
|
||||
int order = -1;
|
||||
|
||||
if (tags.contains("lod"))
|
||||
{
|
||||
lod = to_int(tags.get("lod").get<std::string>());
|
||||
}
|
||||
|
||||
if (tags.contains("basis"))
|
||||
{
|
||||
basis = tags.get("comps").get<std::string>();
|
||||
}
|
||||
|
||||
if (tags.contains("order"))
|
||||
{
|
||||
order = to_int(tags.get("comps").get<std::string>());
|
||||
}
|
||||
|
||||
field_info_map[it->first] =
|
||||
VisItFieldInfo(tags.get("assoc").get<std::string>(),
|
||||
to_int(tags.get("comps").get<std::string>()),
|
||||
lod, basis, order);
|
||||
to_int(tags.get("comps").get<std::string>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -817,11 +780,6 @@ void ParaViewDataCollectionBase::SetHighOrderOutput(bool high_order_output_)
|
||||
high_order_output = high_order_output_;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetBoundaryOutput(bool bdr_output_)
|
||||
{
|
||||
bdr_output = bdr_output_;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
|
||||
{
|
||||
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
|
||||
@@ -977,27 +935,18 @@ void ParaViewDataCollection::Save()
|
||||
std::string vtu_prefix = col_path + "/" + GenerateVTUPath() + "/";
|
||||
|
||||
// Save the local part of the mesh and grid functions fields to the local
|
||||
// VTU file. Also save coefficient fields.
|
||||
// VTU file
|
||||
{
|
||||
std::string os_str = vtu_prefix + GenerateVTUFileName("proc", myid);
|
||||
std::ofstream os(os_str);
|
||||
MFEM_VERIFY(os.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
std::ofstream os(vtu_prefix + GenerateVTUFileName("proc", myid));
|
||||
os.precision(precision);
|
||||
SaveDataVTU(os, levels_of_detail);
|
||||
}
|
||||
|
||||
// Save the local part of the quadrature function fields.
|
||||
// Save the local part of the quadrature function fields
|
||||
for (const auto &qfield : q_field_map)
|
||||
{
|
||||
MFEM_VERIFY(!bdr_output,
|
||||
"QuadratureFunction output is not supported for "
|
||||
"ParaViewDataCollection on domain boundary!");
|
||||
const std::string &field_name = qfield.first;
|
||||
std::string os_str = vtu_prefix + GenerateVTUFileName(field_name, myid);
|
||||
std::ofstream os(os_str);
|
||||
MFEM_VERIFY(os.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
std::ofstream os(vtu_prefix + GenerateVTUFileName(field_name, myid));
|
||||
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel(), field_name);
|
||||
}
|
||||
|
||||
@@ -1008,13 +957,10 @@ void ParaViewDataCollection::Save()
|
||||
{
|
||||
// Create the main PVTU file
|
||||
{
|
||||
std::string os_str = vtu_prefix + GeneratePVTUFileName("data");
|
||||
std::ofstream pvtu_out(os_str);
|
||||
MFEM_VERIFY(pvtu_out.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
std::ofstream pvtu_out(vtu_prefix + GeneratePVTUFileName("data"));
|
||||
WritePVTUHeader(pvtu_out);
|
||||
|
||||
// Grid function fields and coefficient fields
|
||||
// Grid function fields
|
||||
pvtu_out << "<PPointData>\n";
|
||||
for (auto &field_it : field_map)
|
||||
{
|
||||
@@ -1025,24 +971,7 @@ void ParaViewDataCollection::Save()
|
||||
<< VTKComponentLabels(vec_dim) << " "
|
||||
<< "format=\"" << GetDataFormatString() << "\" />\n";
|
||||
}
|
||||
for (auto &field_it : coeff_field_map)
|
||||
{
|
||||
int vec_dim = 1;
|
||||
pvtu_out << "<PDataArray type=\"" << GetDataTypeString()
|
||||
<< "\" Name=\"" << field_it.first
|
||||
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
|
||||
<< "format=\"" << GetDataFormatString() << "\" />\n";
|
||||
}
|
||||
for (auto &field_it : vcoeff_field_map)
|
||||
{
|
||||
int vec_dim = field_it.second->GetVDim();
|
||||
pvtu_out << "<PDataArray type=\"" << GetDataTypeString()
|
||||
<< "\" Name=\"" << field_it.first
|
||||
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
|
||||
<< "format=\"" << GetDataFormatString() << "\" />\n";
|
||||
}
|
||||
pvtu_out << "</PPointData>\n";
|
||||
|
||||
// Element attributes
|
||||
pvtu_out << "<PCellData>\n";
|
||||
pvtu_out << "\t<PDataArray type=\"Int32\" Name=\"" << "attribute"
|
||||
@@ -1066,10 +995,8 @@ void ParaViewDataCollection::Save()
|
||||
const std::string &q_field_name = q_field.first;
|
||||
std::string q_fname = GeneratePVTUPath() + "/"
|
||||
+ GeneratePVTUFileName(q_field_name);
|
||||
std::string os_str = col_path + "/" + q_fname;
|
||||
std::ofstream pvtu_out(os_str);
|
||||
MFEM_VERIFY(pvtu_out.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
|
||||
std::ofstream pvtu_out(col_path + "/" + q_fname);
|
||||
WritePVTUHeader(pvtu_out);
|
||||
int vec_dim = q_field.second->GetVDim();
|
||||
pvtu_out << "<PPointData>\n";
|
||||
@@ -1142,8 +1069,7 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
|
||||
}
|
||||
os << " version=\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
os << "<UnstructuredGrid>\n";
|
||||
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel(),
|
||||
bdr_output);
|
||||
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel());
|
||||
|
||||
// dump out the grid functions as point data
|
||||
os << "<PointData >\n";
|
||||
@@ -1151,21 +1077,8 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
|
||||
// iterate over all grid functions
|
||||
for (FieldMapIterator it=field_map.begin(); it!=field_map.end(); ++it)
|
||||
{
|
||||
MFEM_VERIFY(!bdr_output,
|
||||
"GridFunction output is not supported for "
|
||||
"ParaViewDataCollection on domain boundary!");
|
||||
SaveGFieldVTU(os,ref,it);
|
||||
}
|
||||
// save the coefficient functions
|
||||
// iterate over all Coefficient and VectorCoefficient functions
|
||||
for (const auto &kv : coeff_field_map)
|
||||
{
|
||||
SaveCoeffFieldVTU(os, ref, kv.first, *kv.second);
|
||||
}
|
||||
for (const auto &kv : vcoeff_field_map)
|
||||
{
|
||||
SaveVCoeffFieldVTU(os, ref, kv.first, *kv.second);
|
||||
}
|
||||
os << "</PointData>\n";
|
||||
// close the mesh
|
||||
os << "</Piece>\n"; // close the piece open in the PrintVTU method
|
||||
@@ -1181,15 +1094,14 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
|
||||
DenseMatrix vval, pmat;
|
||||
std::vector<char> buf;
|
||||
int vec_dim = it->second->VectorDim();
|
||||
int map_type = it->second->FESpace()->GetTypicalFE()->GetMapType();
|
||||
os << "<DataArray type=\"" << GetDataTypeString()
|
||||
<< "\" Name=\"" << it->first
|
||||
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
|
||||
<< VTKComponentLabels(vec_dim) << " "
|
||||
<< "format=\"" << GetDataFormatString() << "\" >" << '\n';
|
||||
if (vec_dim == 1 && (map_type == FiniteElement::VALUE ||
|
||||
map_type == FiniteElement::INTEGRAL))
|
||||
if (vec_dim == 1)
|
||||
{
|
||||
// scalar data
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
RefG = GlobGeometryRefiner.Refine(
|
||||
@@ -1219,131 +1131,11 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pv_data_format != VTKFormat::ASCII)
|
||||
|
||||
if (IsBinaryFormat())
|
||||
{
|
||||
WriteBase64WithSizeAndClear(os, buf, GetCompressionLevel());
|
||||
}
|
||||
os << "</DataArray>" << std::endl;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SaveCoeffFieldVTU(std::ostream &os, int ref_,
|
||||
const std::string &name, Coefficient &coeff)
|
||||
{
|
||||
RefinedGeometry *RefG;
|
||||
real_t val;
|
||||
std::vector<char> buf;
|
||||
int vec_dim = 1;
|
||||
os << "<DataArray type=\"" << GetDataTypeString()
|
||||
<< "\" Name=\"" << name
|
||||
<< "\" NumberOfComponents=\"" << vec_dim << "\""
|
||||
<< " format=\"" << GetDataFormatString() << "\" >" << '\n';
|
||||
{
|
||||
// scalar data
|
||||
if (!bdr_output)
|
||||
{
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
RefG = GlobGeometryRefiner.Refine(
|
||||
mesh->GetElementBaseGeometry(i), ref_, 1);
|
||||
|
||||
ElementTransformation *eltrans = mesh->GetElementTransformation(i);
|
||||
const IntegrationRule *ir = &RefG->RefPts;
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
eltrans->SetIntPoint(&ip);
|
||||
val = coeff.Eval(*eltrans, ip);
|
||||
WriteBinaryOrASCII(os, buf, val, "\n", pv_data_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
RefG = GlobGeometryRefiner.Refine(
|
||||
mesh->GetBdrElementBaseGeometry(i), ref_, 1);
|
||||
|
||||
ElementTransformation *eltrans = mesh->GetBdrElementTransformation(i);
|
||||
const IntegrationRule *ir = &RefG->RefPts;
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
eltrans->SetIntPoint(&ip);
|
||||
val = coeff.Eval(*eltrans, ip);
|
||||
WriteBinaryOrASCII(os, buf, val, "\n", pv_data_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pv_data_format != VTKFormat::ASCII)
|
||||
{
|
||||
WriteBase64WithSizeAndClear(os, buf, GetCompressionLevel());
|
||||
}
|
||||
os << "</DataArray>" << std::endl;
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::SaveVCoeffFieldVTU(std::ostream &os, int ref_,
|
||||
const std::string &name, VectorCoefficient &coeff)
|
||||
{
|
||||
RefinedGeometry *RefG;
|
||||
Vector val;
|
||||
std::vector<char> buf;
|
||||
int vec_dim = coeff.GetVDim();
|
||||
os << "<DataArray type=\"" << GetDataTypeString()
|
||||
<< "\" Name=\"" << name
|
||||
<< "\" NumberOfComponents=\"" << vec_dim << "\""
|
||||
<< " format=\"" << GetDataFormatString() << "\" >" << '\n';
|
||||
{
|
||||
// vector data
|
||||
if (!bdr_output)
|
||||
{
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
RefG = GlobGeometryRefiner.Refine(
|
||||
mesh->GetElementBaseGeometry(i), ref_, 1);
|
||||
|
||||
ElementTransformation *eltrans = mesh->GetElementTransformation(i);
|
||||
const IntegrationRule *ir = &RefG->RefPts;
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
eltrans->SetIntPoint(&ip);
|
||||
coeff.Eval(val, *eltrans, ip);
|
||||
for (int jj = 0; jj < val.Size(); jj++)
|
||||
{
|
||||
WriteBinaryOrASCII(os, buf, val(jj), " ", pv_data_format);
|
||||
}
|
||||
if (pv_data_format == VTKFormat::ASCII) { os << '\n'; }
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
RefG = GlobGeometryRefiner.Refine(
|
||||
mesh->GetBdrElementBaseGeometry(i), ref_, 1);
|
||||
|
||||
ElementTransformation *eltrans = mesh->GetBdrElementTransformation(i);
|
||||
const IntegrationRule *ir = &RefG->RefPts;
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
eltrans->SetIntPoint(&ip);
|
||||
coeff.Eval(val, *eltrans, ip);
|
||||
for (int jj = 0; jj < val.Size(); jj++)
|
||||
{
|
||||
WriteBinaryOrASCII(os, buf, val(jj), " ", pv_data_format);
|
||||
}
|
||||
if (pv_data_format == VTKFormat::ASCII) { os << '\n'; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pv_data_format != VTKFormat::ASCII)
|
||||
{
|
||||
WriteBase64WithSizeAndClear(os, buf, GetCompressionLevel());
|
||||
WriteVTKEncodedCompressed(os,buf.data(),buf.size(),GetCompressionLevel());
|
||||
os << '\n';
|
||||
}
|
||||
os << "</DataArray>" << std::endl;
|
||||
}
|
||||
|
||||
+47
-138
@@ -25,35 +25,21 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Lightweight adaptor over an std::map from type K to type to V
|
||||
template<typename K, typename V,
|
||||
typename = typename std::enable_if<std::is_default_constructible<V>::value>::type>
|
||||
class GenericFieldMap
|
||||
/// Lightweight adaptor over an std::map from strings to pointer to T
|
||||
template<typename T>
|
||||
class NamedFieldsMap
|
||||
{
|
||||
private:
|
||||
static constexpr bool ValueIsPointer = std::is_pointer<V>::value;
|
||||
|
||||
public:
|
||||
typedef std::map<K, V> MapType;
|
||||
typedef std::map<std::string, T*> MapType;
|
||||
typedef typename MapType::iterator iterator;
|
||||
typedef typename MapType::const_iterator const_iterator;
|
||||
|
||||
/// Register field @a field with name @a key
|
||||
/// Only enabled if the template parameter V is not a pointer
|
||||
template<typename = std::enable_if<!ValueIsPointer, bool>>
|
||||
void Register(const K& key, V field)
|
||||
/// Register field @a field with name @a fname
|
||||
/** Replace existing field associated with @a fname (and optionally
|
||||
delete associated pointer if @a own_data is true) */
|
||||
void Register(const std::string& fname, T* field, bool own_data)
|
||||
{
|
||||
field_map[key] = field;
|
||||
}
|
||||
|
||||
/// Register field @a field with name @a key
|
||||
/** Replace existing field associated with @a key (and optionally
|
||||
delete associated pointer if @a own_data is true).
|
||||
Only enabled if the template parameter V is a pointer*/
|
||||
template<typename = std::enable_if<ValueIsPointer, bool>>
|
||||
void Register(const K& key, V field, bool own_data)
|
||||
{
|
||||
V& ref = field_map[key];
|
||||
T*& ref = field_map[fname];
|
||||
if (own_data)
|
||||
{
|
||||
delete ref; // if newly allocated -> ref is null -> OK
|
||||
@@ -61,40 +47,23 @@ public:
|
||||
ref = field;
|
||||
}
|
||||
|
||||
/// Unregister association between field @a field and name @a key
|
||||
/// Only enabled if the template parameter V is not a pointer
|
||||
template<typename = std::enable_if<!ValueIsPointer, bool>>
|
||||
void Deregister(const K& key)
|
||||
/// Unregister association between field @a field and name @a fname
|
||||
/** Optionally delete associated pointer if @a own_data is true */
|
||||
void Deregister(const std::string& fname, bool own_data)
|
||||
{
|
||||
iterator it = field_map.find(key);
|
||||
if ( it != field_map.end() )
|
||||
{
|
||||
field_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/// Unregister association between field @a field and name @a key
|
||||
/** Optionally delete associated pointer if @a own_data is true.
|
||||
Only enabled if the template parameter V is a pointer */
|
||||
template<typename = std::enable_if<ValueIsPointer, bool>>
|
||||
void Deregister(const K& key, bool own_data)
|
||||
{
|
||||
iterator it = field_map.find(key);
|
||||
iterator it = field_map.find(fname);
|
||||
if ( it != field_map.end() )
|
||||
{
|
||||
if (own_data)
|
||||
{
|
||||
delete it->second;
|
||||
it->second = nullptr;
|
||||
}
|
||||
field_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all associations between names and fields
|
||||
/** Delete associated pointers when @a own_data is true.
|
||||
Only enabled if the template parameter V is a pointer */
|
||||
template<typename = std::enable_if<ValueIsPointer, bool>>
|
||||
/** Delete associated pointers when @a own_data is true */
|
||||
void DeleteData(bool own_data)
|
||||
{
|
||||
for (iterator it = field_map.begin(); it != field_map.end(); ++it)
|
||||
@@ -107,37 +76,22 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Predicate to check if a field is associated with name @a key
|
||||
bool Has(const K& key) const
|
||||
/// Predicate to check if a field is associated with name @a fname
|
||||
bool Has(const std::string& fname) const
|
||||
{
|
||||
return field_map.find(key) != field_map.end();
|
||||
return field_map.find(fname) != field_map.end();
|
||||
}
|
||||
|
||||
/// Get a pointer to the field associated with name @a key
|
||||
/** @return Field associated with @a key or NULL,
|
||||
if value is pointer and key not found */
|
||||
V Get(const K& key) const
|
||||
/// Get a pointer to the field associated with name @a fname
|
||||
/** @return Pointer to field associated with @a fname or NULL */
|
||||
T* Get(const std::string& fname) const
|
||||
{
|
||||
const_iterator it = field_map.find(key);
|
||||
if (it != field_map.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (ValueIsPointer)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return V(); // Return default-constructed value for non-pointer types
|
||||
}
|
||||
}
|
||||
const_iterator it = field_map.find(fname);
|
||||
return it != field_map.end() ? it->second : NULL;
|
||||
}
|
||||
|
||||
/// Returns a const reference to the underlying map
|
||||
const MapType &GetMap() const { return field_map; }
|
||||
const MapType& GetMap() const { return field_map; }
|
||||
|
||||
/// Returns the number of registered fields
|
||||
int NumFields() const { return field_map.size(); }
|
||||
@@ -152,24 +106,21 @@ public:
|
||||
/// Returns an end const iterator to the registered fields
|
||||
const_iterator end() const { return field_map.end(); }
|
||||
|
||||
/// Returns an iterator to the field @a key
|
||||
iterator find(const K& key)
|
||||
{ return field_map.find(key); }
|
||||
/// Returns an iterator to the field @a fname
|
||||
iterator find(const std::string& fname)
|
||||
{ return field_map.find(fname); }
|
||||
|
||||
/// Returns a const iterator to the field @a key
|
||||
const_iterator find(const K& key) const
|
||||
{ return field_map.find(key); }
|
||||
/// Returns a const iterator to the field @a fname
|
||||
const_iterator find(const std::string& fname) const
|
||||
{ return field_map.find(fname); }
|
||||
|
||||
/// Clears the map of registered fields
|
||||
/// Clears the map of registered fields without reclaiming memory
|
||||
void clear() { field_map.clear(); }
|
||||
|
||||
protected:
|
||||
MapType field_map;
|
||||
};
|
||||
|
||||
/// Lightweight adaptor over an std::map from strings to pointer to T
|
||||
template<typename T>
|
||||
using NamedFieldsMap = GenericFieldMap<std::string, T*>;
|
||||
|
||||
/** A class for collecting finite element data that is part of the same
|
||||
simulation. Currently, this class groups together grid functions (fields),
|
||||
@@ -182,7 +133,6 @@ private:
|
||||
|
||||
/// A collection of named QuadratureFunctions
|
||||
typedef NamedFieldsMap<QuadratureFunction> QFieldMap;
|
||||
|
||||
public:
|
||||
typedef GFieldMap::MapType FieldMapType;
|
||||
typedef GFieldMap::iterator FieldMapIterator;
|
||||
@@ -299,9 +249,10 @@ public:
|
||||
{ field_map.Deregister(field_name, own_data); }
|
||||
|
||||
/// Add a QuadratureFunction to the collection.
|
||||
virtual void RegisterQField(const std::string& field_name,
|
||||
virtual void RegisterQField(const std::string& q_field_name,
|
||||
QuadratureFunction *qf)
|
||||
{ q_field_map.Register(field_name, qf, own_data); }
|
||||
{ q_field_map.Register(q_field_name, qf, own_data); }
|
||||
|
||||
|
||||
/// Remove a QuadratureFunction from the collection
|
||||
virtual void DeregisterQField(const std::string& field_name)
|
||||
@@ -329,13 +280,13 @@ public:
|
||||
#endif
|
||||
|
||||
/// Check if a QuadratureFunction with the given name is in the collection.
|
||||
bool HasQField(const std::string& field_name) const
|
||||
{ return q_field_map.Has(field_name); }
|
||||
bool HasQField(const std::string& q_field_name) const
|
||||
{ return q_field_map.Has(q_field_name); }
|
||||
|
||||
/// Get a pointer to a QuadratureFunction in the collection.
|
||||
/** Returns NULL if @a field_name is not in the collection. */
|
||||
QuadratureFunction *GetQField(const std::string& field_name)
|
||||
{ return q_field_map.Get(field_name); }
|
||||
QuadratureFunction *GetQField(const std::string& q_field_name)
|
||||
{ return q_field_map.Get(q_field_name); }
|
||||
|
||||
/// Get a const reference to the internal field map.
|
||||
/** The keys in the map are the field names and the values are pointers to
|
||||
@@ -351,13 +302,11 @@ public:
|
||||
|
||||
/// Get a pointer to the mesh in the collection
|
||||
Mesh *GetMesh() { return mesh; }
|
||||
|
||||
/// Set/change the mesh associated with the collection
|
||||
/** When passed a Mesh, assumes the serial case: MPI rank id is set to 0 and
|
||||
MPI num_procs is set to 1. When passed a ParMesh, MPI info from the
|
||||
ParMesh is used to set the DataCollection's MPI rank and num_procs. */
|
||||
virtual void SetMesh(Mesh *new_mesh);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Set/change the mesh associated with the collection.
|
||||
/** For this case, @a comm is used to set the DataCollection's MPI rank id
|
||||
@@ -420,7 +369,8 @@ public:
|
||||
/// Save one field, assuming the collection directory already exists.
|
||||
virtual void SaveField(const std::string &field_name);
|
||||
/// Save one q-field, assuming the collection directory already exists.
|
||||
virtual void SaveQField(const std::string &field_name);
|
||||
virtual void SaveQField(const std::string &q_field_name);
|
||||
|
||||
/// Load the collection. Not implemented in the base class DataCollection.
|
||||
virtual void Load(int cycle_ = 0);
|
||||
|
||||
@@ -457,18 +407,12 @@ public:
|
||||
class VisItFieldInfo
|
||||
{
|
||||
public:
|
||||
std::string association = "";
|
||||
int num_components = 0;
|
||||
int lod = 1;
|
||||
std::string basis = "";
|
||||
int order = -1;
|
||||
VisItFieldInfo() = default;
|
||||
VisItFieldInfo(std::string association_, int num_components_, int lod_ = 1,
|
||||
std::string basis_ = "", int order_ = -1)
|
||||
{
|
||||
association = association_; num_components = num_components_; lod =lod_;
|
||||
basis = basis_; order = order_;
|
||||
}
|
||||
std::string association;
|
||||
int num_components;
|
||||
int lod;
|
||||
VisItFieldInfo() { association = ""; num_components = 0; lod = 1;}
|
||||
VisItFieldInfo(std::string association_, int num_components_, int lod_ = 1)
|
||||
{ association = association_; num_components = num_components_; lod =lod_;}
|
||||
};
|
||||
|
||||
/// Data collection with VisIt I/O routines
|
||||
@@ -566,9 +510,7 @@ protected:
|
||||
int compression_level = -1;
|
||||
bool high_order_output = false;
|
||||
bool restart_mode = false;
|
||||
bool bdr_output = false;
|
||||
VTKFormat pv_data_format = VTKFormat::BINARY;
|
||||
|
||||
public:
|
||||
ParaViewDataCollectionBase(const std::string &name, Mesh *mesh);
|
||||
|
||||
@@ -601,10 +543,6 @@ public:
|
||||
/// Reading high-order data requires ParaView 5.5 or later.
|
||||
void SetHighOrderOutput(bool high_order_output_);
|
||||
|
||||
/// @brief Configures collection to save only fields evaluated on boundaries of
|
||||
/// the mesh.
|
||||
void SetBoundaryOutput(bool bdr_output_);
|
||||
|
||||
/// If compression is enabled, return the compression level, else return 0.
|
||||
int GetCompressionLevel() const;
|
||||
|
||||
@@ -626,6 +564,8 @@ public:
|
||||
///
|
||||
/// If restart is enabled, new writes will preserve timestep metadata for any
|
||||
/// solutions prior to the currently defined time.
|
||||
///
|
||||
/// Initially, restart mode is disabled.
|
||||
void UseRestartMode(bool restart_mode_);
|
||||
};
|
||||
|
||||
@@ -635,23 +575,11 @@ class ParaViewDataCollection : public ParaViewDataCollectionBase
|
||||
private:
|
||||
std::fstream pvd_stream;
|
||||
|
||||
/// A collection of named Coefficients and VectorCoefficients
|
||||
using CoeffFieldMap = NamedFieldsMap<Coefficient>;
|
||||
using VCoeffFieldMap = NamedFieldsMap<VectorCoefficient>;
|
||||
|
||||
/** A FieldMap mapping registered names to Coefficient and VectorCoefficient
|
||||
pointers. */
|
||||
CoeffFieldMap coeff_field_map;
|
||||
VCoeffFieldMap vcoeff_field_map;
|
||||
protected:
|
||||
void WritePVTUHeader(std::ostream &out);
|
||||
void WritePVTUFooter(std::ostream &out, const std::string &vtu_prefix);
|
||||
void SaveDataVTU(std::ostream &out, int ref);
|
||||
void SaveGFieldVTU(std::ostream& out, int ref_, const FieldMapIterator& it);
|
||||
void SaveCoeffFieldVTU(std::ostream& out, int ref_, const std::string &name,
|
||||
Coefficient &coeff);
|
||||
void SaveVCoeffFieldVTU(std::ostream& out, int ref_, const std::string &name,
|
||||
VectorCoefficient& coeff);
|
||||
const char *GetDataFormatString() const;
|
||||
const char *GetDataTypeString() const;
|
||||
|
||||
@@ -670,25 +598,6 @@ public:
|
||||
ParaViewDataCollection(const std::string& collection_name,
|
||||
Mesh *mesh_ = nullptr);
|
||||
|
||||
/// Get a const reference to the internal coefficient-field map.
|
||||
const typename CoeffFieldMap::MapType &GetCoeffFieldMap() const
|
||||
{ return coeff_field_map.GetMap(); }
|
||||
const typename VCoeffFieldMap::MapType &GetVCoeffFieldMap() const
|
||||
{ return vcoeff_field_map.GetMap(); }
|
||||
|
||||
/// Add a Coefficient or VectorCoefficient to the collection.
|
||||
void RegisterCoeffField(const std::string& field_name, Coefficient *coeff)
|
||||
{ coeff_field_map.Register(field_name, coeff, own_data); }
|
||||
void RegisterVCoeffField(const std::string& field_name,
|
||||
VectorCoefficient *vcoeff)
|
||||
{ vcoeff_field_map.Register(field_name, vcoeff, own_data); }
|
||||
|
||||
/// Remove a Coefficient or VectorCoefficient from the collection
|
||||
void DeregisterCoeffField(const std::string& field_name)
|
||||
{ coeff_field_map.Deregister(field_name, own_data); }
|
||||
void DeregisterVCoeffField(const std::string& field_name)
|
||||
{ vcoeff_field_map.Deregister(field_name, own_data); }
|
||||
|
||||
/// Save the collection - the directory name is constructed based on the
|
||||
/// cycle value
|
||||
void Save() override;
|
||||
|
||||
@@ -1,403 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Assemble element matrix for three dimensional data.
|
||||
///
|
||||
/// Note: In the below layouts, total_trial_op_dim is > 1 if
|
||||
/// there are more than one inputs dependent on the derivative variable.
|
||||
///
|
||||
/// @param A Memory for one element matrix with layout
|
||||
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
|
||||
/// @param fhat Memory to hold the residual computation with layout
|
||||
/// [test_vdim, test_op_dim, nqp].
|
||||
/// @param qpdc The quadrature point data cache with data layout
|
||||
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
|
||||
/// @param itod Input Trial Operator Dimension array. If the trial
|
||||
/// operator is not dependent, the dimension is 0 to indicate that.
|
||||
/// @param inputs The input field operator types.
|
||||
/// @param output The output field operator types.
|
||||
/// @param input_dtqmaps The input DofToQuad maps.
|
||||
/// @param output_dtqmap The output DofToQuad maps.
|
||||
/// @param scratch_shmem Scratch shared memory for computations.
|
||||
/// @param q1d The number of quadrature points in one dimension.
|
||||
/// @param td1d The number of trial dofs in one dimension.
|
||||
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
|
||||
MFEM_HOST_DEVICE void assemble_element_mat_t3d(
|
||||
const DeviceTensor<4, real_t>& A,
|
||||
const DeviceTensor<3, real_t>& fhat,
|
||||
const DeviceTensor<5, const real_t>& qpdc,
|
||||
const DeviceTensor<1, const real_t>& itod,
|
||||
const input_fop_ts& inputs,
|
||||
const output_fop_t& output,
|
||||
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
|
||||
const DofToQuadMap& output_dtqmap,
|
||||
std::array<DeviceTensor<1>, 6>& scratch_shmem,
|
||||
const int& q1d,
|
||||
const int& td1d)
|
||||
{
|
||||
constexpr int dimension = 3;
|
||||
|
||||
// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, num_qp]
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
|
||||
// [num_test_dof, ...]
|
||||
const auto num_test_dof = A.GetShape()[0];
|
||||
|
||||
for (int Jx = 0; Jx < td1d; Jx++)
|
||||
{
|
||||
for (int Jy = 0; Jy < td1d; Jy++)
|
||||
{
|
||||
for (int Jz = 0; Jz < td1d; Jz++)
|
||||
{
|
||||
const int J = Jx + td1d * (Jy + td1d * Jz);
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int tv = 0; tv < test_vdim; tv++)
|
||||
{
|
||||
for (int tod = 0; tod < test_op_dim; tod++)
|
||||
{
|
||||
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);
|
||||
fhat(tv, tod, q) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MSVC lambda capture workaround
|
||||
[[maybe_unused]] const auto& inputs_ref = inputs;
|
||||
|
||||
int m_offset = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
using fop_t = std::decay_t<decltype(get<s>(inputs_ref))>;
|
||||
|
||||
const int trial_op_dim = static_cast<int>(itod(static_cast<int>(s)));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
// This is inside a lambda so we have to return
|
||||
// instead of idiomatic 'continue'.
|
||||
return;
|
||||
}
|
||||
|
||||
auto& B = input_dtqmaps[s].B;
|
||||
auto& G = input_dtqmaps[s].G;
|
||||
|
||||
if constexpr (is_value_fop<fop_t>::value)
|
||||
{
|
||||
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);
|
||||
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy) * B(qz, 0, Jz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<fop_t>::value)
|
||||
{
|
||||
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);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
if (m == 0)
|
||||
{
|
||||
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy) * B(qz, 0, Jz);
|
||||
}
|
||||
else if (m == 1)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy) * B(qz, 0, Jz);
|
||||
}
|
||||
else if (m == 2)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy) * G(qz, 0, Jz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("sum factorized sparse matrix assemble routine "
|
||||
"not implemented for field operator");
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
m_offset += trial_op_dim;
|
||||
});
|
||||
|
||||
auto bvtfhat = Reshape(&A(0, 0, J, j), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(bvtfhat, fhat, output, output_dtqmap,
|
||||
scratch_shmem, dimension, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assemble element matrix for two dimensional data.
|
||||
///
|
||||
/// Note: In the below layouts, total_trial_op_dim is > 1 if
|
||||
/// there are more than one inputs dependent on the derivative variable.
|
||||
///
|
||||
/// @param A Memory for one element matrix with layout
|
||||
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
|
||||
/// @param fhat Memory to hold the residual computation with layout
|
||||
/// [test_vdim, test_op_dim, nqp].
|
||||
/// @param qpdc The quadrature point data cache with data layout
|
||||
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
|
||||
/// @param itod Input Trial Operator Dimension array. If the trial
|
||||
/// operator is not dependent, the dimension is 0 to indicate that.
|
||||
/// @param inputs The input field operator types.
|
||||
/// @param output The output field operator types.
|
||||
/// @param input_dtqmaps The input DofToQuad maps.
|
||||
/// @param output_dtqmap The output DofToQuad maps.
|
||||
/// @param scratch_shmem Scratch shared memory for computations.
|
||||
/// @param q1d The number of quadrature points in one dimension.
|
||||
/// @param td1d The number of trial dofs in one dimension.
|
||||
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
|
||||
MFEM_HOST_DEVICE void assemble_element_mat_t2d(
|
||||
const DeviceTensor<4, real_t>& A,
|
||||
const DeviceTensor<3, real_t>& fhat,
|
||||
const DeviceTensor<5, const real_t>& qpdc,
|
||||
const DeviceTensor<1, const real_t>& itod,
|
||||
const input_fop_ts& inputs,
|
||||
const output_fop_t& output,
|
||||
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
|
||||
const DofToQuadMap& output_dtqmap,
|
||||
std::array<DeviceTensor<1>, 6>& scratch_shmem,
|
||||
const int& q1d,
|
||||
const int& td1d)
|
||||
{
|
||||
constexpr int dimension = 2;
|
||||
|
||||
// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, num_qp]
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
|
||||
// [num_test_dof, ...]
|
||||
const auto num_test_dof = A.GetShape()[0];
|
||||
|
||||
for (int Jx = 0; Jx < td1d; Jx++)
|
||||
{
|
||||
for (int Jy = 0; Jy < td1d; Jy++)
|
||||
{
|
||||
const int J = Jy + Jx * td1d;
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int tv = 0; tv < test_vdim; tv++)
|
||||
{
|
||||
for (int tod = 0; tod < test_op_dim; tod++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
fhat(tv, tod, q) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MSVC lambda capture workaround
|
||||
[[maybe_unused]] const auto& inputs_ref = inputs;
|
||||
|
||||
int m_offset = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
using fop_t = std::decay_t<decltype(get<s>(inputs_ref))>;
|
||||
|
||||
const int trial_op_dim = static_cast<int>(itod(static_cast<int>(s)));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
// This is inside a lambda so we have to return
|
||||
// instead of idiomatic 'continue'.
|
||||
return;
|
||||
}
|
||||
|
||||
auto& B = input_dtqmaps[s].B;
|
||||
auto& G = input_dtqmaps[s].G;
|
||||
|
||||
if constexpr (is_value_fop<fop_t>::value)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<fop_t>::value)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
if (m == 0)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy);
|
||||
}
|
||||
else
|
||||
{
|
||||
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("sum factorized sparse matrix assemble routine "
|
||||
"not implemented for field operator");
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
m_offset += trial_op_dim;
|
||||
});
|
||||
|
||||
auto bvtfhat = Reshape(&A(0, 0, J, j), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(bvtfhat, fhat, output, output_dtqmap,
|
||||
scratch_shmem, dimension, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assemble element matrix for two or three dimensional data.
|
||||
///
|
||||
/// Note: In the below layouts, total_trial_op_dim is > 1 if
|
||||
/// there are more than one inputs dependent on the derivative variable.
|
||||
///
|
||||
/// @param A Memory for one element matrix with layout
|
||||
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
|
||||
/// @param fhat Memory to hold the residual computation with layout
|
||||
/// [test_vdim, test_op_dim, nqp].
|
||||
/// @param qpdc The quadrature point data cache with data layout
|
||||
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
|
||||
/// @param itod Input Trial Operator Dimension array. If the trial
|
||||
/// operator is not dependent, the dimension is 0 to indicate that.
|
||||
/// @param inputs The input field operator types.
|
||||
/// @param output The output field operator types.
|
||||
/// @param input_dtqmaps The input DofToQuad maps.
|
||||
/// @param output_dtqmap The output DofToQuad maps.
|
||||
/// @param scratch_shmem Scratch shared memory for computations.
|
||||
/// @param dimension The spatial dimension.
|
||||
/// @param q1d The number of quadrature points in one dimension.
|
||||
/// @param td1d The number of trial dofs in one dimension.
|
||||
/// @param use_sum_factorization Indicator if sum factorization is used.
|
||||
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
|
||||
MFEM_HOST_DEVICE void assemble_element_mat_naive(
|
||||
const DeviceTensor<4, real_t>& A,
|
||||
const DeviceTensor<3, real_t>& fhat,
|
||||
const DeviceTensor<5, const real_t>& qpdc,
|
||||
const DeviceTensor<1, const real_t>& itod,
|
||||
const input_fop_ts& inputs,
|
||||
const output_fop_t& output,
|
||||
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
|
||||
const DofToQuadMap& output_dtqmap,
|
||||
std::array<DeviceTensor<1>, 6>& scratch_shmem,
|
||||
const int& dimension,
|
||||
const int& q1d,
|
||||
const int& td1d,
|
||||
const bool& use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
assemble_element_mat_t2d(A, fhat, qpdc, itod, inputs, output,
|
||||
input_dtqmaps, output_dtqmap, scratch_shmem, q1d, td1d);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
assemble_element_mat_t3d(A, fhat, qpdc, itod, inputs, output,
|
||||
input_dtqmaps, output_dtqmap, scratch_shmem, q1d, td1d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("element matrix assemble not implemented for non tensor "
|
||||
"product basis");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
+48
-623
@@ -22,7 +22,6 @@
|
||||
#include "interpolate.hpp"
|
||||
#include "integrate.hpp"
|
||||
#include "qfunction_apply.hpp"
|
||||
#include "assemble.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
@@ -31,23 +30,14 @@ namespace mfem::future
|
||||
using action_t =
|
||||
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that computes the cache for the action of a derivative
|
||||
using derivative_setup_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that computes the action of a derivative
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that assembles the SparseMatrix of a
|
||||
/// derivative operator
|
||||
using assemble_derivative_sparsematrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, SparseMatrix *&)>;
|
||||
|
||||
/// @brief Type alias for a function that assembles the HypreParMatrix of a
|
||||
/// @brief Type alias for a function that assembles the sparse matrix of a
|
||||
/// derivative operator
|
||||
using assemble_derivative_hypreparmatrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix *&)>;
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
|
||||
|
||||
/// @brief Type alias for a function that applies the appropriate restriction to
|
||||
/// the solution and parameters
|
||||
@@ -91,8 +81,6 @@ public:
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
&assemble_derivative_sparsematrix_callbacks,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
Operator(height, width),
|
||||
@@ -103,8 +91,6 @@ public:
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
transpose_direction(transpose_direction),
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
assemble_derivative_sparsematrix_callbacks(
|
||||
assemble_derivative_sparsematrix_callbacks),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
@@ -170,29 +156,14 @@ public:
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Assemble the derivative operator into a SparseMatrix.
|
||||
///
|
||||
/// @param A The SparseMatrix to assemble the derivative operator into. Can
|
||||
/// be an uninitialized object.
|
||||
void Assemble(SparseMatrix *&A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_sparsematrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a SparseMatrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_sparsematrix_callbacks)
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assemble the derivative operator into a HypreParMatrix.
|
||||
///
|
||||
/// @param A The HypreParMatrix to assemble the derivative operator into. Can
|
||||
/// be an uninitialized object.
|
||||
void Assemble(HypreParMatrix *&A)
|
||||
void Assemble(HypreParMatrix &A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a HypreParMatrix");
|
||||
"derivative can't be assembled into a matrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
@@ -225,10 +196,6 @@ private:
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
/// Callbacks that assemble derivatives into a SparseMatrix.
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
|
||||
/// Callbacks that assemble derivatives into a HypreParMatrix.
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
@@ -244,8 +211,8 @@ private:
|
||||
///
|
||||
/// The operator is constructed with solution fields that it will act on and
|
||||
/// parameter fields that define coefficients. Quadrature functions are added by
|
||||
/// e.g. using AddDomainIntegrator() which specify how the operator evaluates
|
||||
/// those functions and parameters at quadrature points.
|
||||
/// e.g. using AddDomainIntegrator() which specify how the operator evaluates f
|
||||
/// those functionas and parameters at quadrature points.
|
||||
///
|
||||
/// Derivatives can be computed by obtaining a DerivativeOperator using
|
||||
/// GetDerivative().
|
||||
@@ -313,22 +280,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Add an integrator to the operator.
|
||||
/// Called only from AddDomainIntegrator() and AddBoundaryIntegrator().
|
||||
template <
|
||||
typename entity_t,
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t>
|
||||
void AddIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &attributes,
|
||||
derivative_ids_t derivative_ids);
|
||||
|
||||
/// @brief Add a domain integrator to the operator.
|
||||
///
|
||||
/// @param qfunc The quadrature function to be added.
|
||||
@@ -354,31 +305,6 @@ public:
|
||||
const Array<int> &domain_attributes,
|
||||
derivative_ids_t derivative_ids = std::make_index_sequence<0> {});
|
||||
|
||||
/// @brief Add a boundary integrator to the operator.
|
||||
///
|
||||
/// @param qfunc The quadrature function to be added.
|
||||
/// @param inputs Tuple of FieldOperators for the inputs of the quadrature
|
||||
/// function.
|
||||
/// @param outputs Tuple of FieldOperators for the outputs of the quadrature
|
||||
/// function.
|
||||
/// @param integration_rule IntegrationRule to use with this integrator.
|
||||
/// @param boundary_attributes Boundary attributes marker array indicating over
|
||||
/// which attributes this integrator will integrate over.
|
||||
/// @param derivative_ids Derivatives to be made available for this
|
||||
/// integrator.
|
||||
template <
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t = decltype(std::make_index_sequence<0> {})>
|
||||
void AddBoundaryIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &boundary_attributes,
|
||||
derivative_ids_t derivative_ids = std::make_index_sequence<0> {});
|
||||
|
||||
/// @brief Set the parameters for the operator.
|
||||
///
|
||||
/// This has to be called before using Mult() or MultTranspose().
|
||||
@@ -431,34 +357,6 @@ public:
|
||||
|
||||
const size_t derivative_idx = FindIdx(derivative_id, fields);
|
||||
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
{
|
||||
s_l[i] = *sol_l[i];
|
||||
}
|
||||
|
||||
std::vector<Vector> p_l(parameters_l.size());
|
||||
for (size_t i = 0; i < p_l.size(); i++)
|
||||
{
|
||||
p_l[i] = *par_l[i];
|
||||
}
|
||||
|
||||
fields_e.resize(solutions_l.size() + parameters_l.size());
|
||||
restriction_callback(s_l, p_l, fields_e);
|
||||
|
||||
// Dummy
|
||||
Vector dir_l;
|
||||
if (derivative_idx > s_l.size())
|
||||
{
|
||||
dir_l = p_l[derivative_idx - s_l.size()];
|
||||
}
|
||||
else
|
||||
{
|
||||
dir_l = s_l[derivative_idx];
|
||||
}
|
||||
|
||||
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
height,
|
||||
GetTrueVSize(fields[derivative_idx]),
|
||||
@@ -472,7 +370,6 @@ public:
|
||||
par_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose,
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id],
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
}
|
||||
|
||||
@@ -482,14 +379,10 @@ private:
|
||||
MultLevel mult_level = TVECTOR;
|
||||
|
||||
std::vector<action_t> action_callbacks;
|
||||
std::map<size_t, std::vector<derivative_setup_t>> derivative_setup_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
@@ -510,8 +403,6 @@ private:
|
||||
std::function<void(Vector &, Vector &)> output_restriction_transpose;
|
||||
restriction_callback_t restriction_callback;
|
||||
|
||||
std::map<size_t, Vector> derivative_qp_caches;
|
||||
|
||||
std::map<size_t, size_t> assembled_vector_sizes;
|
||||
|
||||
bool use_tensor_product_structure = true;
|
||||
@@ -532,52 +423,7 @@ void DifferentiableOperator::AddDomainIntegrator(
|
||||
const Array<int> &domain_attributes,
|
||||
derivative_ids_t derivative_ids)
|
||||
{
|
||||
AddIntegrator<Entity::Element>(
|
||||
qfunc, inputs, outputs, integration_rule, domain_attributes, derivative_ids);
|
||||
}
|
||||
|
||||
template <
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t>
|
||||
void DifferentiableOperator::AddBoundaryIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &boundary_attributes,
|
||||
derivative_ids_t derivative_ids)
|
||||
{
|
||||
|
||||
if (mesh.GetNFbyType(FaceType::Boundary) != mesh.GetNBE())
|
||||
{
|
||||
MFEM_ABORT("AddBoundaryIntegrator on meshes with interior boundaries is not supported.");
|
||||
}
|
||||
AddIntegrator<Entity::BoundaryElement>(
|
||||
qfunc, inputs, outputs, integration_rule, boundary_attributes, derivative_ids);
|
||||
}
|
||||
|
||||
template <
|
||||
typename entity_t,
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t>
|
||||
void DifferentiableOperator::AddIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &attributes,
|
||||
derivative_ids_t derivative_ids)
|
||||
{
|
||||
if constexpr (!(std::is_same_v<entity_t, Entity::Element> ||
|
||||
std::is_same_v<entity_t, Entity::BoundaryElement>))
|
||||
{
|
||||
static_assert(dfem::always_false<entity_t>,
|
||||
"entity type not supported in AddIntegrator");
|
||||
}
|
||||
using entity_t = Entity::Element;
|
||||
|
||||
static constexpr size_t num_inputs =
|
||||
tuple_size<decltype(inputs)>::value;
|
||||
@@ -638,44 +484,25 @@ void DifferentiableOperator::AddIntegrator(
|
||||
inputs_vdim[i] = get<i>(inputs).vdim;
|
||||
});
|
||||
|
||||
const Array<int> *elem_attributes = nullptr;
|
||||
if constexpr (std::is_same_v<entity_t, Entity::Element>)
|
||||
|
||||
Array<int> elem_attributes;
|
||||
elem_attributes.SetSize(mesh.GetNE());
|
||||
for (int i = 0; i < mesh.GetNE(); ++i)
|
||||
{
|
||||
elem_attributes = &mesh.GetElementAttributes();
|
||||
}
|
||||
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
|
||||
{
|
||||
elem_attributes = &mesh.GetBdrFaceAttributes();
|
||||
elem_attributes[i] = mesh.GetAttribute(i);
|
||||
}
|
||||
|
||||
const auto output_fop = get<0>(outputs);
|
||||
test_space_field_idx = FindIdx(output_fop.GetFieldId(), fields);
|
||||
|
||||
bool use_sum_factorization = false;
|
||||
Element::Type entity_element_type;
|
||||
if constexpr (std::is_same_v<entity_t, Entity::Element>)
|
||||
auto entity_element_type =
|
||||
Element::TypeFromGeometry(mesh.GetTypicalElementGeometry());
|
||||
if ((entity_element_type == Element::QUADRILATERAL ||
|
||||
entity_element_type == Element::HEXAHEDRON) &&
|
||||
use_tensor_product_structure == true)
|
||||
{
|
||||
entity_element_type =
|
||||
Element::TypeFromGeometry(mesh.GetTypicalElementGeometry());
|
||||
|
||||
if ((entity_element_type == Element::QUADRILATERAL ||
|
||||
entity_element_type == Element::HEXAHEDRON) &&
|
||||
use_tensor_product_structure == true)
|
||||
{
|
||||
use_sum_factorization = true;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
|
||||
{
|
||||
entity_element_type =
|
||||
Element::TypeFromGeometry(mesh.GetTypicalFaceGeometry());
|
||||
|
||||
if ((entity_element_type == Element::SEGMENT ||
|
||||
entity_element_type == Element::QUADRILATERAL) &&
|
||||
use_tensor_product_structure == true)
|
||||
{
|
||||
use_sum_factorization = true;
|
||||
}
|
||||
use_sum_factorization = true;
|
||||
}
|
||||
|
||||
ElementDofOrdering element_dof_ordering = ElementDofOrdering::NATIVE;
|
||||
@@ -713,17 +540,8 @@ void DifferentiableOperator::AddIntegrator(
|
||||
prolongation_transpose = get_prolongation_transpose(
|
||||
fields[test_space_field_idx], output_fop, mesh.GetComm());
|
||||
|
||||
int dimension;
|
||||
if constexpr (std::is_same_v<entity_t, Entity::Element>)
|
||||
{
|
||||
dimension = mesh.Dimension();
|
||||
}
|
||||
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
|
||||
{
|
||||
dimension = mesh.Dimension() - 1;
|
||||
}
|
||||
|
||||
[[maybe_unused]] const int num_elements = GetNumEntities<entity_t>(mesh);
|
||||
const int dimension = mesh.Dimension();
|
||||
[[maybe_unused]] const int num_elements = GetNumEntities<Entity::Element>(mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(mesh);
|
||||
const int num_qp = integration_rule.GetNPoints();
|
||||
|
||||
@@ -797,12 +615,6 @@ void DifferentiableOperator::AddIntegrator(
|
||||
thread_blocks.z = 1;
|
||||
}
|
||||
}
|
||||
else if (dimension == 1)
|
||||
{
|
||||
thread_blocks.x = q1d;
|
||||
thread_blocks.y = 1;
|
||||
thread_blocks.z = 1;
|
||||
}
|
||||
|
||||
action_callbacks.push_back(
|
||||
// Explicitly capture everything we need, so we can make explicit choice
|
||||
@@ -818,7 +630,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
domain_attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
@@ -851,13 +663,13 @@ void DifferentiableOperator::AddIntegrator(
|
||||
action_shmem_info.field_sizes,
|
||||
num_entities);
|
||||
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_attr = attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
const bool has_attr = domain_attributes.Size() > 0;
|
||||
const auto d_domain_attr = domain_attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes.Read();
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
|
||||
residual_shmem, scratch_shmem] =
|
||||
@@ -904,8 +716,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
Vector direction_e(get_restriction<entity_t>(fields[d_field_idx],
|
||||
element_dof_ordering)->Height());
|
||||
Vector direction_e;
|
||||
Vector derivative_action_e(output_e_size);
|
||||
derivative_action_e = 0.0;
|
||||
|
||||
@@ -917,84 +728,24 @@ void DifferentiableOperator::AddIntegrator(
|
||||
}
|
||||
const auto input_is_dependent = it->second;
|
||||
|
||||
// Trial operator dimension for each input.
|
||||
// The trial operator dimension is set for each input that is
|
||||
// dependent and if it is independent the dimension is 0.
|
||||
Vector inputs_trial_op_dim(num_inputs);
|
||||
int total_trial_op_dim = 0;
|
||||
{
|
||||
auto itod = Reshape(inputs_trial_op_dim.HostReadWrite(), num_inputs);
|
||||
int idx = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
if (!input_is_dependent[s])
|
||||
{
|
||||
itod(idx) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: BUG! Make this a general function that works for all kinds of inputs.
|
||||
itod(idx) = input_size_on_qp[s] / get<s>(inputs).vdim;
|
||||
}
|
||||
total_trial_op_dim += static_cast<int>(itod(idx));
|
||||
idx++;
|
||||
});
|
||||
}
|
||||
|
||||
// First Input index of the derivative
|
||||
const size_t d_input_idx = [d_field_idx, &input_to_field]
|
||||
{
|
||||
for (size_t i = 0; i < input_to_field.size(); i++)
|
||||
{
|
||||
if (input_to_field[i] == d_field_idx)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return size_t(SIZE_MAX);
|
||||
}();
|
||||
|
||||
const int trial_vdim = GetVDim(fields[d_field_idx]);
|
||||
const int num_trial_dof =
|
||||
get_restriction<entity_t>(fields[d_field_idx], element_dof_ordering)->Height() /
|
||||
inputs_vdim[d_input_idx] / num_entities;
|
||||
const int num_trial_dof_1d =
|
||||
input_dtq_maps[d_input_idx].B.GetShape()[DofToQuadMap::Index::DOF];
|
||||
|
||||
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim *
|
||||
num_entities);
|
||||
Ae_mem = 0.0;
|
||||
|
||||
// Quadrature point local derivative cache for each element, with data
|
||||
// layout:
|
||||
// [test_vdim, test_op_dim, trial_vdim, trial_op_dim, qp, num_entities].
|
||||
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
|
||||
trial_vdim *
|
||||
total_trial_op_dim * num_qp * num_entities);
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& fields_ref = this->fields;
|
||||
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
|
||||
|
||||
// In each of the callbacks we're saving the derivatives in the quadrature point
|
||||
// caches. This trades memory with computational effort but also minimizes
|
||||
// data movement on each multiplication of the gradient with a directional
|
||||
// vector.
|
||||
derivative_setup_callbacks[derivative_id].push_back(
|
||||
derivative_action_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
domain_attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
@@ -1002,37 +753,35 @@ void DifferentiableOperator::AddIntegrator(
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
|
||||
input_is_dependent, // std::array<bool, num_inputs>
|
||||
direction, // FieldDescriptor
|
||||
direction_e, // Vector
|
||||
derivative_action_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
da_size_on_qp, // int
|
||||
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
inputs_trial_op_dim,
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref
|
||||
](std::vector<Vector> &f_e, const Vector &dir_l) mutable
|
||||
&or_transpose
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_l,
|
||||
Vector &der_action_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, dir_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(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_domain_attr = attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes.Read();
|
||||
const bool has_attr = domain_attributes.Size() > 0;
|
||||
const auto d_domain_attr = domain_attributes.Read();
|
||||
|
||||
derivative_action_e = 0.0;
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
@@ -1050,104 +799,20 @@ void DifferentiableOperator::AddIntegrator(
|
||||
inputs, ir_weights, scratch_shmem, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
// TODO: Probably redundant
|
||||
set_zero(shadow_shmem);
|
||||
|
||||
auto qpdc_e = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc_e, itod, da_size_on_qp,
|
||||
q1d, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_info.total_size,
|
||||
shmem_cache.ReadWrite());
|
||||
});
|
||||
|
||||
// The derivative action only uses the quadrature point caches and applies
|
||||
// them to an input vector before integrating with the desired trial operator.
|
||||
derivative_action_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_fop, // class derived from FieldOperator
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent, // std::array<bool, num_inputs>
|
||||
direction, // FieldDescriptor
|
||||
direction_e, // Vector
|
||||
derivative_action_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
inputs_trial_op_dim,
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&or_transpose
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_l,
|
||||
Vector &der_action_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, dir_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(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_attr = attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
|
||||
derivative_action_e = 0.0;
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
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);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
map_direction_to_quadrature_data_conditional(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, inputs,
|
||||
ir_weights, scratch_shmem, input_is_dependent, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
call_qfunction_derivative_action<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem,
|
||||
da_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim,
|
||||
test_op_dim, num_qp);
|
||||
|
||||
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
|
||||
apply_qpdc(fhat, shadow_shmem, qpdce, itod, q1d, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
y, fhat, output_fop, output_dtq_shmem[0],
|
||||
@@ -1156,246 +821,6 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_cache.ReadWrite());
|
||||
or_transpose(derivative_action_e, der_action_l);
|
||||
});
|
||||
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent, // std::array<bool, num_inputs>
|
||||
direction_e, // Vector
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
num_trial_dof,
|
||||
num_trial_dof_1d,
|
||||
inputs_trial_op_dim,
|
||||
Ae_mem,
|
||||
output_to_field,
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&fields = fields_ref
|
||||
](std::vector<Vector> &f_e, SparseMatrix *&A) mutable
|
||||
{
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
auto Ae = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
|
||||
trial_vdim, num_entities);
|
||||
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_domain_attr = attributes.Read();
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
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);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto Aee = Reshape(&Ae(0, 0, 0, 0, e), num_test_dof, test_vdim, num_trial_dof,
|
||||
trial_vdim);
|
||||
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
assemble_element_mat_naive(Aee, fhat, qpdce, itod, inputs, output_fop,
|
||||
input_dtq_shmem, output_dtq_shmem[0], scratch_shmem, dimension, q1d,
|
||||
num_trial_dof_1d, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_info.total_size,
|
||||
shmem_cache.ReadWrite());
|
||||
|
||||
FieldDescriptor *trial_field = nullptr;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
trial_field = &fields[input_to_field[s]];
|
||||
}
|
||||
}
|
||||
|
||||
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&trial_field->data);
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
|
||||
auto tmp = Reshape(Ae_mem.HostReadWrite(), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim, num_entities);
|
||||
for (int e = 0; e < num_entities; e++)
|
||||
{
|
||||
DenseMatrix Aee(&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);
|
||||
trial_fes->GetElementVDofs(e, trial_vdofs);
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
Array<int> test_vdofs_mapped(test_vdofs.Size());
|
||||
|
||||
const Array<int> &test_dofmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*test_fes->GetFE(0)).GetDofMap();
|
||||
|
||||
if (test_dofmap.Size() == 0)
|
||||
{
|
||||
test_vdofs_mapped = test_vdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(test_dofmap.Size() == num_test_dof,
|
||||
"internal error: dof map of the test space does not "
|
||||
"match previously determined number of test space dofs");
|
||||
|
||||
for (int vd = 0; vd < test_vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < num_test_dof; i++)
|
||||
{
|
||||
test_vdofs_mapped[i + vd * num_test_dof] =
|
||||
test_vdofs[test_dofmap[i] + vd * num_test_dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> trial_vdofs_mapped(trial_vdofs.Size());
|
||||
const Array<int> &trial_dofmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*trial_fes->GetFE(0)).GetDofMap();
|
||||
|
||||
if (trial_dofmap.Size() == 0)
|
||||
{
|
||||
trial_vdofs_mapped = trial_vdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(trial_dofmap.Size() == num_trial_dof,
|
||||
"internal error: dof map of the test space does not "
|
||||
"match previously determined number of test space dofs");
|
||||
|
||||
for (int vd = 0; vd < trial_vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < num_trial_dof; i++)
|
||||
{
|
||||
trial_vdofs_mapped[i + vd * num_trial_dof] =
|
||||
trial_vdofs[trial_dofmap[i] + vd * num_trial_dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
A->AddSubMatrix(test_vdofs_mapped, trial_vdofs_mapped, Aee, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
|
||||
}
|
||||
}
|
||||
A->Finalize();
|
||||
});
|
||||
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& assemble_derivative_sparsematrix_callbacks_ref =
|
||||
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
|
||||
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
|
||||
[
|
||||
input_is_dependent,
|
||||
input_to_field,
|
||||
output_to_field,
|
||||
&spmatcb = assemble_derivative_sparsematrix_callbacks_ref,
|
||||
&fields = fields_ref
|
||||
](std::vector<Vector> &f_e, HypreParMatrix *&A) mutable
|
||||
{
|
||||
SparseMatrix *spmat = nullptr;
|
||||
for (const auto &f : spmatcb)
|
||||
{
|
||||
f(f_e, spmat);
|
||||
}
|
||||
|
||||
if (spmat == nullptr)
|
||||
{
|
||||
MFEM_ABORT("internal error");
|
||||
}
|
||||
|
||||
bool same_test_and_trial = false;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
if (output_to_field[0] == input_to_field[s])
|
||||
{
|
||||
same_test_and_trial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldDescriptor *trial_field = nullptr;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
trial_field = &fields[input_to_field[s]];
|
||||
}
|
||||
}
|
||||
|
||||
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&trial_field->data);
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
if (same_test_and_trial)
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
spmat);
|
||||
A = RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
trial_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
trial_fes->GetDofOffsets(),
|
||||
spmat);
|
||||
A = RAP(test_fes->Dof_TrueDof_Matrix(), &tmp,
|
||||
trial_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
delete spmat;
|
||||
});
|
||||
}, derivative_ids);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-93
@@ -90,88 +90,8 @@ void map_quadrature_data_to_fields_impl(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("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_1d(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] 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);
|
||||
auto yd = Reshape(&y(0, 0), d1d, vdim);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx) * B(qx, 0, dx);
|
||||
}
|
||||
yd(dx, 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);
|
||||
auto yd = Reshape(&y(0, 0), d1d, vdim);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx) * G(qx, 0, dx);
|
||||
}
|
||||
yd(dx, vd) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_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);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d);
|
||||
|
||||
for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
yqp(sq, qx) = fqp(sq, qx);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
"for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,9 +227,8 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,9 +413,8 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,11 +431,7 @@ void map_quadrature_data_to_fields(
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_1d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
else if (dimension == 2)
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_2d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
|
||||
+8
-113
@@ -338,92 +338,6 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_field_to_quadrature_data_tensor_product_1d(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] 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, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, vd);
|
||||
}
|
||||
fqp(vd, qx) = 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, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += G(qx, 0, dx) * field(dx, vd);
|
||||
}
|
||||
fqp(vd, 0, qx) = acc;
|
||||
}
|
||||
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(std::pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
f(qx) = w(qx);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_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);
|
||||
field_qp = field;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::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(
|
||||
@@ -511,7 +425,7 @@ 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<size_t, num_inputs> &input_to_field,
|
||||
const std::array<int, num_inputs> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
@@ -526,18 +440,11 @@ void map_fields_to_quadrature_data(
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
const DeviceTensor<1> &field_e =
|
||||
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
|
||||
fields_e[input_to_field[i]];
|
||||
(input_to_field[i] == -1) ? dummy_field_weight : fields_e[input_to_field[i]];
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_1d(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 2)
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
@@ -582,20 +489,14 @@ void map_field_to_quadrature_data_conditional(
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_1d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
}
|
||||
}
|
||||
@@ -638,7 +539,7 @@ void map_direction_to_quadrature_data_conditional(
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
@@ -646,13 +547,7 @@ void map_direction_to_quadrature_data_conditional(
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_1d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 2)
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
|
||||
+16
-337
@@ -44,20 +44,11 @@ void call_qfunction(
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -68,11 +59,11 @@ void call_qfunction(
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -92,7 +83,7 @@ void call_qfunction(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
@@ -132,26 +123,11 @@ void call_qfunction_derivative_action(
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
@@ -168,11 +144,11 @@ void call_qfunction_derivative_action(
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
@@ -188,14 +164,11 @@ void call_qfunction_derivative_action(
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("unsupported dimension");
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -207,301 +180,7 @@ void call_qfunction_derivative_action(
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction_derivative(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
DeviceTensor<5> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &das_qp,
|
||||
const int &q)
|
||||
{
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
const size_t num_inputs = itod.GetShape()[0];
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(itod(s));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto d_qp = Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
d_qp(j, m, q) = 1.0;
|
||||
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
d_qp(j, m, q) = 0.0;
|
||||
|
||||
auto f = Reshape(&r(0), test_vdim, test_op_dim);
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
qpdc(i, k, j, m + m_offset, q) = f(i, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Call a qfunction with the given parameters and
|
||||
/// compute it's derivative represented by the Jacobian on
|
||||
/// each quadrature point.
|
||||
///
|
||||
/// @param qfunc the qfunction to call.
|
||||
/// @param input_shmem the input shared memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param residual_shmem the residual shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param das_qp the size of the derivative action.
|
||||
/// @param q1d the number of quadrature points in 1D.
|
||||
/// @param dimension the spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction_derivative(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
DeviceTensor<5> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &das_qp,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
{
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("unsupported dimension");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
{
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// @brief Apply the quadrature point data cache (qpdc) to a vector
|
||||
/// (usually a direction) on quadrature point q.
|
||||
///
|
||||
/// The qpdc consists of compatible data to be used for integration with a test
|
||||
/// operator, e.g. Jacobians of a linearization from a FE operation with a trial
|
||||
/// function including integration weights and necessesary transformations.
|
||||
///
|
||||
/// @param fhat the qpdc applied to a vector in shadow_memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param q the current quadrature point index.
|
||||
template <size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q)
|
||||
{
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
const size_t num_inputs = itod.GetShape()[0];
|
||||
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(itod(s));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto d_qp =
|
||||
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
fhat(i, k, q) = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Apply the quadrature point data cache (qpdc) to a vector
|
||||
/// (usually a direction).
|
||||
///
|
||||
/// The qpdc consists of compatible data to be used for integration with a test
|
||||
/// operator, e.g. Jacobians of a linearization from a FE operation with a trial
|
||||
/// function including integration weights and necessesary transformations.
|
||||
///
|
||||
/// @param fhat the qpdc applied to a vector in shadow_memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param q1d number of quadrature points in 1D.
|
||||
/// @param dimension spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
template <size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("unsupported dimension");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ void process_qf_arg(
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * n) + j);
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,8 +94,8 @@ void process_qf_arg(
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * n) + j);
|
||||
arg(j, i).gradient = v((i * n) + j);
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
arg(j, i).gradient = v((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,14 +181,6 @@ void process_derivative_from_native_dual(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const dual<T, T> &x)
|
||||
{
|
||||
r(0) = x.gradient;
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
@@ -238,7 +230,7 @@ void process_qf_arg(
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * n) + j);
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +330,7 @@ void process_qf_arg(
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * n) + j);
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
#pragma once
|
||||
|
||||
// This is smith's tuple implementation
|
||||
// This is serac's tuple implementation
|
||||
|
||||
#include <ostream>
|
||||
#include "../../config/config.hpp"
|
||||
@@ -454,7 +454,7 @@ MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
|
||||
*
|
||||
* @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 subtracted from
|
||||
* @param x tuple of values to be subracted from
|
||||
* @param y tuple of values to subtract from x
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
@@ -596,7 +596,7 @@ MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
|
||||
* @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 denominator
|
||||
* @param a the constant denomenator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
@@ -726,7 +726,7 @@ MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const real_t a)
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @tparam i a list of indices used to access each element of 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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user