Compare commits

..
31 Commits
Author SHA1 Message Date
Vladimir Z Tomov eb378fac0e Time step estimates for the CG case - global vs local. 2026-05-22 10:43:48 -07:00
Vladimir Z Tomov 4626eb5e68 style 2025-04-18 17:47:53 -07:00
Vladimir Z Tomov 0eb3b098e2 Cleaned raw pointers. 2025-04-18 17:35:15 -07:00
Vladimir Z Tomov d69f3c9dd7 Merge branch 'master' into ex9CG 2025-04-18 09:26:22 -07:00
Vladimir Z Tomov a1f9bb47ad Reused repeated code for serial and parallel. 2025-04-18 09:25:29 -07:00
Vladimir Z Tomov b170c87236 Merge branch 'master' into ex9CG 2025-04-11 15:05:33 -07:00
Paul Moujaes:MAIL:TERMINAL:CLOUD f034f56fd2 lines adjusted to have max. 80 characters per line, hopefully I did not mess something up, bc I had some problems with git 2025-01-20 17:07:20 +01:00
Vladimir Z Tomov 590ef8c2b3 style 2024-11-10 14:44:40 -08:00
Vladimir Z Tomov 1d52c34c3b Merge branch 'master' into ex9CG 2024-11-10 14:41:12 -08:00
Paul Moujaes:MAIL:TERMINAL:CLOUD 6cc6f093e4 small changes to comments 2024-10-01 16:32:21 +02:00
Moujaes 1098ef093d initializing pointers as NULL 2024-09-13 16:15:48 -07:00
Moujaes a624c23c4c zeros in the min max function calls fixed 2024-09-13 15:34:58 -07:00
Moujaes 0e56e8779c astyle 2024-09-13 15:08:59 -07:00
Moujaes 48e43f7b4d fixing warning of potentially uninitialized pointer 2024-09-13 15:05:08 -07:00
Moujaes 7c1fbeb8f4 initialize order of CG_FE_Evolution 2024-09-13 14:49:44 -07:00
Moujaes fdca557b08 pull request 2024-09-13 14:10:31 -07:00
Moujaes 27c0aeb778 astyle 2024-09-12 15:41:35 -07:00
Moujaes f142d1a807 master merged to get up to date 2024-09-12 15:27:08 -07:00
Moujaes 7589e562df lumped boundary condition and added example runs to serial code 2024-09-12 15:18:08 -07:00
Moujaes 6706348043 fixed possible mistake in bounding fluxes for C&S 2024-09-11 15:12:57 -07:00
Moujaes bf913dbb08 astyle of ex9 and ex9p 2024-09-10 16:49:43 -07:00
Moujaes 0bea336b6e limiter added to ex9 serial 2024-09-10 12:57:28 -07:00
Moujaes 7c7d4f746f commenting 2024-09-10 11:24:17 -07:00
Moujaes 65cca1288a astyle second try 2024-09-09 17:14:03 -07:00
Moujaes 2427b4cc24 astyle 2024-09-09 16:40:24 -07:00
Moujaes c9a2cea4e9 Class implementations moved below main 2024-09-09 14:23:43 -07:00
Moujaes ac53487552 TargetScheme and low order implemented, some aborts for CG like when mesh is non-conforming, DG uses HypreParVector again such that -ea works again 2024-09-09 13:06:27 -07:00
Moujaes a3fb28cffe small cleanup 2024-09-06 16:27:57 -07:00
Moujaes db27161e5e Clip and Scale 2024-09-06 13:38:48 -07:00
Moujaes cfb5c01de2 Merge branch 'master' into ex9CG 2024-09-05 10:25:24 -07:00
Moujaes 6d46935f90 init 2024-09-05 10:22:17 -07:00
414 changed files with 10566 additions and 40924 deletions
-154
View File
@@ -1,154 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: Sanitizer Config
description: Sets up environment variables for MFEM sanitizer workflow
inputs:
DEBUG:
description: If true, use intermediate caches to speed up the workflow
by reusing previous builds.
default: false
REPOSITORY:
description: Repository to checkout
default: mfem/mfem
BRANCH:
description: Branch to checkout
default: ubsan
CLANG_VER:
description: CLANG version to use
default: 18
# https://github.com/llvm/llvm-project/releases
LLVM_VER:
description: LLVM version to use
default: 19.1.7
# https://github.com/hypre-space/hypre/releases
HYPRE_VER:
description: HYPRE version to use
default: 2.19.0
METIS_VER:
description: METIS version to use
default: 4.0.3
CTEST:
description: CTest command to use
default: ctest -j --test-load $(nproc)
--schedule-random
--stop-on-failure --output-on-failure
--test-dir
# https://clang.llvm.org/docs/AddressSanitizer.html
ASAN_OPTIONS:
default: detect_leaks=1,
strict_init_order=1,
strict_string_checks=1,
check_initialization_order=1,
detect_stack_use_after_return=1
ASAN_CXXFLAGS:
default: -fsanitize=address
-fsanitize-address-use-after-scope
ASAN_LDFLAGS:
default: -fsanitize=address
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
UBSAN_OPTIONS:
default: halt_on_error=1, print_stacktrace=1
UBSAN_CXXFLAGS:
default: -fsanitize=undefined
UBSAN_LDFLAGS:
default: -fsanitize=undefined
# https://clang.llvm.org/docs/MemorySanitizer.html
MSAN_OPTIONS:
default: "poison_in_dtor=1"
MSAN_CXXFLAGS:
default: -fsanitize=memory
-fsanitize-memory-track-origins
-fsanitize-memory-use-after-dtor
MSAN_LDFLAGS:
default: -fsanitize=memory
LSAN_DIR:
description: LSAN suppression directory
default: lsan
LSAN_FILE:
description: LSAN suppression file
default: lsan.supp
NO_FLAGS:
description: If true, do not set any CXXFLAGS or LDFLAGS.
default: false
runs:
using: 'composite'
steps:
- name: Env (Inputs)
run: |
echo DEBUG=${{inputs.DEBUG}} >> $GITHUB_ENV
echo REPOSITORY=${{inputs.REPOSITORY}} >> $GITHUB_ENV
echo BRANCH=${{inputs.BRANCH}} >> $GITHUB_ENV
echo CLANG_VER=${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo LLVM_VER=${{inputs.LLVM_VER}} >> $GITHUB_ENV
echo HYPRE_VER=${{inputs.HYPRE_VER}} >> $GITHUB_ENV
echo METIS_VER=${{inputs.METIS_VER}} >> $GITHUB_ENV
echo CTEST=${{inputs.CTEST}} >> $GITHUB_ENV
echo ASAN_OPTIONS=${{inputs.ASAN_OPTIONS}} >> $GITHUB_ENV
echo UBSAN_OPTIONS=${{inputs.UBSAN_OPTIONS}} >> $GITHUB_ENV
echo MSAN_OPTIONS=${{inputs.MSAN_OPTIONS}} >> $GITHUB_ENV
echo LSAN_DIR=${{inputs.LSAN_DIR}} >> $GITHUB_ENV
echo LSAN_FILE=${{inputs.LSAN_FILE}} >> $GITHUB_ENV
echo ASAN_CXXFLAGS=${{inputs.ASAN_CXXFLAGS}} >> $GITHUB_ENV
echo ASAN_LDFLAGS=${{inputs.ASAN_LDFLAGS}} >> $GITHUB_ENV
echo UBSAN_CXXFLAGS=${{inputs.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Env (dir)
run: |
echo LLVM_DIR=${{github.workspace}}/llvm >> $GITHUB_ENV
echo HYPRE_DIR=hypre-${{inputs.HYPRE_VER}} >> $GITHUB_ENV
echo METIS_DIR=metis-${{inputs.METIS_VER}} >> $GITHUB_ENV
shell: bash
- name: Env (bis)
run: |
echo CC=clang-${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo CXX=clang++-${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo LLVM_INC=${{env.LLVM_DIR}}/include/c++/v1 >> $GITHUB_ENV
echo LLVM_LIB=${{env.LLVM_DIR}}/lib >> $GITHUB_ENV
echo HYPRE_TGZ=v${{inputs.HYPRE_VER}}.tar.gz >> $GITHUB_ENV
echo METIS_TGZ=metis-${{inputs.METIS_VER}}.tar.gz >> $GITHUB_ENV
LSAN_SUPPRESSIONS="${{github.workspace}}/${{inputs.LSAN_DIR}}/${{inputs.LSAN_FILE}}"
echo "LSAN_OPTIONS=suppressions=$LSAN_SUPPRESSIONS" >> $GITHUB_ENV
shell: bash
- name: Env (ter)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo LLVM_CXXFLAGS=-stdlib=libc++ -I${{env.LLVM_INC}} -Isystem${{env.LLVM_INC}} >> $GITHUB_ENV
echo LLVM_LDFLAGS=-L${{env.LLVM_LIB}} -lc++abi -Wl,-rpath,${{env.LLVM_LIB}} >> $GITHUB_ENV
shell: bash
- name: Env (quater)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo CXXFLAGS=${{env.LLVM_CXXFLAGS}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LLVM_LDFLAGS}} >> $GITHUB_ENV
shell: bash
-91
View File
@@ -1,91 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'MFEM Compilation'
description: 'MFEM Compilation'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
runs:
using: 'composite'
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
if: ${{env.DEBUG == 'true'}}
id: debug
with:
path: mfem/build
key: build-${{inputs.par}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/setup
if: ${{steps.debug.outputs.cache-hit != 'true'}}
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
- name: Build with ASAN
if: inputs.sanitizer == 'asan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.ASAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Build with MSAN
if: inputs.sanitizer == 'msan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Build with UBSAN
if: inputs.sanitizer == 'ubsan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- uses: mfem/github-actions/build-mfem@v2.5
if: ${{steps.debug.outputs.cache-hit != 'true'}}
env:
CXXFLAGS: ${{env.CXXFLAGS}}
LDFLAGS: ${{env.LDFLAGS}}
with:
mpi: ${{inputs.par == 'false' && 'seq' || 'par'}}
mfem-dir: mfem
os: ${{runner.os}}
library-only: true
build-system: cmake
hypre-dir: ${{env.HYPRE_DIR}}
metis-dir: ${{env.METIS_DIR}}
config-options: >-
-GNinja
-DMPICXX=${{env.CXX}}
-DCMAKE_CXX_STANDARD=17
-DMFEM_USE_MEMALLOC=OFF
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_VERBOSE_MAKEFILE=ON
-DCMAKE_CXX_COMPILER=${{env.CXX}}
-DCMAKE_CXX_FLAGS_RELEASE='-g -O1 -fno-omit-frame-pointer'
- name: Delete object files
if: ${{steps.debug.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: find . -type f -name '*.o' -delete
shell: bash
- uses: actions/upload-artifact@v4
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
if-no-files-found: error
retention-days: 1
overwrite: false
-33
View File
@@ -1,33 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'Install MPI'
description: 'Installs MPI and set up its environment variables'
runs:
using: 'composite'
steps:
- name: Install
run: sudo apt-get install openmpi-bin libopenmpi-dev
shell: bash
- name: Env
run: |
echo PRTE_MCA_rmaps_default_mapping_policy=:oversubscribe >> $GITHUB_ENV
echo MPI_INC=$(mpicxx --showme:compile) >> $GITHUB_ENV
echo MPI_LIB=$(mpicxx --showme:link) >> $GITHUB_ENV
shell: bash
- name: Env (bis)
run: |
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
shell: bash
@@ -1,71 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'Restore state'
description: 'Restore state to be able to run checks, tests'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
cache-path:
description: 'path to what needs to be restored'
default: none
cache-skip:
description: 'Skip cache restoration'
default: false
outputs:
cache-hit:
description: 'Output from a specific step'
value: ${{steps.debug.outputs.cache-hit}}
runs:
using: 'composite'
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
id: debug
with:
path: ${{inputs.cache-path}}
key: ${{github.job}}-${{inputs.par}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/setup
if: ${{steps.debug.outputs.cache-hit != 'true'}}
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
- uses: actions/download-artifact@v4
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
- name: Ninja Patch
working-directory: mfem/build
run: |
sed -i -e 's/CXX_STATIC_LIBRARY_LINKER__mfem_Release.*/CUSTOM_COMMAND/' build.ninja
sed -i -e '/build tests\/unit\/all:/ s/tests\/unit\/[^ ]*unit_tests[^ ]*//g' build.ninja
sed -i -e '/^add_test(\[=\[\(unit_tests\|punit_tests\)\]=\]/ s/)/ "--input-file .\/list-test-names-${{matrix.tag}}" "--min-duration 1")/' tests/unit/CTestTestfile.cmake
shell: bash
- name: Copy Data
if: ${{steps.debug.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
ninja cmake_object_order_depends_target_unit_tests
cp -pR ../tests/unit/data tests/unit
shell: bash
-64
View File
@@ -1,64 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'Setup state'
description: 'Sets up the state to be able to run build & run'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
runs:
using: 'composite'
steps:
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
with:
path: ${{env.LLVM_DIR}}
fail-on-cache-miss: true
key: build-libcxx-${{env.LLVM_VER}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/mpi
if: ${{inputs.par == 'true'}}
- 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-v2.5
- 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}}-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@v4 # Cache for LSAN suppression file
with:
path: ${{env.LSAN_DIR}}
fail-on-cache-miss: true
key: build-lsan-suppression-file
- uses: actions/checkout@v4 # Checkout the repository
with:
path: mfem
# ref: ${{env.BRANCH}}
# repository: ${{env.REPOSITORY}}
+7 -26
View File
@@ -7,17 +7,18 @@
https://mfem.org
This directory contains the GitHub CI scripts for MFEM.
Note that some of these scripts use the shared MFEM GitHub Actions from the external mfem/github-actions repository:
<https://github.com/mfem/github-actions>
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 in the above from which the action is taken.
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.1`, the `v2.1` suffix denotes the branch in the above from which the action is taken.
The current CI workflows are:
## `repo-check.yml`
### `repo-check.yml`
Runs a number of static repository-level sanity checks.
@@ -29,39 +30,19 @@ 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`)
### `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`
### `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.
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
Uses the following GitHub Actions from https://github.com/mfem/github-actions:
- `mfem/github-actions/build-hypre`
- `mfem/github-actions/build-metis`
- `mfem/github-actions/build-mfem`
- `mfem/github-actions/upload-coverage`
## Sanitizer Workflow for MFEM Verification
This workflow validates MFEM unit tests, examples, and miniapps using sanitizer tools.
- `sanitizers.yml` orchestrates:
- Building and caching dependencies: HYPRE, METIS, LSAN suppression file, and LLVM libcxx.
- Launching fine-grained jobs for serial (ASAN, MSAN, UBSAN) and parallel (ASAN, UBSAN) sanitizers.
- `sanitize-tests.yml` is a reusable workflow accepting `par` mode (`true` for parallel) and `sanitizer` (ASAN, MSAN, or UBSAN) as inputs. It executes the following jobs:
- **Build**: Compiles the MFEM library with specified parallel and sanitizer settings.
- **Check**: Runs verification checks.
- Parallel jobs to test the following: **Examples**, **Miniapps** and **Unit tests**
The workflow leverages composite actions in `.github/actions/sanitize/`:
- `config`: Centralizes settings for the sanitizer workflow.
- `mfem`: Manages the MFEM library build process.
- `mpi`: Installs MPI and applies additional compilation flags.
- `restore`: Restores the testing environment state.
- `setup`: Builds or restores cached dependencies.
+7 -50
View File
@@ -58,7 +58,6 @@ jobs:
build-system: [make, cmake]
hypre-target: [int32]
precision: [fp64]
enzyme: [false]
exclude:
- os: ubuntu-latest
build-system: cmake
@@ -81,17 +80,15 @@ jobs:
codecov: YES
- os: ubuntu-latest
target: dbg
config-opts: "CPPFLAGS+=-Og"
config-opts: 'CPPFLAGS+=-Og'
- os: macos-latest
codecov: NO
- os: windows-latest
codecov: NO
# config-opts: '-G "Ninja Multi-Config"'
- os: windows-latest
target: opt
mpi: par
config-opts: "-DBUILD_SHARED_LIBS=ON"
# config-opts: '-DBUILD_SHARED_LIBS=ON -G "Ninja Multi-Config"'
config-opts: '-DBUILD_SHARED_LIBS=ON'
- os: ubuntu-latest
target: opt
codecov: NO
@@ -99,7 +96,7 @@ jobs:
build-system: cmake
hypre-target: int32
precision: fp64
config-opts: "-DCMAKE_INSTALL_PREFIX=../cmake-install"
config-opts: '-DCMAKE_INSTALL_PREFIX=../cmake-install'
# This option can be set to pass additional configuration options to
# the MFEM configuration command.
# config-opts: '-DCMAKE_VERBOSE_MAKEFILE=ON'
@@ -124,30 +121,18 @@ jobs:
build-system: make
hypre-target: int32
precision: fp32
- os: macos-latest
target: opt
codecov: NO
mpi: par
build-system: make
hypre-target: int32
precision: fp64
enzyme: true
config-opts: MFEM_USE_ENZYME=YES ENZYME_DIR=$(brew --prefix enzyme) LDFLAGS=-L$LLVM_PREFIX/lib/c++
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}${{ matrix.enzyme && '-enzyme' || '' }}
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.enzyme && true || false }}
steps:
# Fix 'No space left on device' errors for Ubuntu builds.
- name: Run Actions Cleaner
if: matrix.os == 'ubuntu-latest'
uses: easimon/maximize-build-space@v8
with:
overprovision-lvm: "true"
remove-android: "true"
overprovision-lvm: 'true'
remove-android: 'true'
# Checkout MFEM in "mfem" subdirectory. Final path:
# /home/runner/work/mfem/mfem/mfem
@@ -159,24 +144,10 @@ jobs:
# Fetch the complete history for codecov to access commits ID
fetch-depth: 0
- name: Windows environment - PowerShell [debug]
if: matrix.os == 'windows-latest'
run: |
ls env: | fl
- name: Windows environment - Bash [debug]
if: matrix.os == 'windows-latest'
run: |
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"
XCODE_PATH="/Applications/Xcode_15.3.app"
echo "> sudo xcode-select -s ${XCODE_PATH}"
sudo xcode-select -s ${XCODE_PATH}
echo "> g++ -v"
@@ -287,20 +258,6 @@ jobs:
run: |
vcpkg install metis-mfem --triplet=x64-windows-static --overlay-ports=${{ env.MFEM_TOP_DIR }}/config/vcpkg/ports
# It's usually fine to build the above TPLs with a different compiler.
#
- name: install Enzyme (macOS w/ Enzyme)
if: matrix.enzyme && matrix.os == 'macos-latest'
run: |
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew update
brew install enzyme
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
echo "OMPI_CXX=$LLVM_PREFIX/bin/clang++" >> $GITHUB_ENV
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v2.5
+69
View File
@@ -0,0 +1,69 @@
# 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: "Sanitizer"
permissions:
actions: write
on:
push:
branches:
- master
- next
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Serial:
runs-on: ubuntu-24.04
steps:
- name: MFEM Checkout
uses: actions/checkout@v4
with:
path: mfem
- name: MFEM Build
uses: mfem/github-actions/build-mfem@v2.5
with:
os: ${{ runner.os }}
target: opt
mpi: seq
hypre-dir: unused-hypre-dir
metis-dir: unused-metis-dir
mfem-dir: mfem
build-system: make
library-only: false
config-options:
CXX="clang++-18"
CXXFLAGS="-g -O1 -std=c++11
-fsanitize=address
-fno-omit-frame-pointer
-fsanitize-address-use-after-scope"
- name: MFEM Info
working-directory: mfem
run: make info
- name: MFEM Sanitize
working-directory: mfem
run:
ASAN_OPTIONS="detect_leaks=1,
strict_init_order=1,
strict_string_checks=1,
check_initialization_order=1,
detect_stack_use_after_return=1"
make test
@@ -1,39 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-hypre
on:
workflow_call:
jobs:
build-hypre:
runs-on: ubuntu-latest
name: 2.19.0
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.HYPRE_DIR}}
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
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.5
with:
archive: ${{env.HYPRE_TGZ}}
dir: ${{env.HYPRE_DIR}}
target: int32
precision: fp64
build-system: make
@@ -1,76 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-libcxx
on:
workflow_call:
jobs:
build-llvm-libcxx:
runs-on: ubuntu-latest
strategy:
matrix:
sanitizer: [asan, msan, ubsan]
include:
- sanitizer: asan
llvm_use_sanitizer: "Address"
- sanitizer: msan
llvm_use_sanitizer: "MemoryWithOrigins"
- sanitizer: ubsan
llvm_use_sanitizer: "Undefined"
name: ${{matrix.sanitizer}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.LLVM_DIR}}
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
- name: Clone
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: >
git clone --filter=blob:none --depth=1
--branch llvmorg-${{env.LLVM_VER}}
--no-checkout https://github.com/llvm/llvm-project.git llvm-project
- name: Checkout
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: llvm-project
run: |
git sparse-checkout set --cone
git checkout llvmorg-${{env.LLVM_VER}}
git sparse-checkout set cmake llvm/cmake runtimes libcxx libcxxabi
- name: Mkdir
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: mkdir ${{env.LLVM_DIR}}
- name: CMake
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: ${{env.LLVM_DIR}}
run: >
VERBOSE=1
cmake -GNinja ../llvm-project/runtimes/
-DCMAKE_C_COMPILER=${{env.CC}}
-DCMAKE_CXX_COMPILER=${{env.CXX}}
-DCMAKE_BUILD_TYPE=RelWithDebInfo
-DCMAKE_INSTALL_PREFIX=/usr
-DLLVM_USE_SANITIZER=${{matrix.llvm_use_sanitizer}}
-DLLVM_BUILD_32_BITS=OFF
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF
-DLLVM_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_BENCHMARKS=OFF
-DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi'
- name: Build
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: ${{env.LLVM_DIR}}
run: cmake --build . -- cxx cxxabi
-38
View File
@@ -1,38 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-file-lsan
on:
workflow_call:
jobs:
build-file-lsan:
runs-on: ubuntu-latest
name: lsan.supp
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.LSAN_DIR}}
key: build-lsan-suppression-file
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
run: |
mkdir -p ${{env.LSAN_DIR}}
cat << EOF > ${{env.LSAN_DIR}}/${{env.LSAN_FILE}}
leak:libevent_core-2.1.so
leak:ompi_mpi_finalize
leak:ompi_mpi_init
leak:PMPI_Init
leak:strdup
EOF
@@ -1,36 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-metis
on:
workflow_call:
jobs:
build-metis:
runs-on: ubuntu-latest
name: 4.0.3
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.METIS_DIR}}
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
with:
archive: ${{env.METIS_TGZ}}
dir: ${{env.METIS_DIR}}
-197
View File
@@ -1,197 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: Sanitize
on:
workflow_call:
inputs:
par:
description: 'Whether to build for parallel (true/false)'
required: false
default: false
type: boolean
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
required: true
default: asan
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/mfem
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
check:
needs: [build]
runs-on: ubuntu-latest
env:
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/examples/${{env.ex}}
- name: MFEM Check
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v check
examples:
needs: [check]
runs-on: ubuntu-latest
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/examples/ex1
- name: Build Examples
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v examples
- name: Test Examples
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} examples ${{env.exclude}} --show-only
${{env.CTEST}} examples ${{env.exclude}}
miniapps:
needs: [check]
runs-on: ubuntu-latest
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/miniapps/meshing/minimal-surface
- name: Build Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v miniapps
- name: Test Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} miniapps ${{env.exclude}} --show-only
${{env.CTEST}} miniapps ${{env.exclude}}
tests-miniapps:
needs: [check]
runs-on: ubuntu-latest
env:
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/sedov_tests_cpu
- name: Build Tests Unit Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v tests/unit/all
- name: Run Tests Unit Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}} --show-only
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}}
tests-unit-build:
needs: [check]
runs-on: ubuntu-latest
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
steps:
- 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}}
- name: Build Unit Tests
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v ${{env.unit_tests}}
- name: Delete object files
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: find . -type f -name '*.o' -delete
- uses: actions/upload-artifact@v4
with:
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build/tests/unit/${{env.unit_tests}}
if-no-files-found: error
retention-days: 1
overwrite: false
tests-unit-run:
needs: [tests-unit-build]
runs-on: ubuntu-latest
strategy:
matrix:
tag: [0, 1, 2, 3]
name: tests-unit-run-${{matrix.tag}}
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
np: ${{inputs.par && '_np=2' || ''}}
steps:
- 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@v4
if: ${{steps.restore.outputs.cache-hit != 'true'}}
with:
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build/tests/unit
- name: Split Unit Tests
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: |
chmod 755 ${{env.unit_tests}}
./${{env.unit_tests}} --list-test-names-only | tail -n +2 > list-test-names
shuf list-test-names -o list-test-names
split --verbose -n l/4 -d -a 1 list-test-names list-test-names-
- name: Cat Unit Tests ${{matrix.tag}}
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: cat list-test-names-${{matrix.tag}}
- name: Run Unit Tests ${{matrix.tag}}
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}" --show-only
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}"
-73
View File
@@ -1,73 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: Sanitizers
permissions:
actions: write
on:
push:
branches: ["master", "next"]
pull_request:
workflow_dispatch:
concurrency:
group: ${{github.workflow}}-${{github.ref}}
cancel-in-progress: true
jobs:
# Build steps for dependencies
build-hypre:
uses: ./.github/workflows/sanitize-build-hypre.yml
build-metis:
uses: ./.github/workflows/sanitize-build-metis.yml
build-lsan:
uses: ./.github/workflows/sanitize-build-lsan.yml
build-libcxx:
uses: ./.github/workflows/sanitize-build-libcxx.yml
# Serial sanitizers: asan, msan, ubsan
seq-asan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: asan
seq-msan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: msan
seq-ubsan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: ubsan
# Parallel sanitizers: asan, ubsan
par-asan:
needs: [build-libcxx, build-hypre, build-metis]
uses: ./.github/workflows/sanitize-tests.yml
with:
par: true
sanitizer: asan
par-ubsan:
needs: [build-libcxx, build-hypre, build-metis]
uses: ./.github/workflows/sanitize-tests.yml
with:
par: true
sanitizer: ubsan
-44
View File
@@ -29,47 +29,3 @@ jobs:
operations-per-run: 500
exempt-issue-labels: "bug,WIP,ready-for-review,in-review,in-next"
exempt-pr-labels: "bug,WIP,ready-for-review,in-review,in-next"
# Stale action for PRs with "in-review" label.
stale-in-review-pr:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
actions: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: ':warning: This PR has been automatically marked as stale because it has not had any activity in the last 150 days. *If no activity occurs in the next 30 days, it will be automatically closed.* Thank you for your contributions.'
only-pr-labels: "in-review"
days-before-pr-stale: 150
days-before-pr-close: 30
days-before-issue-stale: -1
days-before-issue-close: -1
stale-pr-label: 'stale'
operations-per-run: 500
# Stale action for PRs with "WIP" label.
stale-wip-pr:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
actions: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: ':warning: This PR has been automatically marked as stale because it has not had any activity in the last 300 days. *If no activity occurs in the next 30 days, it will be automatically closed.* Thank you for your contributions.'
only-pr-labels: "WIP"
days-before-pr-stale: 300
days-before-pr-close: 30
days-before-issue-stale: -1
days-before-issue-close: -1
stale-pr-label: 'stale'
operations-per-run: 500
+6 -21
View File
@@ -201,20 +201,14 @@ examples/superlu/sol.*
miniapps/adjoint/cvsRoberts_ASAi_dns
miniapps/adjoint/adjoint_advection_diffusion
miniapps/dfem/dfem-minimal-surface
miniapps/dfem/dfem-minimal-surface-output
miniapps/electromagnetics/volta
miniapps/electromagnetics/tesla
miniapps/electromagnetics/maxwell
miniapps/electromagnetics/joule
miniapps/electromagnetics/lorentz
miniapps/electromagnetics/Volta-AMR*
miniapps/electromagnetics/Tesla-AMR*
miniapps/electromagnetics/Maxwell-Parallel*
miniapps/electromagnetics/Joule_[0-9]*
miniapps/electromagnetics/Lorentz_[0-9]*
miniapps/electromagnetics/Lorentz.dat
miniapps/electromagnetics/Joule_*
miniapps/gslib/field-diff
miniapps/gslib/field-interp
@@ -235,7 +229,6 @@ miniapps/meshing/fit-node-position
miniapps/meshing/trimmer
miniapps/meshing/reflector
miniapps/meshing/ref321
miniapps/meshing/mesh-bounding-boxes
miniapps/meshing/mesh-optimizer
miniapps/meshing/pmesh-optimizer
miniapps/meshing/pmesh-fitting
@@ -266,13 +259,11 @@ miniapps/meshing/mesh.*
miniapps/meshing/order.*
miniapps/meshing/sol.*
miniapps/meshing/refined.mesh
miniapps/meshing/bounding-box*
miniapps/meshing/jacobian-determinant*
miniapps/mtop/parheat
miniapps/mtop/ParHeat/*
miniapps/mtop/ParHeat*
miniapps/mtop/seqheat
miniapps/mtop/SeqHeat/*
miniapps/mtop/SeqHeat*
miniapps/autodiff/paradiff
miniapps/autodiff/seqadiff
@@ -280,7 +271,7 @@ miniapps/autodiff/seqtest
miniapps/autodiff/par_example
miniapps/autodiff/seq_example
miniapps/autodiff/seq_test
miniapps/autodiff/Example/*
miniapps/autodiff/Exampl*
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
@@ -303,7 +294,6 @@ miniapps/nurbs/nurbs_solenoidal
miniapps/nurbs/nurbs_printfunc
miniapps/nurbs/nurbs_patch_ex1
miniapps/nurbs/nurbs_curveint
miniapps/nurbs/nurbs_surface
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol_?.gf
@@ -322,7 +312,6 @@ miniapps/nurbs/nurbs_naca_cmesh
miniapps/nurbs/naca-cmesh.mesh
miniapps/nurbs/glvis_naca-cmesh.mesh
miniapps/nurbs/Naca_cmesh
miniapps/nurbs/*-Surface.mesh
miniapps/performance/ex1
miniapps/performance/ex1p
@@ -344,7 +333,6 @@ miniapps/shifted/lsf_integral
miniapps/tools/display-basis
miniapps/tools/load-dc
miniapps/tools/convert-dc
miniapps/tools/gridfunction-bounds
miniapps/tools/lor-transfer
miniapps/tools/plor-transfer
miniapps/tools/get-values
@@ -411,15 +399,12 @@ miniapps/spde/ParaView
miniapps/tribol/contact-patch-test
miniapps/diag-smoothers/abs-l1-jacobi
miniapps/diag-smoothers/mg-abs-l1-jacobi
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
tests/unit/punit_tests
tests/unit/gpu_unit_tests
tests/unit/pgpu_unit_tests
tests/unit/cunit_tests
tests/unit/pcunit_tests
tests/unit/sedov_tests_*
tests/unit/psedov_tests_*
tests/unit/tmop_pa_tests_*
+5 -5
View File
@@ -22,7 +22,7 @@ include:
# the "needs" keyword and express the DAG of jobs for more efficiency.
# - We use setup and setup_baseline phases to download content outside of mfem
# directory.
# - Allocate/Release is where Dane resource are allocated/released once for all.
# - Allocate/Release is where ruby resource are allocated/released once for all.
# - Build and Test is where we build and MFEM for multiple toolchains.
# - Baseline_checks gathers baseline-type test suites execution
# - Baseline_publish, only available on master, allows to update baseline
@@ -53,7 +53,7 @@ variables:
AUTOTEST_COMMIT: "YES"
# Trigger subpipelines:
dane-build-and-test:
ruby-build-and-test:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
@@ -61,10 +61,10 @@ dane-build-and-test:
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-build-and-test.yml
include: .gitlab/ruby-build-and-test.yml
strategy: depend
dane-baseline:
ruby-baseline:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
@@ -73,7 +73,7 @@ dane-baseline:
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-baseline.yml
include: .gitlab/ruby-baseline.yml
strategy: depend
lassen-build-and-test:
+3 -3
View File
@@ -24,7 +24,7 @@ and `test type`.
Machines typically include:
* Dane: Intel Sapphire Rapids
* Ruby: 2nd Gen Intel Xeon (Cascade Lake)
* Lassen: Power9 + Nvidia GPU
* Corona: AMD GPU
@@ -76,13 +76,13 @@ with a spack spec of MFEM, within the limits permitted by the MFEM spack
package.
In any build-and-test sub-pipeline a job basically consists in defining the
spack spec to use. Adding a job on Dane for example resumes to:
spack spec to use. Adding a job on ruby for example resumes to:
```yaml
<job_name>:
variables:
SPEC: "<spack_spec>"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
```
The remaining and non trivial work is to make sure this spec is working. To
+1 -1
View File
@@ -24,7 +24,7 @@ variables:
# TODO: add a clean-up mechanism
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
# On LLNL's Dane, there is only one allocation shared among jobs in order to
# On LLNL's ruby, there is only one allocation shared among jobs in order to
# save time and resource. This allocation has to be uniquely named so that we
# are sure to retrieve it.
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
@@ -9,17 +9,17 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# GitLab pipelines configurations for the Dane machine at LLNL
# GitLab pipelines configurations for the Ruby machine at LLNL
variables:
MACHINE_NAME: dane
MACHINE_NAME: ruby
.on_dane:
.on_ruby:
tags:
- shell
- dane
- ruby
rules:
# Don't run dane jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_DANE == "OFF"'
# Don't run ruby jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_RUBY == "OFF"'
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
@@ -40,17 +40,16 @@ variables:
- when: on_success
# Spack helped builds
# Generic dane build job, extending build script
.build_and_test_on_dane:
extends: [.on_dane]
# Generic ruby build job, extending build script
.build_and_test_on_ruby:
extends: [.on_ruby]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
# Dane has 224 threads/node and we run 7 separate jobs: 224=7*32
- export THREADS=28
- export THREADS=16
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 60 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
+1 -1
View File
@@ -18,7 +18,7 @@
setup_baseline:
tags:
- shell
- dane
- ruby
stage: setup
variables:
GIT_STRATEGY: none
+1 -1
View File
@@ -16,7 +16,7 @@
setup:
tags:
- shell
- dane
- ruby
stage: setup
variables:
GIT_STRATEGY: none
@@ -19,8 +19,8 @@ stages:
- cleanup
- baseline_publish
baselinecheck_mfem_intel_dane:
extends: [.on_dane]
baselinecheck_mfem_intel_ruby:
extends: [.on_ruby]
stage: baseline_check
variables:
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
@@ -31,8 +31,8 @@ baselinecheck_mfem_intel_dane:
script:
- echo ${BUILD_ROOT}
- echo ${TPLS_DIR}
# Used by the tests in MFEM/tests, dane has 224 threads/node:
- export MFEM_TEST_NP=192
# Used by the tests in MFEM/tests:
- export MFEM_TEST_NP=48
# The next script uses the following environment variables:
# * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR,
# * BUILD_ROOT, TPLS_DIR, MACHINE_NAME
@@ -44,7 +44,7 @@ baselinecheck_mfem_intel_dane:
allow_failure: true
cleanup:
extends: .on_dane
extends: .on_ruby
stage: cleanup
variables:
GIT_STRATEGY: none
@@ -53,7 +53,7 @@ cleanup:
- rm -rf "${BUILD_ROOT}" || true
report_baseline:
extends: [.on_dane]
extends: [.on_ruby]
stage: baseline_report
script:
- echo ${MACHINE_NAME}
@@ -113,8 +113,8 @@ report_baseline:
exit $err
) 9> autotest.lock
baselinepublish_mfem_dane:
extends: [.on_dane]
baselinepublish_mfem_ruby:
extends: [.on_ruby]
stage: baseline_publish
rules:
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
@@ -129,5 +129,5 @@ baselinepublish_mfem_dane:
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/dane-config.yml
- local: .gitlab/configs/ruby-config.yml
- local: .gitlab/configs/setup-baseline.yml
@@ -19,54 +19,54 @@ stages:
allocate_resource:
variables:
GIT_STRATEGY: none
extends: .on_dane
extends: .on_ruby
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --reservation=ci --time=60 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
# GitLab jobs for the Dane machine at LLNL
# GitLab jobs for the Ruby machine at LLNL
debug_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug~mpi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
debug_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug+mpi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 ~mpi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10_sundials:
variables:
SPEC: "%gcc@10.3.1 +sundials"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10_petsc:
variables:
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10_pumi:
variables:
SPEC: "%gcc@10.3.1 +pumi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
# Release
release_resource:
variables:
GIT_STRATEGY: none
extends: .on_dane
extends: .on_ruby
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
@@ -78,17 +78,17 @@ release_resource:
report_job_success:
stage: release_resource_and_report
extends:
- .on_dane
- .on_ruby
- .report_job_success
report_job_failure:
stage: release_resource_and_report
extends:
- .on_dane
- .on_ruby
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/dane-config.yml
- local: .gitlab/configs/ruby-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+2 -2
View File
@@ -14,7 +14,7 @@
# locals
glob_err=${BASELINE_TEST}.err
base=${BASELINE_TEST}-${SYS_TYPE}
if [[ "${MACHINE_NAME}" == "dane" ]]; then
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
base="${BASELINE_TEST}-${MACHINE_NAME}"
fi
base_diff=${base}.diff
@@ -31,7 +31,7 @@ cd tests
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
# run
if [[ "${MACHINE_NAME}" == "dane" ]]; then
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "corona" ]]; then
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
+2 -2
View File
@@ -11,7 +11,7 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# There will be collision between corona and dane baselines.
# There will be collision between corona and ruby baselines.
# Once the corresponding files have been generated, we can switch to machine
# specific ref.
ARTIFACT_PATH=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}
@@ -21,7 +21,7 @@ PATCH_FILE=${ARTIFACT_PATH}.patch
FULL_FILE=${ARTIFACT_PATH}.out
DIFF_FILE=${ARTIFACT_PATH}.diff
# There will be collision between corona and dane baselines.
# There will be collision between corona and ruby baselines.
# Once the corresponding files have been generated, we can switch to machine
# specific ref.
SAVED_NAME=baseline-${SYS_TYPE}.saved
-114
View File
@@ -11,120 +11,6 @@
Version 4.8.1 (development)
===========================
Starting with this version, MFEM requires a C++17 compiler.
Discretization improvements
---------------------------
- Introduced dFEM: a new MFEM capability for Automatic Differentiation (AD) of
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.
- In the ParMoonolith integration, added support for variational resampling of
H1 vector fields.
- 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
--------------------
- 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.
GPU computing
-------------
- 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.
- 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.
- Added GPU support in GradientGridFunctionCoefficient and
InnerProductCoefficient by implementing their Project methods.
New and updated examples and miniapps
-------------------------------------
- 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
the Jacobian of the transformation.
- Added a new miniapp (tools/gridfunction-bounds) to compute piecewise linear
bounds on a given high-order grid function.
- Added a new miniapp (electromagnetics/lorentz) which computes the trajectory
of a charged particle, subject to Lorentz forces, in electrostatic and/or
magnetostatic fields as computed by the volta or tesla miniapps.
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`.
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
====================================
+18 -58
View File
@@ -18,8 +18,8 @@ message(STATUS "CMake version: ${CMAKE_VERSION}")
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
"Path to optional user configuration file.")
# Require C++17 and disable compiler-specific extensions
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to use.")
# Require C++11 and disable compiler-specific extensions
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to use.")
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL
"Force the use of the chosen C++ standard.")
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Enable C++ standard extensions.")
@@ -133,6 +133,7 @@ if (MFEM_USE_CUDA)
if (NOT CMAKE_CUDA_HOST_COMPILER)
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
endif()
set(CUDA_FLAGS "--expt-extended-lambda")
if (CMAKE_VERSION VERSION_LESS 3.18.0)
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
elseif (NOT CMAKE_CUDA_ARCHITECTURES)
@@ -147,20 +148,6 @@ if (MFEM_USE_CUDA)
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")
endif()
else()
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.")
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
@@ -269,20 +256,11 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
if (OPENMP_FOUND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
if (MFEM_USE_CUDA)
if(MFEM_CUDA_COMPILER_IS_NVCC)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
else()
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${OpenMP_CXX_FLAGS}")
endif()
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
endif()
endif()
endif()
# Umpire (must be included before hypre, so hypre can use it if needed)
if (MFEM_USE_UMPIRE)
find_package(UMPIRE REQUIRED)
endif()
# MPI -> hypre; PETSc (optional)
if (MFEM_USE_MPI)
find_package(MPI REQUIRED)
@@ -410,11 +388,6 @@ if (MFEM_USE_GSLIB)
find_package(GSLIB REQUIRED)
endif()
# HDF5
if (MFEM_USE_HDF5)
find_package(HDF5 REQUIRED)
endif()
# NetCDF
if (MFEM_USE_NETCDF)
find_package(NetCDF REQUIRED)
@@ -500,13 +473,14 @@ endif()
# RAJA
if (MFEM_USE_RAJA)
# RAJA 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(RAJA REQUIRED)
endif()
# UMPIRE
if (MFEM_USE_UMPIRE)
find_package(UMPIRE REQUIRED)
endif()
# GOOGLE-BENCHMARK
if (MFEM_USE_BENCHMARK)
find_package(Benchmark REQUIRED)
@@ -553,10 +527,9 @@ if (MFEM_USE_TRIBOL)
endif()
endif()
# Enzyme
if (MFEM_USE_ENZYME)
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
find_package(ENZYME REQUIRED)
endif()
# MFEM_TIMER_TYPE
@@ -596,29 +569,22 @@ find_package(Threads REQUIRED)
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
# be before SuiteSparse.
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
ALGOIM ENZYME CUDA::cudart)
ALGOIM ENZYME)
# Add all created targets and *_FOUND libraries in the variables TPL_TARGETS and
# TPL_LIBRARIES, respectively.
set(TPL_TARGETS)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
foreach(TPL IN LISTS MFEM_TPLS)
if (${TPL}_FOUND OR TARGET ${TPL})
if (${TPL}_FOUND)
message(STATUS "MFEM: using package ${TPL}")
if (TARGET ${TPL})
list(APPEND TPL_TARGETS ${TPL})
else()
list(APPEND TPL_LIBRARIES ${${TPL}_LIBRARIES})
list(APPEND TPL_INCLUDE_DIRS ${${TPL}_INCLUDE_DIRS})
endif()
list(APPEND TPL_LIBRARIES ${${TPL}_LIBRARIES})
list(APPEND TPL_INCLUDE_DIRS ${${TPL}_INCLUDE_DIRS})
endif()
endforeach(TPL)
list(REVERSE TPL_LIBRARIES)
list(REMOVE_DUPLICATES TPL_LIBRARIES)
list(REVERSE TPL_LIBRARIES)
@@ -691,10 +657,7 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
# Declaring the library
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
if (TPL_TARGETS)
add_dependencies(mfem ${TPL_TARGETS})
endif()
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
if (MINGW)
target_link_libraries(mfem PRIVATE ws2_32)
endif()
@@ -718,9 +681,6 @@ if (MFEM_USE_MPI)
target_link_libraries(mfem PUBLIC ${MPI_CXX_LINK_FLAGS})
endif()
endif()
if (MFEM_USE_ENZYME)
target_link_libraries(mfem PUBLIC ClangEnzymeFlags)
endif()
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
+1 -5
View File
@@ -120,7 +120,6 @@ The MFEM source code has the following structure:
| └── superlu
├── fem
│ ├── ceed
│ ├── dfem
│ ├── eltrans
│ ├── fe
│ ├── gslib
@@ -139,7 +138,6 @@ The MFEM source code has the following structure:
│ ├── adjoint
│ ├── autodiff
│ ├── common
│ ├── dfem
│ ├── dpg
│ ├── electromagnetics
│ ├── gslib
@@ -551,8 +549,6 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Add a short description of the example in the "Extensive Examples" section of `features.md`.
- [ ] New miniapps:
- [ ] All sample runs at the top of the miniapp source file work.
- [ ] Add to internal testing repo, if sample runs should be included in nightly tests [internally](#tests-at-llnl).
- [ ] Exclude long sample runs from automated testing, with `* ` (one space) before the command.
- [ ] Update top-level `makefile` and `makefile` in corresponding miniapp directory.
- [ ] Add the miniapp binary and any files generated by it to the top-level `.gitignore` file.
- [ ] Update CMake build system:
@@ -747,7 +743,7 @@ and debug build is performed with a simple run of `ex1` to verify the executable
- We mirror the `master` and `next` branches internally (to `gh-master` and
`gh-next`) and run longer nightly tests via cron. On the weekends, a more
extensive test is run which extracts and executes all the different sample
runs from each example and most miniapps.
runs from each example.
- We also mirror PRs on the LLNL GitLab instance. PR mirroring can only be
triggered by _LLNL developers_, but test status is publicly available. Only
+9 -104
View File
@@ -121,11 +121,6 @@ Parallel build:
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-buil-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DFETCH_TPLS=YES
make -j 4
CUDA build:
(this build requires CMake 3.17 or newer)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
@@ -268,7 +263,7 @@ See the configuration file config/defaults.mk for the default settings.
Compilers:
CXX - C++ compiler, serial build
MPICXX - MPI C++ compiler, parallel build
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
CUDA_CXX - The CUDA compiler, 'nvcc'
Compiler options:
OPTIM_FLAGS - Options for optimized build
@@ -428,10 +423,6 @@ MFEM_USE_GNUTLS = YES/NO
When MFEM_USE_GNUTLS is enabled, the additional build options, GNUTLS_*, are
also used, see below.
MFEM_USE_HDF5 = YES/NO
The HDF5 library is used for input and output of HDF5 files, for example
Cubit mesh files or VTKHDF files for ParaView.
MFEM_USE_NETCDF = YES/NO
NetCDF is the library that is used by the SNL Cubit mesh generator to create
Genesis mesh files. This option enables a reader for these files, which
@@ -613,12 +604,11 @@ MFEM_USE_TRIBOL = YES/NO
MFEM_USE_ENZYME = YES/NO
Enables automatic differentiation support through the LLVM plugin Enzyme.
This requires the compiler to be set to clang (>=14.0.0). We also advise the
use of the link time optimization (LTO) plugin, so functions defined over
multiple files (compilation units) can be differentiated automatically. This
requires to also use LLVM/LLD for linking. The recommended options are in
config/defaults.mk. For more detailed instructions, see the section "Specific
options for Enzyme" below.
This requires the compiler to be set to clang (>=14.0.0). We also advise to
use the link time optimization (LTO) plugin, to enable functions that you
define over multiple files (compilation units) and want to be differentiated
automatically, to work. This requires to also use LLVM/LLD for linking.
Recommended options are in config/defaults.mk.
MFEM_BUILD_TAG = (any value)
An optional tag to characterize the build. Exported to config/config.mk.
@@ -668,7 +658,6 @@ The specific libraries and their options are:
- OpenMP (optional), usually part of compiler, used when either MFEM_USE_OPENMP
or MFEM_USE_LEGACY_OPENMP is set to YES.
Options: OPENMP_OPT, OPENMP_LIB.
Versions: OpenMP >= 3.1 when MFEM_USE_OPENMP=YES.
- High-resolution POSIX clocks: when using MFEM_TIMER_TYPE = 2, it may be
necessary to link with a system library (e.g. librt.so).
@@ -744,9 +733,6 @@ The specific libraries and their options are:
Options: GNUTLS_OPT, GNUTLS_LIB.
Versions: GnuTLS >= 2.12.0, older versions may work too.
- HDF5 (optional), used when MFEM_USE_HDF5 = YES, required for reading and
writing files in VTKHDF format.
- NetCDF (optional), used when MFEM_USE_NETCDF = YES, required for reading Cubit
mesh files. Also requires installation of HDF5 and ZLIB, as explained at the
NetCDF web site. Note that we use the plain vanilla "C" version of NetCDF, you
@@ -842,13 +828,12 @@ The specific libraries and their options are:
- CUDA (optional), used when MFEM_USE_CUDA = YES.
URL: https://developer.nvidia.com/cuda-toolkit
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB, CUDA_DIR (when CUDA_CXX=clang++).
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
Versions: CUDA >= 10.1.168.
- HIP (optional), used when MFEM_USE_HIP = YES.
URL: https://rocmdocs.amd.com
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
Versions: ROCm >= 5.6.1.
- OCCA (optional), used when MFEM_USE_OCCA = YES.
URL: https://libocca.org
@@ -859,7 +844,7 @@ The specific libraries and their options are:
URL: https://github.com/CEED/libCEED
https://ceed.exascaleproject.org/libceed
Options: CEED_DIR, CEED_OPT, CEED_LIB.
Versions: libCEED >= 0.12.0.
Versions: libCEED >= 0.12.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.5.1, only RAJA v2022.10.3+ is supported.
@@ -919,7 +904,7 @@ The specific libraries and their options are:
- Enzyme, used when MFEM_USE_ENZYME = YES. Requires LLVM/Clang >= 14.0.0.
URL: https://github.com/EnzymeAD/Enzyme
Options: ENZYME_DIR, ENZYME_OPT, ENZYME_LIB.
Versions: Enzyme >= v0.0.176.
Versions: Enzyme >= v0.0.33.
Building with CMake
@@ -1053,7 +1038,6 @@ MFEM_USE_STRUMPACK
MFEM_USE_GINKGO
MFEM_USE_AMGX
MFEM_USE_GNUTLS
MFEM_USE_HDF5
MFEM_USE_NETCDF
MFEM_USE_MPFR
MFEM_USE_ZLIB
@@ -1081,9 +1065,6 @@ 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.
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):
---------------------------
@@ -1145,12 +1126,6 @@ The following built-in CMake packages are also used:
set the <LIBNAME>_LIBRARIES option directly; the configuration option
<LIBNAME>_DIR is not supported.
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.
- HYPRE
- METIS
Building without GNU make or CMake
==================================
@@ -1207,73 +1182,3 @@ the older HIP C++ library build/linkage. To ensure proper build and linkage
check that `CMAKE_CXX_COMPILER` and `CMAKE_HIP_COMPILER` are set to the same
compiler. This is especially important when using an MPI compiler (for example
crayCC) where some linker flags may get dropped if these two are not identical.
Specific options for Enzyme
===========================
To work properly, MFEM and Enzyme need to use the same LLVM/Clang configuration.
For example, on macOS this can be done by using Homebrew: first install Enzyme,
which in turn installs LLVM as a dependency (as of May 2025, this is LLVM 19):
brew install enzyme
In order to ensure the correct compiler choice for the MFEM makefile build, set
CXX = $(shell brew --prefix llvm@19)/bin/clang++
in the user.mk file (adapted from config/defaults.mk, see the section "Building
with GNU make" above). With MPI, it is convenient to set
MPICXX = OMPI_CXX=$(CXX) mpicxx
for OpenMPI and
MPICXX = MPICH_CXX=$(CXX) mpicxx
for MPICH.
Additionally, the Enzyme directory needs to be set in user.mk as follows:
ENZYME_DIR = $(shell brew --prefix enzyme)
Specifically, a full build on a Mac can be tested by adding the following
user.mk file in the config/ directory
MFEM_USE_ENZYME = YES
ENZYME_DIR = $(shell brew --prefix enzyme)
LLVM_DIR = $(shell brew --prefix llvm@19)
CXX = $(LLVM_DIR)/bin/clang++
MFEM_USE_MPI = YES
MPICXX = OMPI_CXX=$(CXX) mpicxx
and running
make config
make -j
cd miniapps/dfem
make
./dfem-minimal-surface
On Linux systems, for example Ubuntu 24.04, use the package manager to install
the Enzyme dependencies
sudo apt install libclang-dev libzstd-dev llvm-dev clang
and then clone and build Enzyme
cd $HOME
git clone https://github.com/EnzymeAD/Enzyme.git
cd Enzyme/enzyme && mkdir build && cd build
CC=clang CXX=clang++ cmake .. -DLLVM_DIR=/usr/lib/llvm-18/lib/cmake -DCMAKE_INSTALL_PREFIX=$HOME/Enzyme/enzyme/build
make -j
make install
From here, one can proceed in the same way using the following user.mk settings
MFEM_USE_ENZYME = YES
ENZYME_DIR = $(HOME)/Enzyme/enzyme/build
CXX = clang++
MFEM_USE_MPI = YES
MPICXX = OMPI_CXX=$(CXX) mpicxx
On other Linux systems the LLVM packages may have different names, for example
on RHEL9, one needs to "sudo yum install llvm-devel libzstd clang-devel".
-26
View File
@@ -41,7 +41,6 @@ set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
set(MFEM_USE_HIOP @MFEM_USE_HIOP@)
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
set(MFEM_USE_HDF5 @MFEM_USE_HDF5@)
set(MFEM_USE_NETCDF @MFEM_USE_NETCDF@)
set(MFEM_USE_PETSC @MFEM_USE_PETSC@)
set(MFEM_USE_SLEPC @MFEM_USE_SLEPC@)
@@ -84,31 +83,6 @@ set_and_check(MFEM_LIBRARY_DIR "@PACKAGE_LIB_INSTALL_DIR@")
check_required_components(MFEM)
include(CMakeFindDependencyMacro)
if (MFEM_USE_CUDA)
# required for projects linking to MFEM+CUDA, even if they don't use CUDA directly
find_dependency(CUDAToolkit)
endif (MFEM_USE_CUDA)
if (MFEM_USE_HIP)
# hip/rocm uses the modern MFEM way of linking to targets, need to find dependencies
find_dependency(HIP)
find_dependency(HIPBLAS)
find_dependency(HIPSPARSE)
if (MFEM_USE_MPI)
# assume HYPRE uses HIP
# alternatively could check HYPRE_USING_HIP
find_dependency(rocsparse)
find_dependency(rocrand)
find_dependency(rocsolver)
endif (MFEM_USE_MPI)
endif (MFEM_USE_HIP)
if (MFEM_USE_RAJA)
find_dependency(RAJA)
endif()
if (NOT TARGET mfem)
include(${CMAKE_CURRENT_LIST_DIR}/MFEMTargets.cmake)
endif (NOT TARGET mfem)
-3
View File
@@ -132,9 +132,6 @@
// Enable Conduit support.
#cmakedefine MFEM_USE_CONDUIT
// Enable functionality based on the HDF5 library (reading VTKHDF files).
#cmakedefine MFEM_USE_HDF5
// Enable functionality based on the NetCDF library (reading CUBIT files).
#cmakedefine MFEM_USE_NETCDF
+27
View File
@@ -0,0 +1,27 @@
# 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.
message(STATUS "Looking for ENZYME ...")
message(STATUS " in ENZYME_DIR = ${ENZYME_DIR}")
# Make sure the directory and version combination works. Do nothing otherwise.
if(EXISTS "${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
message(STATUS "Found ENZYME: ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
# Set ENZYME_FOUND
set(ENZYME_FOUND TRUE CACHE BOOL "ENZYME was found." FORCE)
# Set CXX flags to accommodate the Enzyme Clang plugin
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -load -Xclang ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so -mllvm -enzyme-loose-types=1")
set(MFEM_USE_ENZYME YES)
else()
endif()
+4 -99
View File
@@ -9,25 +9,21 @@
# 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:
# - HYPRE_FOUND
# - HYPRE_LIBRARIES
# - HYPRE_INCLUDE_DIRS
# - HYPRE_VERSION
# - HYPRE_USING_CUDA (internal)
# - HYPRE_USING_HIP (internal)
# otherwise, the following are defined:
# - HYPRE (imported library target)
# - HYPRE_VERSION (cache variable)
if (HYPRE_FOUND OR TARGET HYPRE)
if (HYPRE_FOUND)
if (HYPRE_USING_CUDA)
find_package(CUDAToolkit REQUIRED)
endif()
if (HYPRE_USING_HIP)
find_package(rocsparse REQUIRED)
find_package(rocrand REQUIRED)
find_package(rocsolver REQUIRED)
endif()
if (HYPRE_LIBRARIES AND HYPRE_INCLUDE_DIRS AND HYPRE_VERSION)
find_package_handle_standard_args(HYPRE
@@ -37,95 +33,6 @@ if (HYPRE_FOUND OR TARGET HYPRE)
endif()
endif()
if (HYPRE_FETCH OR FETCH_TPLS)
# Collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
set(HYPRE_CMAKE_OPTIONS "")
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()
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)
target_link_libraries(HYPRE INTERFACE CUDA::cusparse CUDA::curand CUDA::cublas)
elseif (MFEM_USE_HIP)
list(APPEND HYPRE_CMAKE_OPTIONS -DHYPRE_ENABLE_HIP:BOOL=ON)
find_package(rocsparse REQUIRED)
find_package(rocrand REQUIRED)
target_link_libraries(HYPRE INTERFACE rocsparse rocrand)
endif()
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
if (MFEM_USE_UMPIRE)
if (EXISTS ${umpire_DIR})
list(APPEND HYPRE_CMAKE_OPTIONS -DHYPRE_ENABLE_UMPIRE:BOOL=ON -Dumpire_DIR:PATH=${umpire_DIR})
else()
message(FATAL_ERROR "MFEM_USE_UMPIRE=ON, however umpire_DIR isn't visible to HYPRE")
endif()
else()
list(APPEND HYPRE_CMAKE_OPTIONS -DHYPRE_ENABLE_UMPIRE:BOOL=OFF)
message(WARNING
"================================================================================
Umpire is disabled while building HYPRE with GPU support.
This is not recommended for performance reasons!
Consider enabling Umpire with -DMFEM_USE_UMPIRE=ON and providing -DUMPIRE_DIR.
================================================================================")
endif()
endif()
if (MFEM_USE_SINGLE)
list(APPEND HYPRE_CMAKE_OPTIONS -DHYPRE_ENABLE_SINGLE:BOOL=ON)
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 HYPRE ${HYPRE_FETCH_TAG} to be built with ${HYPRE_CMAKE_OPTIONS}")
set(HYPRE_INSTALL ${CMAKE_BINARY_DIR}/fetch/hypre)
include(ExternalProject)
ExternalProject_Add(hypre
GIT_REPOSITORY https://github.com/hypre-space/hypre.git
GIT_TAG ${HYPRE_FETCH_TAG}
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
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
add_dependencies(HYPRE hypre)
set_target_properties(HYPRE PROPERTIES
IMPORTED_LOCATION ${HYPRE_INSTALL}/lib/libHYPRE.a
INTERFACE_INCLUDE_DIRECTORIES ${HYPRE_INSTALL}/include)
# convert HYPRE version to integer
if (HYPRE_FETCH_TAG MATCHES "^v?([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
# Exact release tag X.Y.Z
string(REGEX MATCHALL "[0-9]+" HYPRE_SPLIT_VERSION "${HYPRE_FETCH_TAG}")
elseif (HYPRE_FETCH_VERSION MATCHES "([0-9]+)\\.([0-9]+)(\\.([0-9]+))?")
string(REGEX MATCHALL "[0-9]+" HYPRE_SPLIT_VERSION "${HYPRE_FETCH_VERSION}")
else (NOT DEFINED HYPRE_VERSION)
message(FATAL_ERROR "Unable to find HYPRE release version. Please provide it via -DHYPRE_VERSION")
endif()
if (HYPRE_SPLIT_VERSION AND NOT DEFINED HYPRE_VERSION)
list(GET HYPRE_SPLIT_VERSION 0 HYPRE_MAJOR_VERSION)
list(GET HYPRE_SPLIT_VERSION 1 HYPRE_MINOR_VERSION)
if (HYPRE_SPLIT_VERSION GREATER 2)
list(GET HYPRE_SPLIT_VERSION 2 HYPRE_PATCH_VERSION)
else()
set(HYPRE_PATCH_VERSION 0)
endif()
math(EXPR HYPRE_VERSION "10000*${HYPRE_MAJOR_VERSION} + 100*${HYPRE_MINOR_VERSION} + ${HYPRE_PATCH_VERSION}")
set(HYPRE_VERSION ${HYPRE_VERSION} CACHE STRING "HYPRE version." FORCE)
endif()
return()
endif()
include(MfemCmakeUtilities)
mfem_find_package(HYPRE HYPRE HYPRE_DIR "include" "HYPRE.h" "lib" "HYPRE"
"Paths to headers required by HYPRE." "Libraries required by HYPRE."
@@ -179,9 +86,8 @@ if (HYPRE_FOUND AND HYPRE_USING_CUDA)
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CURAND curand)
mfem_culib_set_libraries(CUBLAS cublas)
mfem_culib_set_libraries(CUSOLVER cusolver)
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
${CUBLAS_LIBRARIES} ${CUSOLVER_LIBRARIES})
${CUBLAS_LIBRARIES})
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
"HYPRE libraries + dependencies." FORCE)
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
@@ -190,8 +96,7 @@ endif()
if (HYPRE_FOUND AND HYPRE_USING_HIP)
find_package(rocsparse REQUIRED)
find_package(rocrand REQUIRED)
find_package(rocsolver REQUIRED)
list(APPEND HYPRE_LIBRARIES ${rocsparse_LIBRARIES} ${rocrand_LIBRARIES} roc::rocsolver roc::rocblas)
list(APPEND HYPRE_LIBRARIES ${rocsparse_LIBRARIES} ${rocrand_LIBRARIES})
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
"HYPRE libraries + dependencies." FORCE)
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
+1 -30
View File
@@ -9,39 +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:
# - METIS_FOUND
# - METIS_LIBRARIES
# - METIS_INCLUDE_DIRS
# - METIS_VERSION_5
# otherwise, the following are defined:
# - METIS (imported library target)
# - METIS_VERSION_5 (cache variable)
if (METIS_FETCH OR FETCH_TPLS)
set(METIS_FETCH_VERSION 4.0.3)
add_library(METIS STATIC IMPORTED)
# define external project
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
GIT_REPOSITORY https://github.com/mfem/tpls
GIT_TAG b60352fbe9675d374b00828055e55be4584c7995 # tag from 1/16/25
GIT_SHALLOW TRUE
UPDATE_DISCONNECTED TRUE
PREFIX ${PREFIX}
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
BUILD_COMMAND $(MAKE) COPTIONS=-Wno-incompatible-pointer-types
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
# set imported library target properties
add_dependencies(METIS metis)
set_target_properties(METIS PROPERTIES
IMPORTED_LOCATION ${PREFIX}/lib/libmetis.a)
# set cache variables that would otherwise be set after mfem_find_package call
set(METIS_VERSION_5 FALSE CACHE BOOL "Is METIS version 5?")
return()
endif()
include(MfemCmakeUtilities)
mfem_find_package(METIS METIS METIS_DIR "include;Lib" "metis.h"
+5 -10
View File
@@ -125,9 +125,7 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
if (MFEM_USE_CUDA)
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST}
PROPERTIES LANGUAGE CUDA)
if (MFEM_CUDA_COMPILER_IS_NVCC)
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
endif()
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
endif()
# Actually add the executable
@@ -718,7 +716,7 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
get_target_property(IsImported ${tgt} IMPORTED)
# message(STATUS "${tgt}[IMPORTED]: ${IsImported}")
# Generally, the possible target types are: STATIC_LIBRARY, MODULE_LIBRARY,
# SHARED_LIBRARY, INTERFACE_LIBRARY, UNKNOWN_LIBRARY, EXECUTABLE.
# SHARED_LIBRARY, INTERFACE_LIBRARY, EXECUTABLE.
get_target_property(type ${tgt} TYPE)
# message(STATUS "${tgt}[TYPE]: ${type}")
unset(ImportConfig)
@@ -766,7 +764,7 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
else()
message(STATUS " *** Warning: [${tgt}] LOCATION not defined!")
endif()
elseif ("${type}" STREQUAL "SHARED_LIBRARY" OR "${type}" STREQUAL "UNKNOWN_LIBRARY")
elseif ("${type}" STREQUAL "SHARED_LIBRARY")
get_target_property(Location ${tgt} LOCATION)
if (Location)
get_filename_component(Dir ${Location} DIRECTORY)
@@ -879,8 +877,7 @@ function(mfem_export_mk_files)
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME
MFEM_USE_HDF5)
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
@@ -932,14 +929,12 @@ function(mfem_export_mk_files)
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
# For the next 4 variable, these are the values for the build-tree version of
# 'config.mk'
set(MFEM_INC_DIR "${PROJECT_BINARY_DIR}")
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}")
# TODO: CUDA/HIP support:
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
set(MFEM_MPIEXEC ${MPIEXEC})
if (NOT MFEM_MPIEXEC)
set(MFEM_MPIEXEC "mpirun")
@@ -93,17 +93,17 @@ macro (RESOLVE_LIBRARIES LIBS LINK_LINE)
set (_directory_list ${_directory_list} ${libpath})
set (token ${libname})
endif (token MATCHES "^/")
set (_lib "NOTFOUND")
set (_lib "NOTFOUND" CACHE FILEPATH "Cleared" FORCE)
find_library (_lib ${token} HINTS ${_directory_list} ${_root})
if (_lib)
string (REPLACE "//" "/" _lib ${_lib})
string (REPLACE "//" "/" _lib ${_lib})
list (APPEND _libs_found ${_lib})
else (_lib)
message (STATUS "Unable to find library ${token}")
endif (_lib)
unset(_lib CACHE)
endif (token MATCHES "-L([^\" ]+|\"[^\"]+\")")
endforeach (token)
set (_lib "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE)
# only the LAST occurrence of each library is required since there should be no circular dependencies
if (_libs_found)
list (REVERSE _libs_found)
+1 -4
View File
@@ -23,14 +23,11 @@
#include "_config.hpp"
#endif
#include <cstdint>
#include <climits>
namespace mfem
{
#if (defined(MFEM_USE_CUDA) && defined(__CUDACC__)) || \
(defined(MFEM_USE_HIP) && defined(__HIP__))
(defined(MFEM_USE_HIP) && defined(__HIPCC__))
#define MFEM_HOST_DEVICE __host__ __device__
#else
#define MFEM_HOST_DEVICE
-3
View File
@@ -132,9 +132,6 @@
// Enable Conduit support.
// #define MFEM_USE_CONDUIT
// Enable functionality based on the HDF5 library
// #define MFEM_USE_HDF5
// Enable functionality based on the NetCDF library (reading CUBIT files).
// #define MFEM_USE_NETCDF
+1 -3
View File
@@ -40,7 +40,6 @@ MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
MFEM_USE_AMGX = @MFEM_USE_AMGX@
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_HDF5 = @MFEM_USE_HDF5@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
MFEM_USE_SLEPC = @MFEM_USE_SLEPC@
@@ -88,7 +87,6 @@ MFEM_BUILD_TAG = @MFEM_BUILD_TAG@
MFEM_PREFIX = @MFEM_PREFIX@
MFEM_INC_DIR = @MFEM_INC_DIR@
MFEM_LIB_DIR = @MFEM_LIB_DIR@
MFEM_XLINKER = @MFEM_XLINKER@
# Location of test.mk
MFEM_TEST_MK = @MFEM_TEST_MK@
@@ -99,7 +97,7 @@ MFEM_MPIEXEC_NP = @MFEM_MPIEXEC_NP@
MFEM_MPI_NP = @MFEM_MPI_NP@
# The NVCC compiler cannot link with -x=cu
MFEM_LINK_FLAGS := $(filter-out -x=cu -xcuda -xhip, $(MFEM_FLAGS))
MFEM_LINK_FLAGS := $(filter-out -x=cu -xhip, $(MFEM_FLAGS))
# Optional extra configuration
@MFEM_CONFIG_EXTRA@
-9
View File
@@ -43,7 +43,6 @@ option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
option(MFEM_USE_HDF5 "Enable HDF5 usage" OFF)
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
option(MFEM_USE_PETSC "Enable PETSc support." OFF)
option(MFEM_USE_SLEPC "Enable SLEPc support." OFF)
@@ -89,12 +88,6 @@ option(MFEM_ENABLE_EXAMPLES "Build all of the examples" OFF)
option(MFEM_ENABLE_MINIAPPS "Build all of the miniapps" OFF)
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(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.
# set(CXX g++)
@@ -274,8 +267,6 @@ set(TRIBOL_DIR "${MFEM_DIR}/../tribol" CACHE PATH "Path to Tribol")
set(Tribol_REQUIRED_PACKAGES "Axom/core/mint/slam/slic" CACHE STRING
"Additional packages required by Tribol")
set(ENZYME_DIR "${MFEM_DIR}/../enzyme" CACHE PATH "Path to Enzyme")
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
+30 -41
View File
@@ -24,7 +24,7 @@ EGREP_BIN = $(shell command -v egrep 2> /dev/null)
CXX = g++
MPICXX = mpicxx
BASE_FLAGS = -std=c++17
BASE_FLAGS = -std=c++11
OPTIM_FLAGS = -O3 $(BASE_FLAGS)
DEBUG_FLAGS = -g $(XCOMPILER)-Wall $(BASE_FLAGS)
@@ -43,23 +43,12 @@ SHARED = NO
# CUDA configuration options
#
# If you set MFEM_USE_ENZYME=YES, must use CUDA_CXX=clang++
# If you set MFEM_USE_ENZYME=YES, CUDA_CXX has to be configured to use cuda with
# clang as its host compiler.
CUDA_CXX = nvcc
CUDA_ARCH = sm_60
# Base CUDA install directory, only needed if building with clang+cuda:
# The default setting is:
# 1. If CUDA_HOME is defined and non-empty, use that.
# 2. If nvcc is in the path, use the directory two levels up from that.
# 3. Use /usr/local/cuda
CUDA_DIR = $(or $(CUDA_HOME),$(patsubst %/,%,$(dir \
$(patsubst %/,%,$(dir $(shell command -v nvcc))))),/usr/local/cuda)
# flags for clang+cuda
CLANG_CUDA_FLAGS = -xcuda --cuda-path=$(CUDA_DIR) --cuda-gpu-arch=$(CUDA_ARCH)
# flags for nvcc
NVCC_FLAGS = -x=cu --expt-extended-lambda --expt-relaxed-constexpr \
-arch=$(CUDA_ARCH) -isystem "$(CUDA_DIR)/include"
# Prefixes for passing flags to the host compiler and linker when using
# CUDA_CXX=nvcc
CUDA_FLAGS = -x=cu --expt-extended-lambda -arch=$(CUDA_ARCH)
# Prefixes for passing flags to the host compiler and linker when using CUDA_CXX
CUDA_XCOMPILER = -Xcompiler=
CUDA_XLINKER = -Xlinker=
@@ -156,7 +145,6 @@ MFEM_USE_GINKGO = NO
MFEM_USE_AMGX = NO
MFEM_USE_MAGMA = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_HDF5 = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
MFEM_USE_SLEPC = NO
@@ -238,7 +226,7 @@ HYPRE_OPT = -I$(HYPRE_DIR)/include
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
ifeq (YES,$(MFEM_USE_CUDA))
# This is only necessary when hypre is built with cuda:
HYPRE_LIB += -lcusolver -lcusparse -lcurand -lcublas
HYPRE_LIB += -lcusparse -lcurand -lcublas
endif
ifeq (YES,$(MFEM_USE_HIP))
# This is only necessary when hypre is built with hip:
@@ -253,7 +241,7 @@ ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK)$(MFEM_USE_MUMPS),NONONO)
METIS_OPT =
METIS_LIB = -L$(METIS_DIR) -lmetis
else
METIS_DIR = @MFEM_DIR@/../metis-5.1.0
METIS_DIR = @MFEM_DIR@/../metis-5.0
METIS_OPT = -I$(METIS_DIR)/include
METIS_LIB = -L$(METIS_DIR)/lib -lmetis
endif
@@ -413,14 +401,9 @@ MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
GNUTLS_OPT =
GNUTLS_LIB = -lgnutls
# HDF5 library configuration
HDF5_DIR = $(HOME)/local
HDF5_OPT = -I$(HDF5_DIR)/include
HDF5_LIB = $(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib -lhdf5_hl -lhdf5 \
$(ZLIB_LIB)
# NetCDF library configuration
NETCDF_DIR = $(HOME)/local
HDF5_DIR = $(HOME)/local
NETCDF_OPT = -I$(NETCDF_DIR)/include -I$(HDF5_DIR)/include $(ZLIB_OPT)
NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
@@ -522,9 +505,6 @@ GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
# CUDA library configuration
CUDA_OPT =
CUDA_LIB = -lcusparse -lcublas
CLANG_CUDA_LIB = -L$(CUDA_DIR)/lib64 -L$(CUDA_DIR)/lib \
$(XLINKER)-rpath,$(CUDA_DIR)/lib64,-rpath,$(CUDA_DIR)/lib \
-lcudart -ldl -lrt -pthread
# HIP library configuration
HIP_OPT =
@@ -624,20 +604,29 @@ TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mi
-laxom_slam -laxom_slic -laxom_core
# Enzyme configuration
ENZYME_DIR = @MFEM_DIR@/../enzyme
ENZYME_PLUGIN = $(abspath $(wildcard $(subst \
@MFEM_DIR@,$(MFEM_DIR),$(ENZYME_DIR))/lib/ClangEnzyme-*.$(SO_EXT)))
ifeq ($(MAKECMDGOALS)-$(MFEM_USE_ENZYME),config-YES)
ifeq ($(ENZYME_PLUGIN),)
$(error Unable to find the Enzyme pluging! Please set ENZYME_DIR)
endif
ifneq ($(words $(ENZYME_PLUGIN)),1)
$(error Multiple versions of the Enzyme pluging found! \
Please set ENZYME_PLUGIN directly)
endif
# If you want to enable automatic differentiation at compile time, use the
# options below, adapted to your configuration. To be more flexible, we
# recommend using the Enzyme plugin during link time optimization. One option is
# to add your options to the global compiler/linker flags like
#
# BASE_FLAGS += -flto
# CXX_XLINKER += -fuse-ld=lld -Wl,--lto-legacy-pass-manager\
# -Wl,-mllvm=-load=$(ENZYME_DIR)/LLDEnzyme-$(ENZYME_VERSION).so -Wl,
#
ENZYME_DIR ?= @MFEM_DIR@/../enzyme
ENZYME_VERSION ?= 14
ENZYME_OPT = -fno-experimental-new-pass-manager -Xclang -load -Xclang $(ENZYME_DIR)/ClangEnzyme-$(ENZYME_VERSION).so
ENZYME_LIB = ""
# Google Benchmark, SUNDIALS >= 6.4.0, STRUMPACK, RAJA, UMPIRE, and Tribol require C++14:
ifneq ($(filter YES,$(MFEM_USE_BENCHMARK) $(MFEM_USE_SUNDIALS) $(MFEM_USE_STRUMPACK) $(MFEM_USE_RAJA) $(MFEM_USE_UMPIRE) $(MFEM_USE_TRIBOL)),)
BASE_FLAGS = -std=c++14
endif
# Ginkgo requires C++17:
ifeq ($(MFEM_USE_GINKGO),YES)
BASE_FLAGS = -std=c++17
endif
ENZYME_OPT = -fplugin=$(ENZYME_PLUGIN)
ENZYME_LIB =
# If YES, enable some informational messages
VERBOSE = NO
+1 -1
View File
@@ -115,7 +115,7 @@ vertices
nodes
FiniteElementSpace
FiniteElementCollection: H1_3D_P2
FiniteElementCollection: Quadratic
VDim: 3
Ordering: 0
+1 -1
View File
@@ -56,7 +56,7 @@ vertices
nodes
FiniteElementSpace
FiniteElementCollection: H1_3D_P2
FiniteElementCollection: Quadratic
VDim: 3
Ordering: 0
-593
View File
@@ -1,593 +0,0 @@
MFEM mesh v1.0
# Created by: Pointwise
# MFEM Geometry Types:
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
dimension
2
elements
160
1 3 1 164 163 0
1 3 164 165 162 163
1 3 2 166 164 1
1 3 166 132 165 164
1 3 3 167 166 2
1 3 167 131 132 166
1 3 4 168 167 3
1 3 168 130 131 167
1 3 5 169 168 4
1 3 169 129 130 168
1 3 6 170 169 5
1 3 170 128 129 169
1 3 171 172 170 6
1 3 172 127 128 170
1 3 124 125 172 171
1 3 125 126 127 172
1 3 162 165 173 161
1 3 165 132 133 173
1 3 161 173 174 160
1 3 173 133 134 174
1 3 160 174 175 159
1 3 174 134 135 175
1 3 6 7 176 171
1 3 7 8 177 176
1 3 171 176 123 124
1 3 176 177 122 123
1 3 159 175 178 158
1 3 175 135 136 178
1 3 158 178 179 157
1 3 178 136 137 179
1 3 157 179 180 156
1 3 179 137 138 180
1 3 122 177 181 121
1 3 177 8 182 181
1 3 8 9 183 182
1 3 9 10 184 183
1 3 10 11 185 184
1 3 11 12 186 185
1 3 12 13 187 186
1 3 13 14 15 187
1 3 121 181 119 120
1 3 181 182 118 119
1 3 182 183 117 118
1 3 183 184 188 117
1 3 184 185 109 188
1 3 185 186 108 109
1 3 186 187 189 108
1 3 187 15 16 189
1 3 109 110 190 188
1 3 110 111 191 190
1 3 111 112 113 191
1 3 188 190 116 117
1 3 190 191 115 116
1 3 191 113 114 115
1 3 189 192 107 108
1 3 192 193 106 107
1 3 193 194 105 106
1 3 194 195 104 105
1 3 195 196 103 104
1 3 16 17 192 189
1 3 17 18 193 192
1 3 18 19 194 193
1 3 19 20 195 194
1 3 20 21 196 195
1 3 97 98 197 96
1 3 98 99 198 197
1 3 99 100 199 198
1 3 100 101 200 199
1 3 101 102 201 200
1 3 102 103 202 201
1 3 103 196 203 202
1 3 196 21 22 203
1 3 96 197 204 95
1 3 197 198 39 204
1 3 198 199 38 39
1 3 199 200 205 38
1 3 200 201 32 205
1 3 201 202 31 32
1 3 202 203 206 31
1 3 203 22 23 206
1 3 32 33 207 205
1 3 33 34 35 207
1 3 205 207 37 38
1 3 207 35 36 37
1 3 39 40 208 204
1 3 40 41 209 208
1 3 41 42 210 209
1 3 42 43 211 210
1 3 43 44 212 211
1 3 204 208 94 95
1 3 208 209 93 94
1 3 209 210 92 93
1 3 210 211 91 92
1 3 211 212 90 91
1 3 90 212 213 89
1 3 212 44 214 213
1 3 44 45 215 214
1 3 45 46 216 215
1 3 46 47 217 216
1 3 47 48 218 217
1 3 48 49 219 218
1 3 49 50 51 219
1 3 89 213 87 88
1 3 213 214 86 87
1 3 214 215 85 86
1 3 215 216 84 85
1 3 216 217 83 84
1 3 217 218 82 83
1 3 218 219 220 82
1 3 219 51 52 220
1 3 53 221 220 52
1 3 221 81 82 220
1 3 54 222 221 53
1 3 222 80 81 221
1 3 55 223 222 54
1 3 223 79 80 222
1 3 26 27 224 25
1 3 27 28 29 224
1 3 25 224 225 24
1 3 224 29 30 225
1 3 24 225 206 23
1 3 225 30 31 206
1 3 154 155 226 153
1 3 155 156 180 226
1 3 153 226 227 152
1 3 226 180 138 227
1 3 152 227 228 151
1 3 227 138 139 228
1 3 151 228 229 150
1 3 228 139 140 229
1 3 150 229 230 149
1 3 229 140 141 230
1 3 149 230 231 148
1 3 230 141 142 231
1 3 148 231 232 147
1 3 231 142 143 232
1 3 147 232 145 146
1 3 232 143 144 145
1 3 56 233 223 55
1 3 233 78 79 223
1 3 57 234 233 56
1 3 234 77 78 233
1 3 58 235 234 57
1 3 235 76 77 234
1 3 61 236 59 60
1 3 236 235 58 59
1 3 62 237 236 61
1 3 237 76 235 236
1 3 63 238 237 62
1 3 238 75 76 237
1 3 64 239 238 63
1 3 239 74 75 238
1 3 65 240 239 64
1 3 240 73 74 239
1 3 66 241 240 65
1 3 241 72 73 240
1 3 67 242 241 66
1 3 242 71 72 241
1 3 68 69 242 67
1 3 69 70 71 242
boundary
164
3 1 0 1
3 1 1 2
3 1 2 3
3 1 3 4
3 1 4 5
3 1 5 6
3 1 6 7
3 1 7 8
3 1 8 9
3 1 9 10
3 1 10 11
3 1 11 12
3 1 12 13
3 1 13 14
3 1 16 17
3 1 17 18
3 1 18 19
3 1 19 20
3 1 20 21
3 1 21 22
3 1 22 23
3 1 23 24
3 1 24 25
3 1 25 26
3 1 26 27
3 1 27 28
3 1 28 29
3 1 29 30
3 1 30 31
3 1 31 32
3 1 32 33
3 1 33 34
3 1 34 35
3 1 35 36
3 1 36 37
3 1 37 38
3 1 38 39
3 1 39 40
3 1 40 41
3 1 41 42
3 1 42 43
3 1 43 44
3 1 49 50
3 1 48 49
3 1 47 48
3 1 46 47
3 1 45 46
3 1 44 45
3 1 52 53
3 1 53 54
3 1 54 55
3 1 57 58
3 1 56 57
3 1 55 56
3 1 60 61
3 1 61 62
3 1 62 63
3 1 63 64
3 1 64 65
3 1 65 66
3 1 66 67
3 1 67 68
3 1 75 76
3 1 74 75
3 1 73 74
3 1 72 73
3 1 71 72
3 1 70 71
3 1 76 77
3 1 77 78
3 1 78 79
3 1 81 82
3 1 80 81
3 1 79 80
3 1 82 83
3 1 83 84
3 1 84 85
3 1 85 86
3 1 86 87
3 1 87 88
3 1 94 95
3 1 93 94
3 1 92 93
3 1 91 92
3 1 90 91
3 1 96 97
3 1 95 96
3 1 97 98
3 1 98 99
3 1 99 100
3 1 100 101
3 1 101 102
3 1 102 103
3 1 107 108
3 1 106 107
3 1 105 106
3 1 104 105
3 1 103 104
3 1 108 109
3 1 109 110
3 1 110 111
3 1 111 112
3 1 112 113
3 1 113 114
3 1 114 115
3 1 115 116
3 1 116 117
3 1 119 120
3 1 118 119
3 1 117 118
3 1 131 132
3 1 130 131
3 1 129 130
3 1 128 129
3 1 127 128
3 1 126 127
3 1 132 133
3 1 133 134
3 1 134 135
3 1 137 138
3 1 136 137
3 1 135 136
3 1 138 139
3 1 139 140
3 1 140 141
3 1 141 142
3 1 142 143
3 1 143 144
3 1 147 148
3 1 146 147
3 1 153 154
3 1 152 153
3 1 151 152
3 1 150 151
3 1 149 150
3 1 148 149
3 1 156 157
3 1 157 158
3 1 158 159
3 1 161 162
3 1 160 161
3 1 159 160
2 1 69 70
2 1 68 69
3 1 88 89
3 1 89 90
3 1 121 122
3 1 120 121
3 1 123 124
3 1 122 123
3 1 125 126
3 1 124 125
1 1 144 145
1 1 145 146
3 1 15 16
3 1 14 15
3 1 50 51
3 1 51 52
3 1 59 60
3 1 58 59
3 1 154 155
3 1 155 156
3 1 163 0
3 1 162 163
vertices
243
2
4 4
4 3.5
4 3
4 2.5
4 2
4 1.5
4 1
4.5 1
5 1
5 1.5
5 2
5 2.5
5 3
5 3.5
5 4
5.500 4
6 4
6.500 4
7 4
7.5 4
8 4
8.5 4
9 4
9.5 4
10 4
10.5 4
11 4
11 3.5
11 3
10.5 3
10 3
9.5 3
9.5 2.5
10 2.5
10.5 2.5
10.5 2
10.5 1.5
10 1.5
9.5 1.5
9.5 1
10 1
10.5 1
11 1
11.5 1
12 1
12 1.5
12 2
12 2.5
12 3
12 3.5
12 4
12.5 4
13 4
13.333 3.75
13.666 3.5
14.000 3.25
14.333 3.5
14.666 3.75
15.000 4
15.500 4
16.000 4
16.000 3.5
16.000 3
16.000 2.5
16.000 2
16.000 1.5
16.000 1
16.000 0.5
16.000 0
15.500 0
15.000 0
15.000 0.5000000000000002
15.000 1
15.000 1.5
15.000 2
15.000 2.5
15.000 3
14.666 2.75
14.333 2.5
14.000 2.25
13.666 2.5
13.333 2.75
13 3
13 2.5
13 2
13 1.5
13 1
13 0.500
13 0
12.5 0
12 0
11.5 0
11 0
10.5 0
10 0
9.5 0
9 0
8.5 0
8.5 0.5
8.5 1
8.5 1.5
8.5 2
8.5 2.5
8.5 3
8 3
7.5 3
7 3
6.500 3
6 3
6 2.5
6.5 2.5
7 2.5
7.5 2.5
7.5 2
7.5 1.5
7.000 1.5
6.5 1.5
6 1.5
6 1
6 0.5
6 0
5.5 0
5 0
4.5 0
4 0
3.5 0
3 0
3 0.500
3 1
3 1.5
3 2
3 2.5
3 3
2.666 2.75
2.333 2.5
2.000 2.25
1.666 2.5
1.333 2.75
1.000 3
1.000 2.5
1.000 2
1.000 1.5
1.000 1
1.000 0.5000
1.000 0
0.5000 0
0.0000 0
0.0000 0.5
0.0000 1
0.0000 1.5
0.0000 2
0.0000 2.5
0.0000 3
0.0000 3.5
0.0000 4
0.5000 4
1.000 4
1.333 3.75
1.666 3.5
2.000 3.25
2.333 3.5
2.666 3.75
3 4
3.5 4
3.5 3.5
3 3.5
3.5 3
3.5 2.5
3.5 2
3.5 1.5
3.5 1
4 0.5
3.5 0.5
2.666 3.25
2.333 3
2.000 2.75
4.5 0.5
5 0.5
1.666 3
1.333 3.25
1.000 3.5
5.5 0.5
5.500 1
5.500 1.5
5.500 2
5.500 2.5
5.500 3
5.500 3.5
6 2
6 3.5
6.5 2
7 2
6.5 3.5
7 3.5
7.5 3.5
8 3.5
8.5 3.5
9 0.5
9 1
9 1.5
9 2
9 2.5
9 3
9 3.5
9.5 0.5
9.5 2
9.5 3.5
10 2
10 0.5
10.5 0.5
11 0.5
11.5 0.5
12 0.5
12.5 0.500
12.5 1
12.5 1.5
12.5 2
12.5 2.5
12.5 3
12.5 3.5
13 3.5
13.333 3.250
13.666 3
14.000 2.75
10.5 3.5
10 3.5
0.500 3.5
0.500 3
0.500 2.5
0.500 2
0.500 1.5
0.500 1
0.500 0.5
14.333 3
14.666 3.25
15.000 3.5
15.500 3.5
15.500 3
15.500 2.5
15.500 2
15.500 1.5
15.500 1
15.500 0.5
-342
View File
@@ -1,342 +0,0 @@
MFEM NURBS NC-patch mesh v1.0
dimension
3
elements
13
0 1 5 0 8 10 11 9 4 6 7 5
0 1 5 0 18 8 24 32 30 23 36 38
0 1 5 0 0 18 32 14 12 30 38 29
0 1 5 0 32 24 10 20 38 36 26 35
0 1 5 0 14 32 20 2 29 38 35 16
0 1 5 0 30 23 36 38 31 22 37 39
0 1 5 0 12 30 38 29 13 31 39 28
0 1 5 0 38 36 26 35 39 37 27 34
0 1 5 0 29 38 35 16 28 39 34 17
0 1 5 0 31 22 37 39 19 9 25 33
0 1 5 0 13 31 39 28 1 19 33 15
0 1 5 0 39 37 27 34 33 25 11 21
0 1 5 0 28 39 34 17 15 33 21 3
boundary
31
9999 3 8 10 6 4
9999 3 10 11 7 6
9999 3 11 9 5 7
9999 3 9 8 4 5
9999 3 4 6 7 5
9999 3 32 24 8 18
9999 3 18 8 23 30
9999 3 14 32 18 0
9999 3 0 18 30 12
9999 3 14 0 12 29
9999 3 20 10 24 32
9999 3 10 20 35 26
9999 3 2 20 32 14
9999 3 20 2 16 35
9999 3 2 14 29 16
9999 3 30 23 22 31
9999 3 12 30 31 13
9999 3 29 12 13 28
9999 3 26 35 34 27
9999 3 35 16 17 34
9999 3 16 29 28 17
9999 3 31 22 9 19
9999 3 19 9 25 33
9999 3 13 31 19 1
9999 3 28 13 1 15
9999 3 1 19 33 15
9999 3 27 34 21 11
9999 3 33 25 11 21
9999 3 34 17 3 21
9999 3 17 28 15 3
9999 3 15 33 21 3
vertex_to_knotspan
8
23 0 1 8 10 11 9
22 0 2 8 10 11 9
24 1 0 8 10 11 9
36 1 1 8 10 11 9
37 1 2 8 10 11 9
25 1 3 8 10 11 9
26 2 1 8 10 11 9
27 2 2 8 10 11 9
coordinates
40
3
0 0 0
0 1 0
4 0 0
4 1 0
0 0 4
0 1 4
4 0 4
4 1 4
0 0 2
0 1 2
4 0 2
4 1 2
0 0.333333333333333 0
0 0.666666666666667 0
2 0 0
2 1 0
4 0.333333333333334 0
4 0.666666666666667 0
0 0 1
0 1 1
4 0 1
4 1 1
0 0.666666666666667 2
0 0.333333333333333 2
2 0 2
2 1 2
4 0.333333333333333 2
4 0.666666666666667 2
2 0.666666666666667 0
2 0.333333333333333 0
0 0.333333333333333 1
0 0.666666666666667 1
1.81325211007895 0 1
1.81325211007895 1 1
4 0.666666666666667 1
4 0.333333333333333 1
2 0.333333333333333 2
2 0.666666666666667 2
1.81325211007895 0.333333333333333 1
1.81325211007895 0.666666666666667 1
edges
87
0 8 10
1 10 11
0 9 11
1 8 9
0 4 6
1 6 7
0 5 7
1 4 5
2 4 8
2 6 10
2 7 11
2 5 9
9 18 8
7 8 24
9 32 24
7 18 32
9 30 23
7 23 36
9 38 36
7 30 38
3 18 30
3 8 23
3 24 36
3 32 38
8 0 18
8 14 32
7 0 14
8 12 30
8 29 38
7 12 29
3 0 12
3 14 29
6 24 10
9 20 10
6 32 20
6 36 26
9 35 26
6 38 35
3 10 26
3 20 35
8 2 20
6 14 2
8 16 35
6 29 16
3 2 16
9 31 22
7 22 37
9 39 37
7 31 39
4 30 31
4 23 22
4 36 37
4 38 39
8 13 31
8 28 39
7 13 28
4 12 13
4 29 28
6 37 27
9 34 27
6 39 34
4 26 27
4 35 34
8 17 34
6 28 17
4 16 17
9 19 9
7 9 25
9 33 25
7 19 33
5 31 19
5 22 9
5 37 25
5 39 33
8 1 19
8 15 33
7 1 15
5 13 1
5 28 15
6 25 11
9 21 11
6 33 21
5 27 11
5 34 21
8 3 21
6 15 3
5 17 3
knotvectors
10
1 3 0 0 0.5 1 1
1 4 0 0 0.333333333333333 0.666666666666667 1 1
1 3 0 0 0.5 1 1
1 2 0 0 1 1
1 2 0 0 1 1
1 2 0 0 1 1
1 2 0 0 1 1
1 2 0 0 1 1
1 2 0 0 1 1
1 2 0 0 1 1
spacing
0
weights
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
FiniteElementSpace
FiniteElementCollection: NURBS1
VDim: 3
Ordering: 1
0 0 0
0 1 0
4 0 0
4 1 0
0 0 4
0 1 4
4 0 4
4 1 4
0 0 2
0 1 2
4 0 2
4 1 2
0 0.333333333333333 0
0 0.666666666666667 0
2 0 0
2 1 0
4 0.333333333333334 0
4 0.666666666666667 0
0 0 1
0 1 1
4 0 1
4 1 1
0 0.666666666666667 2
0 0.333333333333333 2
2 0 2
2 1 2
4 0.333333333333333 2
4 0.666666666666667 2
2 0.666666666666667 0
2 0.333333333333333 0
0 0.333333333333333 1
0 0.666666666666667 1
1.81325211007895 0 1
1.81325211007895 1 1
4 0.666666666666667 1
4 0.333333333333333 1
2 0.333333333333333 2
2 0.666666666666667 2
1.81325211007895 0.333333333333333 1
1.81325211007895 0.666666666666667 1
2 0 4
4 0.333333333333333 4
4 0.666666666666667 4
2 1 4
0 0.333333333333333 4
0 0.666666666666667 4
0 0 3
4 0 3
4 1 3
0 1 3
2 0 3
4 0.333333333333333 3
4 0.666666666666667 3
2 1 3
0 0.666666666666667 3
0 0.333333333333333 3
2 0.333333333333333 4
2 0.666666666666667 4
2 0.333333333333333 3
2 0.666666666666667 3
-96
View File
@@ -1,96 +0,0 @@
MFEM NURBS NC-patch mesh v1.0
dimension
2
# rank attr geom ref_type nodes/children
elements
3
0 1 3 0 0 4 5 1
0 1 3 0 6 7 4 2
0 1 3 0 6 3 5 7
# attr geom nodes
boundary
7
1 1 0 4
1 1 5 1
1 1 1 0
1 1 2 6
1 1 6 3
1 1 4 2
1 1 5 3
vertex_to_knotspan
1
7 1 4 5
# top-level node coordinates
coordinates
8
2
0 0
0 1
2 0
2 1
1 0
1 1
2 0.5
1 0.5
edges
11
0 0 4
1 4 5
0 1 5
1 0 1
2 6 7
4 7 4
2 2 4
4 6 2
3 6 3
2 3 5
3 7 5
knotvectors
5
1 3 0 0 0.5 1 1
1 3 0 0 0.5 1 1
1 2 0 0 1 1
1 2 0 0 1 1
1 2 0 0 1 1
spacing
0
weights
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
FiniteElementSpace
FiniteElementCollection: NURBS1
VDim: 2
Ordering: 1
0 0
0 1
2 0
2 1
1 0
1 1
2 0.5
1 0.5
0.5 0
0.5 1
0 0.5
0.5 0.5
mfem_mesh_end
+1 -1
View File
@@ -227,7 +227,7 @@ vertices
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P2
FiniteElementCollection: Quadratic
VDim: 2
Ordering: 0
+1 -1
View File
@@ -65,7 +65,7 @@ vertices
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P2
FiniteElementCollection: Quadratic
VDim: 2
Ordering: 0
-2
View File
@@ -951,7 +951,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/fem/ceed/integrators/nlconvection \
@MFEM_SOURCE_DIR@/fem/ceed/interface \
@MFEM_SOURCE_DIR@/fem/ceed/solvers \
@MFEM_SOURCE_DIR@/fem/dfem \
@MFEM_SOURCE_DIR@/fem/eltrans \
@MFEM_SOURCE_DIR@/fem/fe \
@MFEM_SOURCE_DIR@/fem/gslib \
@@ -973,7 +972,6 @@ 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/dfem \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
-1
View File
@@ -202,7 +202,6 @@ namespace mfem {
* - <a class="el" href="tesla_8cpp_source.html">Tesla</a>: simple magnetostatics simulation code
* - <a class="el" href="maxwell_8cpp_source.html">Maxwell</a>: simple transient full-wave electromagnetics simulation code
* - <a class="el" href="joule_8cpp_source.html">Joule</a>: transient magnetics and Joule heating miniapp
* - <a class="el" href="lorentz_8cpp_source.html">Lorentz</a>: simple particle tracking code based on the Lorentz force
* - <a class="el" href="classmfem_1_1navier_1_1NavierSolver.html">Navier</a>: solve the transient incompressible Navier-Stokes equations
* - <a class="el" href="mobius-strip_8cpp_source.html">Mobius Strip</a>: generate various Mobius strip-like meshes
* - <a class="el" href="klein-bottle_8cpp_source.html">Klein Bottle</a>: generate three types of Klein bottle surfaces
-9
View File
@@ -205,15 +205,6 @@ if (MFEM_ENABLE_TESTING)
$<TARGET_FILE:ex25p> "-no-vis" "--mumps-solver"
${MPIEXEC_POSTFLAGS})
endif()
# Parallel libCEED example
if (MFEM_USE_CEED AND MFEM_USE_MPI)
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"
${MPIEXEC_POSTFLAGS})
endif()
endif()
# Include the examples/amgx directory if AmgX is enabled
-1
View File
@@ -27,7 +27,6 @@
// ex1 -m ../data/fichera-amr.mesh
// ex1 -m ../data/mobius-strip.mesh
// ex1 -m ../data/mobius-strip.mesh -o -1 -sc
// ex1 -m ../data/nc3-nurbs.mesh -o -1
//
// Device sample runs:
// ex1 -pa -d cuda
+187 -61
View File
@@ -2,14 +2,14 @@
//
// Compile with: make ex9
//
// Sample runs:
// DG sample runs:
// ex9 -m ../data/periodic-segment.mesh -p 0 -r 2 -dt 0.005
// ex9 -m ../data/periodic-square.mesh -p 0 -r 2 -dt 0.01 -tf 10
// ex9 -m ../data/periodic-hexagon.mesh -p 0 -r 2 -dt 0.01 -tf 10
// ex9 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex9 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 23 -tf 9
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 13 -tf 9
// ex9 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex9 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex9 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9
@@ -19,6 +19,23 @@
// ex9 -m ../data/periodic-square.msh -p 0 -r 2 -dt 0.005 -tf 2
// ex9 -m ../data/periodic-cube.msh -p 0 -r 1 -o 2 -tf 2
//
// CG sample runs:
// ex9 -m ../data/periodic-segment.mesh -p 0 -r 5 -dt 0.001 -sc 11 -vs 50 -o 1 -s 2
// ex9 -m ../data/periodic-segment.mesh -p 0 -r 5 -dt 0.001 -sc 12 -vs 50 -o 1 -s 2
// ex9 -m ../data/periodic-segment.mesh -p 0 -r 5 -dt 0.001 -sc 13 -vs 50 -o 1 -s 2
// ex9 -m ../data/periodic-square.mesh -p 0 -r 3 -dt 0.01 -tf 10 -sc 11 -o 2 -s 3 -vs 20
// ex9 -m ../data/periodic-hexagon.mesh -p 0 -r 3 -dt 0.01 -tf 10 -sc 12 -vs 20
// ex9 -m ../data/periodic-square.mesh -p 1 -r 4 -dt 0.002 -tf 9 -sc 11 -o 1 -s 2 -vs 20
// ex9 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.002 -tf 9 -sc 11 -vs 20
// ex9 -m ../data/periodic-square.mesh -p 1 -r 4 -dt 0.002 -tf 9 -sc 13 -o 1 -s 2 -vs 20
// ex9 -m ../data/star-mixed.mesh -p 1 -r 4 -dt 0.004 -tf 9 -vs 20 -sc 11 -o 1 -s 2
// ex9 -m ../data/star-q3.mesh -p 1 -r 4 -dt 0.004 -tf 9 -vs 20 -sc 11 -o 1 -s 2
// ex9 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9 -sc 11
// ex9 -m ../data/disc-nurbs.mesh -p 1 -r 4 -dt 0.005 -tf 9 -sc 12 -o 2 -s 3
// ex9 -m ../data/periodic-square.mesh -p 3 -r 4 -dt 0.005 -tf 9 -vs 20 -sc 11 -o 2 -s 3
// ex9 -m ../data/periodic-cube.mesh -p 0 -r 2 -dt 0.02 -tf 8 -sc 11 -o 2 -s 3
// ex9 -m ../data/periodic-cube.msh -p 0 -r 2 -o 2 -s 3 -tf 2 -sc 11
//
// Device sample runs:
// ex9 -pa
// ex9 -ea
@@ -41,11 +58,16 @@
// solution. The saving of time-dependent data files for external
// visualization with VisIt (visit.llnl.gov) and ParaView
// (paraview.org) is also illustrated.
// Additionally, the example showcases the implementation of an
// element-based Clip & Scale limiter for continuous finite
// elements, which is designed to be bound-preserving.
// For more detail, see https://doi.org/10.1142/13466.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include <algorithm>
#include "ex9.hpp"
using namespace std;
using namespace mfem;
@@ -104,12 +126,12 @@ public:
}
}
void SetOperator(const Operator &op) override
void SetOperator(const Operator &op)
{
linear_solver.SetOperator(op);
}
void Mult(const Vector &x, Vector &y) const override
virtual void Mult(const Vector &x, Vector &y) const
{
linear_solver.Mult(x, y);
}
@@ -120,7 +142,7 @@ public:
and advection matrices, and b describes the flow on the boundary. This can
be written as a general ODE, du/dt = M^{-1} (K u + b), and this class is
used to evaluate the right-hand side. */
class FE_Evolution : public TimeDependentOperator
class DG_FE_Evolution : public TimeDependentOperator
{
private:
BilinearForm &M, &K;
@@ -132,15 +154,14 @@ private:
mutable Vector z;
public:
FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_);
DG_FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_);
void Mult(const Vector &x, Vector &y) const override;
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
~FE_Evolution() override;
virtual ~DG_FE_Evolution();
};
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
@@ -153,6 +174,7 @@ int main(int argc, char *argv[])
bool fa = false;
const char *device_config = "cpu";
int ode_solver_type = 4;
int scheme = 1;
real_t t_final = 10.0;
real_t dt = 0.01;
bool visualization = true;
@@ -182,7 +204,17 @@ int main(int argc, char *argv[])
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::Types.c_str());
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
" 11 - Backward Euler,\n\t"
" 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t"
" 22 - Implicit Midpoint Method,\n\t"
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
args.AddOption(&scheme, "-sc", "--scheme",
"FE scheme: 1 - DG high-order, unstabilized,\n\t"
" 11 - CG low-order,\n\t"
" 12 - CG high-order, stabilized,\n\t"
" 13 - CG high-order, stabilized, limited.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -209,6 +241,14 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
const bool DG = (scheme < 10);
// Limiter is only implemented to run on cpu.
if (!DG && strcmp(device_config, "cuda") == 0)
{
cout << "Cuda not supported for this CG implementation" << endl;
return 2;
}
Device device(device_config);
device.Print();
@@ -217,9 +257,49 @@ int main(int argc, char *argv[])
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// Nonconforming meshes are not feasible for continuous elements
if (!DG && !mesh.Conforming())
{
cout << "CG needs a conforming mesh." << endl;
return 3;
}
// 3. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
// The CG Limiter is only implemented for explicit time-stepping methods.
if (!DG && ode_solver_type > 10)
{
cout << "The CG methods are supported only with explicit RK schemes.\n";
return 4;
}
// Limiter and low order scheme are only provably bound preserving
// when employing SSP-RK time-stepping methods
else if ((scheme == 11 || scheme == 13) && ode_solver_type > 3)
{
MFEM_WARNING("Non-SSP-RK method! Bounds might be violated.");
}
unique_ptr<ODESolver> ode_solver = nullptr;
switch (ode_solver_type)
{
// Explicit methods
case 1: ode_solver.reset(new ForwardEulerSolver); break;
case 2: ode_solver.reset(new RK2Solver(1.0)); break;
case 3: ode_solver.reset(new RK3SSPSolver); break;
case 4: ode_solver.reset(new RK4Solver); break;
case 6: ode_solver.reset(new RK6Solver); break;
// Implicit (L-stable) methods
case 11: ode_solver.reset(new BackwardEulerSolver); break;
case 12: ode_solver.reset(new SDIRK23Solver(2)); break;
case 13: ode_solver.reset(new SDIRK33Solver); break;
// Implicit A-stable methods (not L-stable)
case 22: ode_solver.reset(new ImplicitMidpointSolver); break;
case 23: ode_solver.reset(new SDIRK23Solver); break;
case 24: ode_solver.reset(new SDIRK34Solver); break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
return 5;
}
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
@@ -235,12 +315,21 @@ int main(int argc, char *argv[])
}
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.
DG_FECollection fec(order, dim, BasisType::GaussLobatto);
FiniteElementSpace fes(&mesh, &fec);
// 5. Define the finite element space of the given polynomial order on the
// refined mesh. Continuous H1 and discontinuous L2 spaces are supported.
DG_FECollection fec_DG(order, dim, BasisType::GaussLobatto);
H1_FECollection fec_CG(order, dim, BasisType::Positive);
unique_ptr<FiniteElementSpace> fes = nullptr;
switch (scheme)
{
case 1: fes.reset(new FiniteElementSpace(&mesh, &fec_DG)); break;
case 11:
case 12:
case 13: fes.reset(new FiniteElementSpace(&mesh, &fec_CG)); break;
default: cout << "Unknown scheme: " << scheme << endl; return 6;
}
cout << "Number of unknowns: " << fes.GetVSize() << endl;
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
@@ -249,46 +338,71 @@ int main(int argc, char *argv[])
FunctionCoefficient inflow(inflow_function);
FunctionCoefficient u0(u0_function);
BilinearForm m(&fes);
BilinearForm k(&fes);
if (pa)
BilinearForm m(fes.get());
BilinearForm k(fes.get());
if (DG)
{
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
if (pa)
{
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
else if (ea)
{
m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
else if (fa)
{
m.SetAssemblyLevel(AssemblyLevel::FULL);
k.SetAssemblyLevel(AssemblyLevel::FULL);
}
}
else if (ea)
else if (scheme == 13 && (pa || ea))
{
m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
else if (fa)
{
m.SetAssemblyLevel(AssemblyLevel::FULL);
k.SetAssemblyLevel(AssemblyLevel::FULL);
cout << "The CG Limiter needs full assembly of the mass matrix to obtain "
<< "the local stencil via its sparsity pattern.\n";
return 7;
}
m.AddDomainIntegrator(new MassIntegrator);
constexpr real_t alpha = -1.0;
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
k.AddInteriorFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
k.AddBdrFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
LinearForm b(&fes);
b.AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, alpha));
m.Assemble();
int skip_zeros = 0;
k.Assemble(skip_zeros);
b.Assemble();
m.Finalize();
k.Finalize(skip_zeros);
constexpr real_t alpha = -1.0;
int skip_zeros = 0;
Vector lumpedmassmatrix(m.Height());
// The convective bilinear form is not needed in the CG case.
if (DG)
{
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
k.AddInteriorFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
k.AddBdrFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
k.Assemble(skip_zeros);
k.Finalize(skip_zeros);
}
// lumped mass matrix not needed in the DG case
else
{
BilinearForm mL(fes.get());
mL.AddDomainIntegrator(new LumpedIntegrator(new MassIntegrator));
mL.Assemble();
mL.Finalize();
mL.SpMat().GetDiag(lumpedmassmatrix);
}
LinearForm b(fes.get());
b.AddBdrFaceIntegrator(new BoundaryFlowIntegrator(inflow, velocity, alpha));
b.Assemble();
// 7. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
// GLVis visualization.
GridFunction u(&fes);
GridFunction u(fes.get());
u.ProjectCoefficient(u0);
{
@@ -302,7 +416,7 @@ int main(int argc, char *argv[])
// Create data collection for solution output: either VisItDataCollection for
// ascii data files, or SidreDataCollection for binary data files.
DataCollection *dc = NULL;
unique_ptr<DataCollection> dc = nullptr;
if (visit)
{
if (binary)
@@ -315,7 +429,7 @@ int main(int argc, char *argv[])
}
else
{
dc = new VisItDataCollection("Example9", &mesh);
dc.reset(new VisItDataCollection("Example9", &mesh));
dc->SetPrecision(precision);
}
dc->RegisterField("solution", &u);
@@ -324,10 +438,10 @@ int main(int argc, char *argv[])
dc->Save();
}
ParaViewDataCollection *pd = NULL;
unique_ptr<ParaViewDataCollection> pd = nullptr;
if (paraview)
{
pd = new ParaViewDataCollection("Example9", &mesh);
pd.reset(new ParaViewDataCollection("Example9", &mesh));
pd->SetPrefixPath("ParaView");
pd->RegisterField("solution", &u);
pd->SetLevelsOfDetail(order);
@@ -365,11 +479,23 @@ int main(int argc, char *argv[])
// 8. 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).
FE_Evolution adv(m, k, b);
//DG_FE_Evolution adv(m, k, b);
unique_ptr<TimeDependentOperator> adv = nullptr;
switch (scheme)
{
case 1: adv.reset(new DG_FE_Evolution(m, k, b)); break;
case 11: adv.reset(new LowOrderScheme(*fes, lumpedmassmatrix,
inflow, velocity, m)); break;
case 12: adv.reset(new HighOrderTargetScheme(*fes, lumpedmassmatrix,
inflow, velocity, m)); break;
case 13: adv.reset(new ClipAndScale(*fes, lumpedmassmatrix,
inflow, velocity, m)); break;
default: cout << "Unknown scheme: " << scheme << '\n'; return 8;
}
real_t t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
adv->SetTime(t);
ode_solver->Init(*adv);
bool done = false;
for (int ti = 0; !done; )
@@ -413,16 +539,16 @@ int main(int argc, char *argv[])
u.Save(osol);
}
// 10. Free the used memory.
delete pd;
delete dc;
ConstantCoefficient zero(0.0);
std::cout << "Norm: " << u.ComputeL2Error(zero) << std::endl;
return 0;
}
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_)
// Implementation of class DG_FE_Evolution
DG_FE_Evolution::DG_FE_Evolution(BilinearForm &M_, BilinearForm &K_,
const Vector &b_)
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
M(M_), K(K_), b(b_), z(height)
{
@@ -447,7 +573,7 @@ FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_)
M_solver.SetPrintLevel(0);
}
void FE_Evolution::Mult(const Vector &x, Vector &y) const
void DG_FE_Evolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K.Mult(x, z);
@@ -455,7 +581,7 @@ void FE_Evolution::Mult(const Vector &x, Vector &y) const
M_solver.Mult(z, y);
}
void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
void DG_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");
@@ -465,7 +591,7 @@ void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
dg_solver->Mult(z, k);
}
FE_Evolution::~FE_Evolution()
DG_FE_Evolution::~DG_FE_Evolution()
{
delete M_prec;
delete dg_solver;
+554
View File
@@ -0,0 +1,554 @@
// MFEM Example 9 - Serial/Parallel Shared Code
#include "mfem.hpp"
#include <limits>
namespace mfem
{
// Function f = 1 for lumped boundary operator
real_t one(const Vector &x) { return 1.0; }
/** Abstract base class for evaluating the time-dependent operator in the ODE
formulation. The continuous Galerkin (CG) strong form of the advection
equation du/dt = -v.grad(u) is given by M du/dt = -K u + b, where M and K
are the mass and advection matrices, respectively, and b represents the
boundary flow contribution.
The ODE can be reformulated as:
du/dt = M_L^{-1}((-K + D) u + F^*(u) + b),
where M_L is the lumped mass matrix, D is a low-order stabilization term,
and F^*(u) represents the limited anti-diffusive fluxes.
Here, F^* is a limited version of F, which recover the high-order target
scheme. The limited anti-diffusive fluxes F^* are the sum of the limited
element contributions of the original flux F to enforce local bounds.
Additional to the limiter we implement the low-order scheme and
high-order target scheme by chosing:
- F^* = 0 for the bound-preserving low-order scheme.
- F^* = F for the high-order target scheme which is not bound-preserving.
This abstract class provides a framework for evaluating the right-hand side
of the ODE and is intended to be inherited by classes that implement
the three schemes:
- The ClipAndScale class, which employes the limiter to enforces local
bounds
- The HighOrderTargetScheme class, which employs the raw anti-diffusive
fluxes F
- The LowOrderScheme class, which employs F = 0 and has low accuracy, but
is bound-preserving */
class CG_FE_Evolution : public TimeDependentOperator
{
protected:
const Vector &lumpedmassmatrix;
FiniteElementSpace &fes;
#ifdef MFEM_USE_MPI
ParFiniteElementSpace *pfes;
#endif
int *I, *J;
LinearForm b_lumped;
GridFunction u_inflow;
mutable DenseMatrix Ke, Me;
mutable Vector ue, re, udote, fe, fe_star, gammae;
mutable ConvectionIntegrator conv_int;
mutable MassIntegrator mass_int;
mutable Vector z;
virtual void ComputeLOTimeDerivatives(const Vector &u, Vector &udot) const;
public:
CG_FE_Evolution(FiniteElementSpace &fes_,
const Vector &lumpedmassmatrix_, FunctionCoefficient &inflow,
VectorFunctionCoefficient &vel, BilinearForm &M)
: TimeDependentOperator(lumpedmassmatrix_.Size()),
lumpedmassmatrix(lumpedmassmatrix_), fes(fes_),
I(M.SpMat().GetI()), J(M.SpMat().GetJ()), b_lumped(&fes),
u_inflow(&fes), conv_int(vel), mass_int()
{
u_inflow.ProjectCoefficient(inflow);
// For bound preservation the boundary condition \hat{u} is enforced
// via a lumped approximation to < (u_h - u_inflow) * min(v * n, 0 ), w >,
// i.e., (u_i - (u_inflow)_i) * \int_F \varphi_i * min(v * n, 0).
// The integral can be implemented as follows:
FunctionCoefficient fc1(one);
b_lumped.AddBdrFaceIntegrator(new BoundaryFlowIntegrator(fc1, vel, 1.0));
b_lumped.Assemble();
z.SetSize(lumpedmassmatrix.Size());
#ifdef MFEM_USE_MPI
pfes = dynamic_cast<ParFiniteElementSpace *>(&fes);
if (pfes)
{
// distribute the lumped mass matrix entries
Array<real_t> lumpedmassmatrix_array(lumpedmassmatrix.GetData(),
lumpedmassmatrix.Size());
pfes->GroupComm().Reduce<real_t>(lumpedmassmatrix_array,
GroupCommunicator::Sum);
pfes->GroupComm().Bcast(lumpedmassmatrix_array);
}
#endif
}
virtual void Mult(const Vector &x, Vector &y) const = 0;
/// Estimate a CFL-like forward Euler time step for the low-order (LO)
/// scheme, based on Lemma 4.3 in:
/// HIGH-ORDER MULTI-MATERIAL ALE HYDRODYNAMICS
/// Anderson, Dobrev, Kolev, Rieben, Tomov (2018).
///
/// The LO forward Euler update can be written in the form
/// M_L u^{n+1} = (M_L + dt * K_LO) u^n + dt * rhs_const,
/// where M_L is the lumped mass matrix and K_LO has nonnegative
/// off-diagonals and nonpositive diagonal.
/// Lemma 4.3 gives the sufficient condition for entrywise nonnegativity:
/// m_i + dt * (K_LO)_{ii} >= 0 for all i,
/// i.e., dt <= min_i m_i / (-(K_LO)_{ii}) over DOFs with (K_LO)_{ii} < 0.
///
/// Two estimates are returned:
/// - dt_local: uses per-element (unassembled) matrices; ignores overlap.
/// - dt_global: uses the globally assembled diagonal (sums element overlap).
void ComputeLOTimeStepEstimates(real_t &dt_local, real_t &dt_global) const;
virtual ~CG_FE_Evolution() { }
};
// High-order target scheme class
class HighOrderTargetScheme : public CG_FE_Evolution
{
private:
mutable Vector udot;
public:
HighOrderTargetScheme(FiniteElementSpace &fes_,
const Vector &lumpedmassmatrix_,
FunctionCoefficient &inflow,
VectorFunctionCoefficient &velocity, BilinearForm &M)
: CG_FE_Evolution(fes_, lumpedmassmatrix_, inflow, velocity, M)
{
udot.SetSize(lumpedmassmatrix.Size());
}
virtual void Mult(const Vector &x, Vector &y) const override;
};
// Low-order scheme class
class LowOrderScheme : public CG_FE_Evolution
{
public:
LowOrderScheme(FiniteElementSpace &fes_,
const Vector &lumpedmassmatrix_, FunctionCoefficient &inflow,
VectorFunctionCoefficient &velocity, BilinearForm &M)
: CG_FE_Evolution(fes_, lumpedmassmatrix_, inflow, velocity, M) { }
virtual void Mult(const Vector &x, Vector &y) const override
{
ComputeLOTimeDerivatives(x, y);
}
};
// Clip and Scale limiter class
class ClipAndScale : public CG_FE_Evolution
{
private:
mutable Array<real_t> umin, umax;
mutable Vector udot;
virtual void ComputeBounds(const Vector &u, Array<real_t> &u_min,
Array<real_t> &u_max) const;
public:
ClipAndScale(FiniteElementSpace &fes_,
const Vector &lumpedmassmatrix_, FunctionCoefficient &inflow,
VectorFunctionCoefficient &velocity, BilinearForm &M)
: CG_FE_Evolution(fes_, lumpedmassmatrix_, inflow, velocity, M)
{
umin.SetSize(lumpedmassmatrix.Size());
umax.SetSize(lumpedmassmatrix.Size());
udot.SetSize(lumpedmassmatrix.Size());
}
virtual void Mult(const Vector &x, Vector &y) const override;
virtual ~ClipAndScale() { }
};
void CG_FE_Evolution::ComputeLOTimeDerivatives(const Vector &u,
Vector &udot) const
{
udot = 0.0;
const int nE = fes.GetNE();
Array<int> dofs;
for (int e = 0; e < nE; e++)
{
auto element = fes.GetFE(e);
auto eltrans = fes.GetElementTransformation(e);
// assemble element matrix of convection operator
conv_int.AssembleElementMatrix(*element, *eltrans, Ke);
fes.GetElementDofs(e, dofs);
ue.SetSize(dofs.Size());
u.GetSubVector(dofs, ue);
re.SetSize(dofs.Size());
re = 0.0;
for (int i = 0; i < dofs.Size(); i++)
{
for (int j = 0; j < i; j++)
{
// add low-order stabilization with discrete upwinding
real_t dije = std::max(std::max(Ke(i,j), Ke(j,i)), real_t(0.0));
real_t diffusion = dije * (ue(j) - ue(i));
re(i) += diffusion;
re(j) -= diffusion;
}
}
// Add -K_e u_e to obtain (-K_e + D_e) u_e and add element contribution
// to global vector
Ke.AddMult(ue, re, -1.0);
udot.AddElementVector(dofs, re);
}
// add boundary condition (u - u_inflow) * b.
// This is under the assumption that b_lumped has been updated
subtract(u, u_inflow, z);
z *= b_lumped;
udot += z;
#ifdef MFEM_USE_MPI
if (pfes)
{
// Sum over the shared DOFs.
Array<real_t> udot_array(udot.GetData(), udot.Size());
pfes->GroupComm().Reduce<real_t>(udot_array, GroupCommunicator::Sum);
pfes->GroupComm().Bcast(udot_array);
}
#endif
// apply inverse lumped mass matrix
udot /= lumpedmassmatrix;
}
void CG_FE_Evolution::ComputeLOTimeStepEstimates(real_t &dt_local,
real_t &dt_global) const
{
// Elementwise (unassembled) estimate.
dt_local = std::numeric_limits<real_t>::infinity();
// Globally assembled diagonal of K_LO (in the DOF numbering of 'fes').
Vector kdiag(lumpedmassmatrix.Size());
kdiag = 0.0;
const int nE = fes.GetNE();
Array<int> dofs;
for (int e = 0; e < nE; e++)
{
auto element = fes.GetFE(e);
auto eltrans = fes.GetElementTransformation(e);
// Assemble element matrices for the LO operator:
// K_LO,e = (-K_e + D_e),
// where D_e is the discrete upwinding diffusion constructed from K_e.
conv_int.AssembleElementMatrix(*element, *eltrans, Ke);
mass_int.AssembleElementMatrix(*element, *eltrans, Me);
fes.GetElementDofs(e, dofs);
const int nd = dofs.Size();
Vector me(nd), kdiag_e(nd);
for (int i = 0; i < nd; i++)
{
// m_i^e = sum_j (M_e)_{ij} (row-sum lumping)
real_t mi = 0.0;
for (int j = 0; j < nd; j++) { mi += Me(i, j); }
me(i) = mi;
// Start with the diagonal from -K_e.
kdiag_e(i) = -Ke(i, i);
}
// Add diagonal contributions from the diffusion D_e:
// D_e has off-diagonal entries d_ij >= 0 and diagonal entries
// (D_e)_{ii} = -sum_{j != i} d_ij.
for (int i = 0; i < nd; i++)
{
for (int j = 0; j < i; j++)
{
const real_t d_ij =
std::max(std::max(Ke(i, j), Ke(j, i)), real_t(0.0));
kdiag_e(i) -= d_ij;
kdiag_e(j) -= d_ij;
}
}
// Per-element time step estimate (ignores overlap).
for (int i = 0; i < nd; i++)
{
if (kdiag_e(i) < 0.0)
{
dt_local = std::min(dt_local, me(i) / (-kdiag_e(i)));
}
}
// Contribute to the globally assembled diagonal.
for (int i = 0; i < nd; i++) { kdiag(dofs[i]) += kdiag_e(i); }
}
// Add boundary flow contribution (diagonal) from the LO evolution operator.
// Note: In this example set-up this is typically zero.
kdiag += b_lumped;
#ifdef MFEM_USE_MPI
if (pfes)
{
// Sum K_LO diagonal contributions over shared DOFs.
Array<real_t> kdiag_array(kdiag.GetData(), kdiag.Size());
pfes->GroupComm().Reduce<real_t>(kdiag_array, GroupCommunicator::Sum);
pfes->GroupComm().Bcast(kdiag_array);
}
#endif
// Global (assembled) estimate.
dt_global = std::numeric_limits<real_t>::infinity();
for (int i = 0; i < kdiag.Size(); i++)
{
if (kdiag(i) < 0.0)
{
dt_global = std::min(dt_global, lumpedmassmatrix(i) / (-kdiag(i)));
}
}
#ifdef MFEM_USE_MPI
if (pfes)
{
// Reduce to a global minimum over all MPI ranks.
real_t dt_local_glob = dt_local;
real_t dt_global_glob = dt_global;
MPI_Allreduce(&dt_local, &dt_local_glob, 1, MPITypeMap<real_t>::mpi_type,
MPI_MIN, pfes->GetComm());
MPI_Allreduce(&dt_global, &dt_global_glob, 1,
MPITypeMap<real_t>::mpi_type, MPI_MIN, pfes->GetComm());
dt_local = dt_local_glob;
dt_global = dt_global_glob;
}
#endif
}
void HighOrderTargetScheme::Mult(const Vector &x, Vector &y) const
{
y = 0.0;
// compute low-order time derivative for high-order stabilization
ComputeLOTimeDerivatives(x, udot);
Array<int> dofs;
for (int e = 0; e < fes.GetNE(); e++)
{
auto element = fes.GetFE(e);
auto eltrans = fes.GetElementTransformation(e);
// assemble element mass and convection matrices
conv_int.AssembleElementMatrix(*element, *eltrans, Ke);
mass_int.AssembleElementMatrix(*element, *eltrans, Me);
fes.GetElementDofs(e, dofs);
ue.SetSize(dofs.Size());
re.SetSize(dofs.Size());
udote.SetSize(dofs.Size());
x.GetSubVector(dofs, ue);
udot.GetSubVector(dofs, udote);
re = 0.0;
for (int i = 0; i < dofs.Size(); i++)
{
for (int j = 0; j < i; j++)
{
// add high-order stabilization without correction for low-order
// stabilization
real_t fije = Me(i,j) * (udote(i) - udote(j));
re(i) += fije;
re(j) -= fije;
}
}
// add convective term and add to global vector
Ke.AddMult(ue, re, -1.0);
y.AddElementVector(dofs, re);
}
// add boundary condition (u - u_inflow) * b (u - u_inflow) * b
subtract(x, u_inflow, z);
z *= b_lumped;
y += z;
#ifdef MFEM_USE_MPI
if (pfes)
{
// Sum over the shared DOFs.
Array<real_t> y_array(y.GetData(), y.Size());
pfes->GroupComm().Reduce<real_t>(y_array, GroupCommunicator::Sum);
pfes->GroupComm().Bcast(y_array);
}
#endif
// apply inverse lumped mass matrix
y /= lumpedmassmatrix;
}
void ClipAndScale::ComputeBounds(const Vector &u,
Array<real_t> &u_min,
Array<real_t> &u_max) const
{
// iterate over local number of dofs on this processor
// and compute maximum and minimum over local stencil
for (int i = 0; i < fes.GetVSize(); i++)
{
umin[i] = u(i);
umax[i] = u(i);
for (int k = I[i]; k < I[i+1]; k++)
{
int j = J[k];
umin[i] = std::min(umin[i], u(j));
umax[i] = std::max(umax[i], u(j));
}
}
#ifdef MFEM_USE_MPI
if (pfes)
{
// Reduce min and max over the shared DOFs.
pfes->GroupComm().Reduce<real_t>(umax, GroupCommunicator::Max);
pfes->GroupComm().Bcast(umax);
pfes->GroupComm().Reduce<real_t>(umin, GroupCommunicator::Min);
pfes->GroupComm().Bcast(umin);
}
#endif
}
void ClipAndScale::Mult(const Vector &x, Vector &y) const
{
y = 0.0;
// compute low-order time derivative for high-order
// stabilization and local bounds
ComputeLOTimeDerivatives(x, udot);
ComputeBounds(x, umin, umax);
Array<int> dofs;
for (int e = 0; e < fes.GetNE(); e++)
{
auto element = fes.GetFE(e);
auto eltrans = fes.GetElementTransformation(e);
// assemble element mass and convection matrices
conv_int.AssembleElementMatrix(*element, *eltrans, Ke);
mass_int.AssembleElementMatrix(*element, *eltrans, Me);
fes.GetElementDofs(e, dofs);
ue.SetSize(dofs.Size());
re.SetSize(dofs.Size());
udote.SetSize(dofs.Size());
fe.SetSize(dofs.Size());
fe_star.SetSize(dofs.Size());
gammae.SetSize(dofs.Size());
x.GetSubVector(dofs, ue);
udot.GetSubVector(dofs, udote);
re = 0.0;
fe = 0.0;
gammae = 0.0;
for (int i = 0; i < dofs.Size(); i++)
{
for (int j = 0; j < i; j++)
{
// add low-order diffusion
real_t dije = std::max(std::max(Ke(i,j), Ke(j,i)), real_t(0.0));
real_t diffusion = dije * (ue(j) - ue(i));
re(i) += diffusion;
re(j) -= diffusion;
// for bounding fluxes
gammae(i) += dije;
gammae(j) += dije;
// assemble raw antidifussive fluxes
// note that fije = - fjie
real_t fije = Me(i,j) * (udote(i) - udote(j)) - diffusion;
fe(i) += fije;
fe(j) -= fije;
}
}
// add convective term
Ke.AddMult(ue, re, -1.0);
gammae *= 2.0;
real_t P_plus = 0.0;
real_t P_minus = 0.0;
//Clip
for (int i = 0; i < dofs.Size(); i++)
{
// bounding fluxes to enforce u_i = u_i_min implies du/dt >= 0
// and u_i = u_i_max implies du/dt <= 0
real_t fie_max = gammae(i) * (umax[dofs[i]] - ue(i));
real_t fie_min = gammae(i) * (umin[dofs[i]] - ue(i));
fe_star(i) = std::min(std::max(fie_min, fe(i)), fie_max);
// track positive and negative contributions s
P_plus += std::max(fe_star(i), real_t(0.0));
P_minus += std::min(fe_star(i), real_t(0.0));
}
const real_t P = P_minus + P_plus;
//and Scale for the sum of fe_star to be 0, i.e., mass conservation
for (int i = 0; i < dofs.Size(); i++)
{
if (fe_star(i) > 0.0 && P > 0.0)
{
fe_star(i) *= - P_minus / P_plus;
}
else if (fe_star(i) < 0.0 && P < 0.0)
{
fe_star(i) *= - P_plus / P_minus;
}
}
// add limited antidiffusive fluxes to element contribution
// and add to global vector
re += fe_star;
y.AddElementVector(dofs, re);
}
// add boundary condition (u - u_inflow) * b
subtract(x, u_inflow, z);
z *= b_lumped;
y += z;
#ifdef MFEM_USE_MPI
if (pfes)
{
// Sum over the shared DOFs.
Array<real_t> y_array(y.GetData(), y.Size());
pfes->GroupComm().Reduce<real_t>(y_array, GroupCommunicator::Sum);
pfes->GroupComm().Bcast(y_array);
}
#endif
// apply inverse lumped mass matrix
y /= lumpedmassmatrix;
}
} // namespace mfem
+304 -117
View File
@@ -2,14 +2,14 @@
//
// Compile with: make ex9p
//
// Sample runs:
// DG sample runs:
// mpirun -np 4 ex9p -m ../data/periodic-segment.mesh -p 0 -dt 0.005
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 0 -dt 0.01
// mpirun -np 4 ex9p -m ../data/periodic-hexagon.mesh -p 0 -dt 0.01
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 1 -dt 0.005 -tf 9
// mpirun -np 4 ex9p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.005 -tf 9
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.002 -tf 9
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.02 -s 23 -tf 9
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.02 -s 13 -tf 9
// mpirun -np 4 ex9p -m ../data/star-q3.mesh -p 1 -rp 1 -dt 0.004 -tf 9
// mpirun -np 4 ex9p -m ../data/star-mixed.mesh -p 1 -rp 1 -dt 0.004 -tf 9
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9
@@ -20,7 +20,24 @@
// mpirun -np 4 ex9p -m ../data/periodic-cube.msh -p 0 -rs 1 -o 2 -tf 2
// mpirun -np 3 ex9p -m ../data/amr-hex.mesh -p 1 -rs 1 -rp 0 -dt 0.005 -tf 0.5
//
// Device sample runs:
// CG sample runs:
// mpirun -np 4 ex9p -m ../data/periodic-segment.mesh -p 0 -rp 5 -dt 0.00025 -sc 11 -o 1 -s 2 -vs 200
// mpirun -np 4 ex9p -m ../data/periodic-segment.mesh -p 0 -rp 5 -dt 0.00025 -sc 12 -o 1 -s 2 -vs 200
// mpirun -np 4 ex9p -m ../data/periodic-segment.mesh -p 0 -rp 5 -dt 0.00025 -sc 13 -o 1 -s 2 -vs 200
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 0 -rp 1 -dt 0.0025 -tf 2 -vs 20 -sc 11 -s 3 -o 2
// mpirun -np 4 ex9p -m ../data/periodic-hexagon.mesh -p 0 -rp 1 -dt 0.0025 -tf 2 -vs 20 -sc 11
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 1 -rp 3 -dt 0.002 -tf 9 -sc 11 -o 1 -s 2 -vs 20
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 1 -rp 1 -dt 0.002 -tf 9 -sc 11 -vs 20
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 1 -rp 3 -dt 0.002 -tf 9 -sc 13 -o 1 -s 2 -vs 20
// mpirun -np 4 ex9p -m ../data/star-mixed.mesh -p 1 -rp 2 -dt 0.004 -tf 9 -vs 20 -sc 11 -o 1 -s 2
// mpirun -np 4 ex9p -m ../data/star-q3.mesh -p 1 -rp 2 -dt 0.004 -tf 9 -vs 20 -sc 11 -o 1 -s 2
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9 -sc 11 -vs 20
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 2 -rp 2 -dt 0.005 -tf 9 -sc 12 -s 3 -o 2 -vs 20
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 3 -rp 4 -dt 0.0025 -tf 9 -vs 20 -sc 11 -s 2 -o 1
// mpirun -np 4 ex9p -m ../data/periodic-cube.mesh -p 0 -o 2 -s 3 -rp 1 -dt 0.01 -tf 8 -sc 11
// mpirun -np 4 ex9p -m ../data/periodic-cube.msh -p 0 -rp 1 -o 2 -s 3 -tf 2 -sc 11
//
// Device sample runs (DG only):
// mpirun -np 4 ex9p -pa
// mpirun -np 4 ex9p -ea
// mpirun -np 4 ex9p -fa
@@ -43,10 +60,15 @@
// with VisIt (visit.llnl.gov) and ParaView (paraview.org), as
// well as the optional saving with ADIOS2 (adios2.readthedocs.io)
// are also illustrated.
// Additionally, the example showcases the parallel implementation
// of an element-based Clip & Scale limiter for continuous finite
// elements, which is designed to be bound-preserving.
// For more detail, see https://doi.org/10.1142/13466.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ex9.hpp"
using namespace std;
using namespace mfem;
@@ -64,6 +86,9 @@ real_t u0_function(const Vector &x);
// Inflow boundary condition
real_t inflow_function(const Vector &x);
// Function f = 1 for lumped boundary operator
real_t one(const Vector &x) {return 1.0;}
// Mesh bounding box
Vector bb_min, bb_max;
@@ -92,7 +117,7 @@ private:
public:
AIR_prec(int blocksize_) : AIR_solver(NULL), blocksize(blocksize_) { }
void SetOperator(const Operator &op) override
void SetOperator(const Operator &op)
{
width = op.Width();
height = op.Height();
@@ -110,7 +135,7 @@ public:
AIR_solver->SetMaxLevels(50);
}
void Mult(const Vector &x, Vector &y) const override
virtual void Mult(const Vector &x, Vector &y) const
{
// Scale the rhs by block inverse and solve system
HypreParVector z_s;
@@ -119,7 +144,7 @@ public:
AIR_solver->Mult(z_s, y);
}
~AIR_prec() override
~AIR_prec()
{
delete AIR_solver;
}
@@ -137,7 +162,8 @@ private:
Solver *prec;
real_t dt;
public:
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes,
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_,
const FiniteElementSpace &fes,
PrecType prec_type)
: M(M_),
K(K_),
@@ -185,17 +211,17 @@ public:
}
}
void SetOperator(const Operator &op) override
void SetOperator(const Operator &op)
{
linear_solver.SetOperator(op);
}
void Mult(const Vector &x, Vector &y) const override
virtual void Mult(const Vector &x, Vector &y) const
{
linear_solver.Mult(x, y);
}
~DG_Solver() override
~DG_Solver()
{
delete prec;
delete A;
@@ -208,7 +234,7 @@ public:
and advection matrices, and b describes the flow on the boundary. This can
be written as a general ODE, du/dt = M^{-1} (K u + b), and this class is
used to evaluate the right-hand side. */
class FE_Evolution : public TimeDependentOperator
class DG_FE_Evolution : public TimeDependentOperator
{
private:
OperatorHandle M, K;
@@ -220,16 +246,15 @@ private:
mutable Vector z;
public:
FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, const Vector &b_,
PrecType prec_type);
DG_FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, const Vector &b_,
PrecType prec_type);
void Mult(const Vector &x, Vector &y) const override;
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
~FE_Evolution() override;
virtual ~DG_FE_Evolution();
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI and HYPRE.
@@ -249,6 +274,7 @@ int main(int argc, char *argv[])
bool fa = false;
const char *device_config = "cpu";
int ode_solver_type = 4;
int scheme = 1;
real_t t_final = 10.0;
real_t dt = 0.01;
bool visualization = true;
@@ -285,7 +311,17 @@ int main(int argc, char *argv[])
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::Types.c_str());
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
" 11 - Backward Euler,\n\t"
" 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t"
" 22 - Implicit Midpoint Method,\n\t"
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
args.AddOption(&scheme, "-sc", "--scheme",
"FE scheme: 1 - DG high-order, unstabilized,\n\t"
" 11 - CG low-order,\n\t"
" 12 - CG high-order, stabilized,\n\t"
" 13 - CG high-order, stabilized, limited.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -323,17 +359,82 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
const bool DG = (scheme < 10);
// Limiter is only implemented to run on cpu.
if (!DG && strcmp(device_config, "cuda") == 0)
{
if (Mpi::Root())
{
cout << "Cuda not supported for this CG implementation" << endl;
}
return 2;
}
Device device(device_config);
if (Mpi::Root()) { device.Print(); }
if (Mpi::Root())
{
device.Print();
}
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
// Nonconforming meshes are not feasible for continuous elements
if (!DG && !mesh->Conforming())
{
if (Mpi::Root())
{
cout << "CG needs a conforming mesh." << endl;
}
return 3;
}
// 4. Define the ODE solver used for time integration.
// Several explicit Runge-Kutta methods are available.
// The CG Limiter is only implemented for explicit
// time-stepping methods.
if (!DG && ode_solver_type > 10)
{
cout << "The CG methods are supported only with explicit RK schemes.\n";
return 4;
}
// Limiter and low order scheme are only provably
// bound preserving when employing SSP-RK time-stepping methods
else if ((scheme == 11 || scheme == 13) && ode_solver_type > 3)
{
if (Mpi::Root())
{
MFEM_WARNING("Non-SSP-RK mehod! Bounds might be violated.");
}
}
unique_ptr<ODESolver> ode_solver = nullptr;
switch (ode_solver_type)
{
// Explicit methods
case 1: ode_solver.reset(new ForwardEulerSolver); break;
case 2: ode_solver.reset(new RK2Solver(1.0)); break;
case 3: ode_solver.reset(new RK3SSPSolver); break;
case 4: ode_solver.reset(new RK4Solver); break;
case 6: ode_solver.reset(new RK6Solver); break;
// Implicit (L-stable) methods
case 11: ode_solver.reset(new BackwardEulerSolver); break;
case 12: ode_solver.reset(new SDIRK23Solver(2)); break;
case 13: ode_solver.reset(new SDIRK33Solver); break;
// Implicit A-stable methods (not L-stable)
case 22: ode_solver.reset(new ImplicitMidpointSolver); break;
case 23: ode_solver.reset(new SDIRK23Solver); break;
case 24: ode_solver.reset(new SDIRK34Solver); break;
default:
if (Mpi::Root())
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
delete mesh;
return 5;
}
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
@@ -352,17 +453,31 @@ int main(int argc, char *argv[])
// 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);
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
pmesh.UniformRefinement();
}
// 7. Define the parallel discontinuous DG finite element space on the
// parallel refined mesh of the given polynomial order.
DG_FECollection fec(order, dim, BasisType::GaussLobatto);
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
// 7. Define the parallel discontinuous DG finite element or continuouts CG
// space on the parallel refined mesh of the given polynomial order.
DG_FECollection fec_DG(order, dim, BasisType::GaussLobatto);
H1_FECollection fec_CG(order, dim, BasisType::Positive);
unique_ptr<ParFiniteElementSpace> fes = nullptr;
switch (scheme)
{
case 1: fes.reset(new ParFiniteElementSpace(&pmesh, &fec_DG)); break;
case 11:
case 12:
case 13: fes.reset(new ParFiniteElementSpace(&pmesh, &fec_CG)); break;
default:
if (Mpi::Root())
{
cout << "Unknown scheme: " << scheme << '\n';
}
return 6;
}
HYPRE_BigInt global_vSize = fes->GlobalTrueVSize();
if (Mpi::Root())
@@ -377,52 +492,82 @@ int main(int argc, char *argv[])
FunctionCoefficient inflow(inflow_function);
FunctionCoefficient u0(u0_function);
ParBilinearForm *m = new ParBilinearForm(fes);
ParBilinearForm *k = new ParBilinearForm(fes);
if (pa)
ParBilinearForm m(fes.get());
ParBilinearForm k(fes.get());
if (DG)
{
m->SetAssemblyLevel(AssemblyLevel::PARTIAL);
k->SetAssemblyLevel(AssemblyLevel::PARTIAL);
if (pa)
{
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
else if (ea)
{
m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
else if (fa)
{
m.SetAssemblyLevel(AssemblyLevel::FULL);
k.SetAssemblyLevel(AssemblyLevel::FULL);
}
}
else if (ea)
else if (scheme == 13 && (pa || ea))
{
m->SetAssemblyLevel(AssemblyLevel::ELEMENT);
k->SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
else if (fa)
{
m->SetAssemblyLevel(AssemblyLevel::FULL);
k->SetAssemblyLevel(AssemblyLevel::FULL);
if (Mpi::Root())
{
cout << "The CG Limiter needs full assembly of the mass matrix to "
<< "obtain the local stencil via its sparsity pattern.\n";
}
return 7;
}
m->AddDomainIntegrator(new MassIntegrator);
m.AddDomainIntegrator(new MassIntegrator);
m.Assemble();
m.Finalize();
constexpr real_t alpha = -1.0;
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
k->AddInteriorFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
k->AddBdrFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
ParLinearForm *b = new ParLinearForm(fes);
b->AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, alpha));
int skip_zeros = 0;
m->Assemble();
k->Assemble(skip_zeros);
b->Assemble();
m->Finalize();
k->Finalize(skip_zeros);
Vector lumpedmassmatrix(m.Height());
// The convective bilinear form is not needed in the CG case.
if (DG)
{
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
k.AddInteriorFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
k.AddBdrFaceIntegrator(
new NonconservativeDGTraceIntegrator(velocity, alpha));
HypreParVector *B = b->ParallelAssemble();
k.Assemble(skip_zeros);
k.Finalize(skip_zeros);
}
// lumped mass matrix not needed in the DG case
else
{
ParBilinearForm mL(fes.get());
mL.AddDomainIntegrator(new LumpedIntegrator(new MassIntegrator));
mL.Assemble();
mL.Finalize();
mL.SpMat().GetDiag(lumpedmassmatrix);
}
ParLinearForm b(fes.get());
b.AddBdrFaceIntegrator(new BoundaryFlowIntegrator(inflow, velocity, alpha));
b.Assemble();
unique_ptr<HypreParVector> B(b.ParallelAssemble());
// 9. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
// GLVis visualization.
ParGridFunction *u = new ParGridFunction(fes);
u->ProjectCoefficient(u0);
HypreParVector *U = u->GetTrueDofs();
ParGridFunction u(fes.get());
u.ProjectCoefficient(u0);
// DG uses a HypreParVector to communicate between processess.
// In the implementation of the element-based Clip & Scale limiter we do
// this by hand.
unique_ptr<HypreParVector> U = nullptr;
if (DG) { U.reset(u.GetTrueDofs()); }
{
ostringstream mesh_name, sol_name;
@@ -430,15 +575,15 @@ int main(int argc, char *argv[])
sol_name << "ex9-init." << setfill('0') << setw(6) << myid;
ofstream omesh(mesh_name.str().c_str());
omesh.precision(precision);
pmesh->Print(omesh);
pmesh.Print(omesh);
ofstream osol(sol_name.str().c_str());
osol.precision(precision);
u->Save(osol);
u.Save(osol);
}
// Create data collection for solution output: either VisItDataCollection for
// ascii data files, or SidreDataCollection for binary data files.
DataCollection *dc = NULL;
unique_ptr<DataCollection> dc = nullptr;
if (visit)
{
if (binary)
@@ -451,23 +596,23 @@ int main(int argc, char *argv[])
}
else
{
dc = new VisItDataCollection("Example9-Parallel", pmesh);
dc.reset(new VisItDataCollection("Example9-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->RegisterField("solution", &u);
dc->SetCycle(0);
dc->SetTime(0.0);
dc->Save();
}
ParaViewDataCollection *pd = NULL;
unique_ptr<ParaViewDataCollection> pd = nullptr;
if (paraview)
{
pd = new ParaViewDataCollection("Example9P", pmesh);
pd.reset(new ParaViewDataCollection("Example9P", &pmesh));
pd->SetPrefixPath("ParaView");
pd->RegisterField("solution", u);
pd->RegisterField("solution", &u);
pd->SetLevelsOfDetail(order);
pd->SetDataFormat(VTKFormat::BINARY);
pd->SetHighOrderOutput(true);
@@ -479,7 +624,7 @@ int main(int argc, char *argv[])
// Optionally output a BP (binary pack) file using ADIOS2. This can be
// visualized with the ParaView VTX reader.
#ifdef MFEM_USE_ADIOS2
ADIOS2DataCollection *adios2_dc = NULL;
unique_ptr<ADIOS2DataCollection> adios2_dc = nullptr;
if (adios2)
{
std::string postfix(mesh_file);
@@ -487,7 +632,7 @@ int main(int argc, char *argv[])
postfix += "_o" + std::to_string(order);
const std::string collection_name = "ex9-p-" + postfix + ".bp";
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
adios2_dc.reset(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);
@@ -521,7 +666,7 @@ int main(int argc, char *argv[])
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout.precision(precision);
sout << "solution\n" << *pmesh << *u;
sout << "solution\n" << pmesh << u;
sout << "pause\n";
sout << flush;
if (Mpi::Root())
@@ -535,19 +680,50 @@ int main(int argc, char *argv[])
// 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).
FE_Evolution adv(*m, *k, *B, prec_type);
unique_ptr<TimeDependentOperator> adv = nullptr;
switch (scheme)
{
case 1: adv.reset(new DG_FE_Evolution(m, k, *B, prec_type)); break;
case 11: adv.reset(new LowOrderScheme(*fes, lumpedmassmatrix,
inflow, velocity, m)); break;
case 12: adv.reset(new HighOrderTargetScheme(*fes, lumpedmassmatrix,
inflow, velocity, m)); break;
case 13: adv.reset(new ClipAndScale(*fes, lumpedmassmatrix,
inflow, velocity, m)); break;
}
if (!DG)
{
auto *cg_adv = dynamic_cast<CG_FE_Evolution *>(adv.get());
MFEM_VERIFY(cg_adv != NULL, "Expected a CG_FE_Evolution operator.");
real_t dt_lo_local = 0.0, dt_lo_global = 0.0;
cg_adv->ComputeLOTimeStepEstimates(dt_lo_local, dt_lo_global);
if (Mpi::Root())
{
cout << "CG low-order time step estimate (local matrices): "
<< dt_lo_local << '\n';
cout << "CG low-order time step estimate (global assembled): "
<< dt_lo_global << '\n';
std::cout << dt_lo_global / dt_lo_local << std::endl;
cout << "Commandl line dt: " << dt << std::endl;
}
// dt = dt_lo_global;
}
real_t t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
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++;
if (DG) { ode_solver->Step(*U, t, dt_real); }
else { ode_solver->Step(u, t, dt_real); }
ti++;
done = (t >= t_final - 1e-8*dt);
if (done || ti % vis_steps == 0)
@@ -557,14 +733,15 @@ int main(int argc, char *argv[])
cout << "time step: " << ti << ", time: " << t << endl;
}
// 11. Extract the parallel grid function corresponding to the finite
// element approximation U (the local solution on each processor).
*u = *U;
// 11. In case of DG extract the parallel grid function corresponding
// to the finite element approximation U.
// (the local solution on each processor).
if (DG) { u = *U; }
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << *u << flush;
sout << "solution\n" << pmesh << u << flush;
}
if (visit)
@@ -596,39 +773,23 @@ int main(int argc, char *argv[])
// 12. Save the final solution in parallel. This output can be viewed later
// using GLVis: "glvis -np <np> -m ex9-mesh -g ex9-final".
{
*u = *U;
ostringstream sol_name;
sol_name << "ex9-final." << setfill('0') << setw(6) << myid;
ofstream osol(sol_name.str().c_str());
osol.precision(precision);
u->Save(osol);
u.Save(osol);
}
// 13. Free the used memory.
delete U;
delete u;
delete B;
delete b;
delete k;
delete m;
delete fes;
delete pmesh;
delete pd;
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
delete adios2_dc;
}
#endif
delete dc;
ConstantCoefficient zero(0.0);
std::cout << "Norm: " << u.ComputeL2Error(zero) << std::endl;
return 0;
}
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
const Vector &b_, PrecType prec_type)
// Implementation of class DG_FE_Evolution
DG_FE_Evolution::DG_FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
const Vector &b_, PrecType prec_type)
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()), b(b_),
M_solver(M_.ParFESpace()->GetComm()),
z(height)
@@ -674,7 +835,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
// u_t = M^{-1}(Ku + b),
// by solving associated linear system
// (M - dt*K) d = K*u + b
void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
void DG_FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
{
K->Mult(x, z);
z += b;
@@ -682,7 +843,7 @@ void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
dg_solver->Mult(z, k);
}
void FE_Evolution::Mult(const Vector &x, Vector &y) const
void DG_FE_Evolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K->Mult(x, z);
@@ -690,13 +851,12 @@ void FE_Evolution::Mult(const Vector &x, Vector &y) const
M_solver.Mult(z, y);
}
FE_Evolution::~FE_Evolution()
DG_FE_Evolution::~DG_FE_Evolution()
{
delete M_prec;
delete dg_solver;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
{
@@ -717,9 +877,17 @@ void velocity_function(const Vector &x, Vector &v)
// 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.);
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;
@@ -731,9 +899,18 @@ void velocity_function(const Vector &x, Vector &v)
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;
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;
}
@@ -745,9 +922,18 @@ void velocity_function(const Vector &x, Vector &v)
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;
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;
}
@@ -815,7 +1001,8 @@ real_t inflow_function(const Vector &x)
case 0:
case 1:
case 2:
case 3: return 0.0;
case 3:
return 0.0;
}
return 0.0;
}
-6
View File
@@ -173,12 +173,6 @@ ex11p-test-cpardiso: ex11p
@$(call mfem-test,$<, $(RUN_MPI), MKL_CPARDISO example,--cpardiso)
test-par-YES: ex11p-test-cpardiso
endif
ifeq ($(MFEM_USE_CEED),YES)
ex1p-test-ceed: ex1p
@$(call mfem-test,$<, $(RUN_MPI),\
Parallel libCEED example,-d ceed-cpu -pa -a)
test-par-YES: ex1p-test-ceed
endif
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
+2 -8
View File
@@ -16,16 +16,10 @@
// multi-physics applications.
//
// This particular example is only for serial runtimes.
// For non-conforming meshes please have a look at example
// "ex2p.cpp".
#include "example_utils.hpp"
#include "mfem.hpp"
#ifndef MFEM_USE_MOONOLITH
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
#endif
using namespace mfem;
using namespace std;
@@ -221,8 +215,8 @@ int main(int argc, char *argv[])
mfem::out << "l2 error: src: " << src_err << ", dest: " << dest_err
<< std::endl;
plot(*src_mesh, src_fun, "source", 0);
plot(*dest_mesh, dest_fun, "destination", 1);
plot(*src_mesh, src_fun, "source");
plot(*dest_mesh, dest_fun, "destination");
}
}
else
+16 -54
View File
@@ -8,23 +8,18 @@
// mpirun -np 4 ex1p -s ../../data/inline-hex.mesh -d ../../data/inline-tet.mesh
//
// Description: This example code demonstrates the use of MFEM for transferring
// discrete fields from one conforming finite element mesh to another. The
// discrete fields from one finite element mesh to another. The
// meshes can be of arbitrary shape and completely unrelated with
// each other. This feature can be used for implementing immersed
// domain methods for fluid-structure interaction or general
// multi-physics applications.
//
// This particular example is for parallel runtimes. Vector FE is
// an experimental feature in parallel. For non-conforming meshes
// please have a look at example "ex2p.cpp".
// an experimental feature in parallel.
#include "example_utils.hpp"
#include "mfem.hpp"
#ifndef MFEM_USE_MOONOLITH
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
#endif
using namespace mfem;
using namespace std;
@@ -55,8 +50,6 @@ int main(int argc, char *argv[])
int dest_fe_order = 1;
bool visualization = true;
bool use_vector_fe = false;
bool use_h1 = true;
bool use_vector_space = false;
bool verbose = false;
bool assemble_mass_and_coupling_together = true;
@@ -79,28 +72,14 @@ int main(int argc, char *argv[])
args.AddOption(&verbose, "-verb", "--verbose", "--no-verb", "--no-verbose",
"Enable/Disable verbose output");
args.AddOption(&use_vector_fe, "-vfe", "--use_vector_fe", "-no-vfe",
"--no-vector_fe",
"Use RT|ND vector finite elements (Experimental)");
args.AddOption(&use_vector_space, "-vfs", "--use_vector_space", "-no-vfs",
"--no-vector_space",
"Use Lagrange vector finite elements (Experimental)");
args.AddOption(&use_h1, "-h1", "--use-h1", "-nh1", "--no-h1",
"Use H1 collection");
"--no-vector_fe", "Use vector finite elements (Experimental)");
args.AddOption(&assemble_mass_and_coupling_together, "-act",
"--assemble_mass_and_coupling_together", "-no-act",
"--no-assemble_mass_and_coupling_together",
"Assemble mass and coupling operators together (better for "
"non-affine elements)");
"Assemble mass and coupling operators together (better for non-affine elements)");
args.Parse();
check_options(args);
if (use_vector_fe && use_vector_space)
{
mfem::err <<
"WARNING: use_vector_fe and use_vector_space options"
"are both true, ignoring use_vector_fe\n";
}
shared_ptr<Mesh> src_mesh, dest_mesh;
ifstream imesh;
@@ -190,30 +169,17 @@ int main(int argc, char *argv[])
}
else
{
if (use_h1)
{
src_fe_coll =
make_shared<H1_FECollection>(source_fe_order, src_mesh->Dimension());
dest_fe_coll =
make_shared<H1_FECollection>(dest_fe_order, dest_mesh->Dimension());
}
else
{
src_fe_coll =
make_shared<L2_FECollection>(source_fe_order, src_mesh->Dimension());
dest_fe_coll =
make_shared<L2_FECollection>(dest_fe_order, dest_mesh->Dimension());
}
src_fe_coll =
make_shared<L2_FECollection>(source_fe_order, src_mesh->Dimension());
dest_fe_coll =
make_shared<L2_FECollection>(dest_fe_order, dest_mesh->Dimension());
}
auto src_fe = make_shared<ParFiniteElementSpace>(
p_src_mesh.get(), src_fe_coll.get(),
use_vector_space ? src_mesh->Dimension() : 1);
auto src_fe =
make_shared<ParFiniteElementSpace>(p_src_mesh.get(), src_fe_coll.get());
auto dest_fe = make_shared<ParFiniteElementSpace>(
p_dest_mesh.get(), dest_fe_coll.get(),
use_vector_space ? dest_mesh->Dimension() : 1);
auto dest_fe =
make_shared<ParFiniteElementSpace>(p_dest_mesh.get(), dest_fe_coll.get());
ParGridFunction src_fun(src_fe.get());
@@ -223,7 +189,7 @@ int main(int argc, char *argv[])
// To be used with vector fe
VectorFunctionCoefficient vector_coeff(dim, &vector_fun);
if (use_vector_fe || use_vector_space)
if (use_vector_fe)
{
src_fun.ProjectCoefficient(vector_coeff);
src_fun.Update();
@@ -243,11 +209,7 @@ int main(int argc, char *argv[])
assemble_mass_and_coupling_together);
assembler.SetVerbose(verbose);
if (use_vector_space)
{
assembler.AddMortarIntegrator(make_shared<LagrangeVectorL2MortarIntegrator>());
}
else if (use_vector_fe)
if (use_vector_fe)
{
assembler.AddMortarIntegrator(make_shared<VectorL2MortarIntegrator>());
}
@@ -281,8 +243,8 @@ int main(int argc, char *argv[])
<< std::endl;
}
plot(*p_src_mesh, src_fun, "source", 0);
plot(*p_dest_mesh, dest_fun, "destination", 1);
plot(*p_src_mesh, src_fun, "source");
plot(*p_dest_mesh, dest_fun, "destination");
}
}
else
+2 -6
View File
@@ -20,10 +20,6 @@
#include "example_utils.hpp"
#include "mfem.hpp"
#ifndef MFEM_USE_MOONOLITH
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
#endif
using namespace mfem;
using namespace std;
@@ -190,8 +186,8 @@ int main(int argc, char *argv[])
<< std::endl;
}
plot(*p_src_mesh, src_fun, "source", 0);
plot(*p_dest_mesh, dest_fun, "destination", 1);
plot(*p_src_mesh, src_fun, "source");
plot(*p_dest_mesh, dest_fun, "destination");
}
}
else
+1 -15
View File
@@ -84,8 +84,7 @@ void vector_fun(const mfem::Vector &x, mfem::Vector &f)
f = n;
}
inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title,
const int plot_number = 0)
inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title)
{
using namespace std;
using namespace mfem;
@@ -104,18 +103,5 @@ inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title,
sol_sock.precision(8);
sol_sock << "solution\n" << mesh << x
<< "window_title '"<< title << "'\n" << flush;
sol_sock << "window_geometry ";
sol_sock << (plot_number * 600) << " " << 0 << " " << 600 << " " << 600 <<
"\n";
if (mesh.Dimension() == 2)
{
sol_sock << "keys jRmclA\n";
}
else
{
sol_sock << "keys rmclAa\n";
}
sol_sock << flush;
}
-24
View File
@@ -64,7 +64,6 @@ set(SRCS
datacollection.cpp
dgmassinv.cpp
doftrans.cpp
dfem/doperator.cpp
eltrans.cpp
batchitrans.cpp
estimators.cpp
@@ -82,8 +81,6 @@ set(SRCS
fe/fe_ser.cpp
fe_coll.cpp
fespace.cpp
derefmat_op.cpp
pderefmat_op.cpp
geom.cpp
gridfunc.cpp
hybridization.cpp
@@ -164,18 +161,12 @@ set(SRCS
transfer.cpp
hyperbolic.cpp
integrator.cpp
bounds.cpp
)
set(HDRS
bilinearform.hpp
bilinearform_ext.hpp
bilininteg.hpp
integ/lininteg_domain_kernels.hpp
integ/bilininteg_dgdiffusion_kernels.hpp
integ/bilininteg_dgtrace_kernels.hpp
integ/bilininteg_vecdiffusion_kernels.hpp
integ/bilininteg_convection_kernels.hpp
integ/bilininteg_diffusion_kernels.hpp
integ/bilininteg_elasticity_kernels.hpp
integ/bilininteg_hcurl_kernels.hpp
@@ -189,14 +180,6 @@ set(HDRS
dgmassinv.hpp
dgmassinv_kernels.hpp
doftrans.hpp
dfem/doperator.hpp
dfem/fieldoperator.hpp
dfem/integrate.hpp
dfem/parameterspace.hpp
dfem/qfunction_apply.hpp
dfem/qfunction_transform.hpp
dfem/tuple.hpp
dfem/util.hpp
eltrans.hpp
estimators.hpp
fe.hpp
@@ -246,20 +229,14 @@ set(HDRS
lor/lor_ams.hpp
lor/lor_batched.hpp
lor/lor_h1.hpp
lor/lor_dg.hpp
lor/lor_nd.hpp
lor/lor_rt.hpp
lor/lor_h1_impl.hpp
lor/lor_dg_impl.hpp
lor/lor_nd_impl.hpp
lor/lor_rt_impl.hpp
lor/lor_util.hpp
multigrid.hpp
nonlinearform.hpp
nonlinearform_ext.hpp
nonlininteg.hpp
qfunction.hpp
qinterp/det.hpp
qinterp/eval.hpp
qinterp/eval_hdiv.hpp
qinterp/grad.hpp
@@ -286,7 +263,6 @@ set(HDRS
transfer.hpp
hyperbolic.hpp
integrator.hpp
bounds.hpp
)
if (MFEM_USE_SIDRE)
-1
View File
@@ -515,7 +515,6 @@ struct InvTNewtonSolver<Geometry::SEGMENT, SDim, SType, max_team_x>
phys_tol += pptr[idx + d * npts] * pptr[idx + d * npts];
}
phys_tol = fmax(phys_rtol * phys_rtol, phys_tol * phys_rtol * phys_rtol);
hit_bdr[0] = prev_hit_bdr[0] = false;
}
// for each iteration
while (true)
+33 -19
View File
@@ -466,6 +466,7 @@ void BilinearForm::Assemble(int skip_zeros)
}
ElementTransformation *eltrans;
DofTransformation * doftrans;
Mesh *mesh = fes -> GetMesh();
DenseMatrix elmat, *elmat_p;
@@ -502,14 +503,13 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
DofTransformation doftrans;
// Element-wise integration
for (int i = 0; i < fes -> GetNE(); i++)
{
// Set both doftrans (potentially needed to assemble the element
// matrix) and vdofs, which is also needed when the element matrices
// are pre-assembled.
fes->GetElementVDofs(i, vdofs, doftrans);
doftrans = fes->GetElementVDofs(i, vdofs);
if (element_matrices)
{
elmat_p = &(*element_matrices)(i);
@@ -547,7 +547,10 @@ void BilinearForm::Assemble(int skip_zeros)
{
elmat_p = &elmat;
}
doftrans.TransformDual(elmat);
if (doftrans)
{
doftrans->TransformDual(elmat);
}
elmat_p = &elmat;
}
if (static_cond)
@@ -625,14 +628,13 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
DofTransformation doftrans;
for (int i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
const FiniteElement &be = *fes->GetBE(i);
fes -> GetBdrElementVDofs (i, vdofs, doftrans);
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
eltrans = fes -> GetBdrElementTransformation (i);
int k = 0;
for (; k < boundary_integs.Size(); k++)
@@ -652,7 +654,10 @@ void BilinearForm::Assemble(int skip_zeros)
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
elmat += elemmat;
}
doftrans.TransformDual(elmat);
if (doftrans)
{
doftrans->TransformDual(elmat);
}
elmat_p = &elmat;
if (!static_cond)
{
@@ -1525,6 +1530,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
}
ElementTransformation *eltrans;
DofTransformation * dom_dof_trans;
DofTransformation * ran_dof_trans;
DenseMatrix elmat;
Mesh *mesh = test_fes -> GetMesh();
@@ -1547,12 +1554,11 @@ void MixedBilinearForm::Assemble(int skip_zeros)
}
}
DofTransformation dom_dof_trans, ran_dof_trans;
for (int i = 0; i < test_fes -> GetNE(); i++)
{
const int elem_attr = mesh->GetAttribute(i);
trial_fes->GetElementVDofs (i, trial_vdofs, dom_dof_trans);
test_fes->GetElementVDofs (i, test_vdofs, ran_dof_trans);
dom_dof_trans = trial_fes -> GetElementVDofs (i, trial_vdofs);
ran_dof_trans = test_fes -> GetElementVDofs (i, test_vdofs);
eltrans = test_fes -> GetElementTransformation (i);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
@@ -1568,7 +1574,10 @@ void MixedBilinearForm::Assemble(int skip_zeros)
elmat += elemmat;
}
}
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
if (ran_dof_trans || dom_dof_trans)
{
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
}
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
}
}
@@ -1596,14 +1605,13 @@ void MixedBilinearForm::Assemble(int skip_zeros)
}
}
DofTransformation dom_dof_trans, ran_dof_trans;
for (int i = 0; i < test_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
trial_fes->GetBdrElementVDofs (i, trial_vdofs, dom_dof_trans);
test_fes->GetBdrElementVDofs (i, test_vdofs, ran_dof_trans);
dom_dof_trans = trial_fes -> GetBdrElementVDofs (i, trial_vdofs);
ran_dof_trans = test_fes -> GetBdrElementVDofs (i, test_vdofs);
eltrans = test_fes -> GetBdrElementTransformation (i);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
@@ -1618,7 +1626,10 @@ void MixedBilinearForm::Assemble(int skip_zeros)
*eltrans, elemmat);
elmat += elemmat;
}
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
if (ran_dof_trans || dom_dof_trans)
{
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
}
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
}
}
@@ -2396,6 +2407,8 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
}
ElementTransformation *eltrans;
DofTransformation * dom_dof_trans;
DofTransformation * ran_dof_trans;
DenseMatrix elmat;
Mesh *mesh = test_fes->GetMesh();
@@ -2418,13 +2431,11 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
}
}
DofTransformation dom_dof_trans;
DofTransformation ran_dof_trans;
for (int i = 0; i < test_fes->GetNE(); i++)
{
const int elem_attr = mesh->GetAttribute(i);
trial_fes->GetElementVDofs(i, trial_vdofs, dom_dof_trans);
test_fes->GetElementVDofs(i, test_vdofs, ran_dof_trans);
dom_dof_trans = trial_fes->GetElementVDofs(i, trial_vdofs);
ran_dof_trans = test_fes->GetElementVDofs(i, test_vdofs);
eltrans = test_fes->GetElementTransformation(i);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
@@ -2440,7 +2451,10 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
elmat += elemmat;
}
}
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
if (ran_dof_trans || dom_dof_trans)
{
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
}
mat->SetSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
}
}
+2 -2
View File
@@ -569,7 +569,7 @@ public:
/// @brief Compute and store internally all element matrices.
///
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLevel(), this will
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLeve(), this will
/// use efficient (device-accelerated) assembly of the element matrices.
void ComputeElementMatrices();
@@ -578,7 +578,7 @@ public:
/// @brief Return a DenseTensor containing the assembled element matrices.
///
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLevel(), this will
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLeve(), this will
/// use efficient (device-accelerated) assembly of the element matrices.
const DenseTensor &GetElementMatrices();
+244 -244
View File
@@ -78,7 +78,7 @@ void MFBilinearFormExtension::AssembleDiagonal(Vector &y) const
dynamic_cast<const ElementRestriction*>(elem_restrict);
if (H1elem_restrict)
{
H1elem_restrict->AbsMultTranspose(localY, y);
H1elem_restrict->MultTransposeUnsigned(localY, y);
}
else
{
@@ -266,7 +266,11 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
// Gather the attributes on the host from all the elements
const Mesh &mesh = *trial_fes->GetMesh();
elem_attributes = &mesh.GetElementAttributes();
elem_attributes.SetSize(mesh.GetNE());
for (int i = 0; i < mesh.GetNE(); ++i)
{
elem_attributes[i] = mesh.GetAttribute(i);
}
}
// Construct face restriction operators only if the bilinear form has
@@ -325,7 +329,45 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
bdr_face_dYdn.SetSize(bdr_face_restrict_lex->Height());
}
bdr_face_attributes = &trial_fes->GetMesh()->GetBdrFaceAttributes();
const Mesh &mesh = *trial_fes->GetMesh();
// See LinearFormExtension::Update for explanation of f_to_be logic.
std::unordered_map<int,int> f_to_be;
for (int i = 0; i < mesh.GetNBE(); ++i)
{
const int f = mesh.GetBdrElementFaceIndex(i);
f_to_be[f] = i;
}
const int nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
bdr_attributes.SetSize(nf_bdr);
int f_ind = 0;
int missing_bdr_elems = 0;
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
if (!mesh.GetFaceInformation(f).IsOfFaceType(FaceType::Boundary))
{
continue;
}
int attribute = 1; // default value
if (f_to_be.find(f) != f_to_be.end())
{
const int be = f_to_be[f];
attribute = mesh.GetBdrAttribute(be);
}
else
{
// If a boundary face does not correspond to the a boundary element,
// we assign it the default attribute of 1. We also generate a
// warning at runtime with the number of such missing elements.
++missing_bdr_elems;
}
bdr_attributes[f_ind] = attribute;
++f_ind;
}
if (missing_bdr_elems)
{
MFEM_WARNING("Missing " << missing_bdr_elems << " boundary elements "
"for boundary faces.");
}
}
}
@@ -387,7 +429,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
const int attr = d_attr[e];
if (attr <= 0 || d_m[attr - 1] == 0)
if (d_m[attr - 1] == 0)
{
for (int i = 0; i < nd; ++i)
{
@@ -408,13 +450,13 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
for (int i = 0; i < iSz; ++i)
{
assemble_diagonal_with_markers(*integrators[i], elem_markers[i],
*elem_attributes, localY);
elem_attributes, localY);
}
const ElementRestriction* H1elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict);
if (H1elem_restrict)
{
H1elem_restrict->AbsMultTranspose(localY, y);
H1elem_restrict->MultTransposeUnsigned(localY, y);
}
else
{
@@ -434,7 +476,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
for (int i = 0; i < iSz; ++i)
{
assemble_diagonal_with_markers(*integrators[i], elem_markers[i],
*elem_attributes, y);
elem_attributes, y);
}
}
@@ -447,9 +489,9 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
for (int i = 0; i < n_bdr_integs; ++i)
{
assemble_diagonal_with_markers(*bdr_integs[i], bdr_markers[i],
*bdr_face_attributes, bdr_face_Y);
bdr_attributes, bdr_face_Y);
}
bdr_face_restrict_lex->AddAbsMultTranspose(bdr_face_Y, y);
bdr_face_restrict_lex->AddMultTransposeUnsigned(bdr_face_Y, y);
}
}
@@ -484,8 +526,7 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
A.Reset(oper); // A will own oper
}
void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
const bool useAbs) const
void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
@@ -517,13 +558,11 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
{
if (integrators[i]->Patchwise())
{
MFEM_ASSERT(!useAbs, "AbsMult not implemented with NURBS!")
integrators[i]->AddMultNURBSPA(x, y);
}
else
{
if (useAbs) { integrators[i]->AddAbsMultPA(x, y); }
else { integrators[i]->AddMultPA(x, y); }
integrators[i]->AddMultPA(x, y);
}
}
}
@@ -532,30 +571,14 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
if (iSz)
{
Array<Array<int>*> &elem_markers = *a->GetDBFI_Marker();
auto H1elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict);
if (H1elem_restrict && useAbs)
{
H1elem_restrict->AbsMult(x, localX);
}
else
{
elem_restrict->Mult(x, localX);
}
elem_restrict->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
AddMultWithMarkers(*integrators[i], localX, elem_markers[i],
*elem_attributes, false, localY, useAbs);
}
if (H1elem_restrict && useAbs)
{
H1elem_restrict->AbsMultTranspose(localY, y);
}
else
{
elem_restrict->MultTranspose(localY, y);
elem_attributes, false, localY);
}
elem_restrict->MultTranspose(localY, y);
}
else
{
@@ -567,7 +590,6 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
MFEM_ASSERT(!useAbs, "AbsMult not implemented for face integrators!")
// When assembling interior face integrators for DG spaces, we need to
// exchange the face-neighbor information. This happens inside member
// functions of the 'int_face_restrict_lex'. To avoid repeated calls to
@@ -629,7 +651,6 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
const bool has_bdr_integs = (n_bdr_face_integs > 0 || n_bdr_integs > 0);
if (bdr_face_restrict_lex && has_bdr_integs)
{
MFEM_ASSERT(!useAbs, "AbsMult not implemented for bdr integrators!")
Array<Array<int>*> &bdr_markers = *a->GetBBFI_Marker();
Array<Array<int>*> &bdr_face_markers = *a->GetBFBFI_Marker();
bdr_face_restrict_lex->Mult(x, bdr_face_X);
@@ -648,8 +669,8 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
}
for (int i = 0; i < n_bdr_integs; ++i)
{
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i],
*bdr_face_attributes, false, bdr_face_Y);
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
false, bdr_face_Y);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
@@ -657,14 +678,12 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
{
AddMultNormalDerivativesWithMarkers(
*bdr_face_integs[i], bdr_face_X, bdr_face_dXdn,
bdr_face_markers[i], *bdr_face_attributes, bdr_face_Y,
bdr_face_dYdn);
bdr_face_markers[i], bdr_attributes, bdr_face_Y, bdr_face_dYdn);
}
else
{
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X,
bdr_face_markers[i], *bdr_face_attributes, false,
bdr_face_Y);
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
bdr_attributes, false, bdr_face_Y);
}
}
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
@@ -687,7 +706,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], *elem_attributes,
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], elem_attributes,
true, localY);
}
elem_restrict->MultTranspose(localY, y);
@@ -734,14 +753,13 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
bdr_face_Y = 0.0;
for (int i = 0; i < n_bdr_integs; ++i)
{
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i],
*bdr_face_attributes, true, bdr_face_Y);
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
true, bdr_face_Y);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X,
bdr_face_markers[i], *bdr_face_attributes, true,
bdr_face_Y);
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
bdr_attributes, true, bdr_face_Y);
}
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
@@ -765,7 +783,7 @@ static void AddWithMarkers_(
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
const int attr = d_attr[e];
if (attr <= 0 || d_m[attr - 1] == 0) { return; }
if (d_m[attr - 1] == 0) { return; }
for (int i = 0; i < nd; ++i)
{
d_y(i, e) += d_x(i, e);
@@ -810,39 +828,22 @@ void PABilinearFormExtension::AddMultWithMarkers(
const Array<int> *markers,
const Array<int> &attributes,
const bool transpose,
Vector &y,
const bool useAbs) const
Vector &y) const
{
if (markers)
{
tmp_evec.SetSize(y.Size());
tmp_evec = 0.0;
if (useAbs)
{
if (transpose) { integ.AddAbsMultTransposePA(x, tmp_evec); }
else { integ.AddAbsMultPA(x, tmp_evec); }
}
else
{
if (transpose) { integ.AddMultTransposePA(x, tmp_evec); }
else { integ.AddMultPA(x, tmp_evec); }
}
if (transpose) { integ.AddMultTransposePA(x, tmp_evec); }
else { integ.AddMultPA(x, tmp_evec); }
const int ne = attributes.Size();
const int nd = x.Size() / ne;
AddWithMarkers_(ne, nd, tmp_evec, *markers, attributes, y);
}
else
{
if (useAbs)
{
if (transpose) { integ.AddAbsMultTransposePA(x, y); }
else { integ.AddAbsMultPA(x, y); }
}
else
{
if (transpose) { integ.AddMultTransposePA(x, y); }
else { integ.AddMultPA(x, y); }
}
if (transpose) { integ.AddMultTransposePA(x, y); }
else { integ.AddMultPA(x, y); }
}
}
@@ -881,8 +882,7 @@ void EABilinearFormExtension::Assemble()
{
const int i = idx % sz;
const int e = idx / sz;
const real_t val =
d_a[e] > 0 ? (d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0) : 0;
const real_t val = d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0.0;
if (add)
{
d_ea_2(i, e) += val;
@@ -915,7 +915,7 @@ void EABilinearFormExtension::Assemble()
ea_data_tmp.SetSize(ea_data.Size());
integrators[i]->AssembleEA(*a->FESpace(), ea_data_tmp, false);
add_with_markers(ea_data_tmp, ea_data, ne, *markers,
*elem_attributes, add);
elem_attributes, add);
}
}
}
@@ -944,7 +944,7 @@ void EABilinearFormExtension::Assemble()
ea_data_tmp.SetSize(ea_data_bdr.Size());
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_tmp, add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
*bdr_face_attributes, add);
bdr_attributes, add);
}
}
}
@@ -993,7 +993,7 @@ void EABilinearFormExtension::Assemble()
ea_data_tmp,
add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
*bdr_face_attributes, add);
bdr_attributes, add);
}
}
}
@@ -1010,13 +1010,8 @@ void EABilinearFormExtension::Assemble()
}
}
void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
const bool useTranspose,
const bool useAbs) const
void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
auto elemRest = dynamic_cast<const ElementRestriction*>(elem_restrict);
MFEM_ASSERT(useAbs?(elemRest!=nullptr):true,
"elem_restrict is not ElementRestriction*!")
// Apply the Element Restriction
const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
if (!useRestrict)
@@ -1024,11 +1019,6 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
}
else if (useAbs)
{
elemRest->AbsMult(x, localX);
localY = 0.0;
}
else
{
elem_restrict->Mult(x, localX);
@@ -1036,55 +1026,25 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
}
// Apply the Element Matrices
{
Vector abs_ea_data;
if (useAbs)
{
abs_ea_data = ea_data;
abs_ea_data.Abs();
}
const int NDOFS = elemDofs;
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
auto A = Reshape(useAbs?abs_ea_data.Read():ea_data.Read(), NDOFS, NDOFS, ne);
if (!useTranspose)
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(i, j, e)*X(i, e);
}
Y(j, e) += res;
});
}
else
{
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, e)*X(i, e);
}
Y(j, e) += res;
});
}
res += A(i, j, e)*X(i, e);
}
Y(j, e) += res;
});
// Apply the Element Restriction transposed
if (useRestrict)
{
if (useAbs)
{
elemRest->AbsMultTranspose(localY, y);
}
else
{
elem_restrict->MultTranspose(localY, y);
}
elem_restrict->MultTranspose(localY, y);
}
}
@@ -1093,7 +1053,6 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
MFEM_VERIFY(!useAbs, "AbsMult not implemented with Face integrators!")
// Apply the Interior Face Restriction
int_face_restrict_lex->Mult(x, int_face_X);
if (int_face_X.Size()>0)
@@ -1105,65 +1064,7 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
auto Y = Reshape(int_face_Y.ReadWrite(), NDOFS, 2, nf_int);
if (!factorize_face_terms)
{
Vector abs_ea_data_int(ea_data_int.Size());
if (useAbs)
{
abs_ea_data_int = ea_data_int;
abs_ea_data_int.Abs();
}
auto A_int = Reshape(useAbs?abs_ea_data_int.Read():ea_data_int.Read(),
NDOFS, NDOFS, 2, nf_int);
if (!useTranspose)
{
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(i, j, 0, f)*X(i, 0, f);
}
Y(j, 0, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(i, j, 1, f)*X(i, 1, f);
}
Y(j, 1, f) += res;
});
}
else
{
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(j, i, 0, f)*X(i, 0, f);
}
Y(j, 0, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(j, i, 1, f)*X(i, 1, f);
}
Y(j, 1, f) += res;
});
}
}
Vector abs_ea_data_ext(ea_data_ext.Size());
if (useAbs)
{
abs_ea_data_ext = ea_data_ext;
abs_ea_data_ext.Abs();
}
auto A_ext = Reshape(useAbs?abs_ea_data_ext.Read():ea_data_ext.Read(),
NDOFS, NDOFS, 2, nf_int);
if (!useTranspose)
{
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
@@ -1171,37 +1072,35 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(i, j, 0, f)*X(i, 0, f);
res += A_int(i, j, 0, f)*X(i, 0, f);
}
Y(j, 1, f) += res;
Y(j, 0, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(i, j, 1, f)*X(i, 1, f);
res += A_int(i, j, 1, f)*X(i, 1, f);
}
Y(j, 0, f) += res;
Y(j, 1, f) += res;
});
}
else
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(j, i, 1, f)*X(i, 0, f);
}
Y(j, 1, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(j, i, 0, f)*X(i, 1, f);
}
Y(j, 0, f) += res;
});
}
res += A_ext(i, j, 0, f)*X(i, 0, f);
}
Y(j, 1, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(i, j, 1, f)*X(i, 1, f);
}
Y(j, 0, f) += res;
});
// Apply the Interior Face Restriction transposed
int_face_restrict_lex->AddMultTransposeInPlace(int_face_Y, y);
}
@@ -1210,9 +1109,7 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
// Treatment of boundary faces
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
{
MFEM_ASSERT(!useAbs, "AbsMult not implemented with Face integrators!")
// Apply the Boundary Face Restriction
// TODO: AbsMult if needed
bdr_face_restrict_lex->Mult(x, bdr_face_X);
bdr_face_Y = 0.0;
// Apply the boundary face matrices
@@ -1220,38 +1117,141 @@ void EABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
if (!useTranspose)
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
// TODO: useAbs
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
}
else
{
// TODO: useAbs
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, f)*X(i, f);
}
Y(j, f) += res;
});
}
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
// Apply the Element Restriction
const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
if (!useRestrict)
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
}
else
{
elem_restrict->Mult(x, localX);
localY = 0.0;
}
// Apply the Element Matrices transposed
{
const int NDOFS = elemDofs;
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, e)*X(i, e);
}
Y(j, e) += res;
});
// Apply the Element Restriction transposed
if (useRestrict)
{
elem_restrict->MultTranspose(localY, y);
}
}
// Treatment of interior faces
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
// Apply the Interior Face Restriction
int_face_restrict_lex->Mult(x, int_face_X);
if (int_face_X.Size()>0)
{
int_face_Y = 0.0;
// Apply the interior face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(int_face_X.Read(), NDOFS, 2, nf_int);
auto Y = Reshape(int_face_Y.ReadWrite(), NDOFS, 2, nf_int);
if (!factorize_face_terms)
{
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(j, i, 0, f)*X(i, 0, f);
}
Y(j, 0, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_int(j, i, 1, f)*X(i, 1, f);
}
Y(j, 1, f) += res;
});
}
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(j, i, 1, f)*X(i, 0, f);
}
Y(j, 1, f) += res;
res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A_ext(j, i, 0, f)*X(i, 1, f);
}
Y(j, 0, f) += res;
});
// Apply the Interior Face Restriction transposed
int_face_restrict_lex->AddMultTransposeInPlace(int_face_Y, y);
}
}
// Treatment of boundary faces
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, bdr_face_X);
bdr_face_Y = 0.0;
// Apply the boundary face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
// TODO: AbsMultTranspose if needed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
@@ -1911,7 +1911,7 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
dynamic_cast<const ElementRestriction*>(elem_restrict_trial);
if (H1elem_restrict_trial)
{
H1elem_restrict_trial->AbsMult(D, localTrial);
H1elem_restrict_trial->MultUnsigned(D, localTrial);
}
else
{
@@ -1937,7 +1937,7 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (H1elem_restrict_test)
{
H1elem_restrict_test->AbsMultTranspose(localTest, diag);
H1elem_restrict_test->MultTransposeUnsigned(localTest, diag);
}
else
{
@@ -1993,7 +1993,7 @@ void PADiscreteLinearOperatorExtension::Assemble()
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (elem_restrict)
{
elem_restrict->AbsMultTranspose(ones, test_multiplicity);
elem_restrict->MultTransposeUnsigned(ones, test_multiplicity);
}
else
{
+5 -24
View File
@@ -69,8 +69,7 @@ class PABilinearFormExtension : public BilinearFormExtension
protected:
const FiniteElementSpace *trial_fes, *test_fes; // Not owned
/// Attributes of all mesh elements.
const Array<int> *elem_attributes; // Not owned
const Array<int> *bdr_face_attributes; // Not owned
Array<int> elem_attributes, bdr_attributes;
mutable Vector tmp_evec; // Work array
mutable Vector localX, localY;
mutable Vector int_face_X, int_face_Y;
@@ -92,17 +91,12 @@ public:
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) override;
void Mult(const Vector &x, Vector &y) const override
{ MultInternal(x,y); }
void AbsMult(const Vector &x, Vector &y) const override
{ MultInternal(x,y, true); }
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
void Update() override;
protected:
void SetupRestrictionOperators(const L2FaceValues m);
void MultInternal(const Vector &x, Vector &y,
const bool useAbs = false) const;
/// @brief Accumulate the action (or transpose) of the integrator on @a x
/// into @a y, taking into account the (possibly null) @a markers array.
@@ -116,14 +110,12 @@ protected:
/// @param attributes Array of element or boundary element attributes.
/// @param transpose Compute the action or transpose of the integrator .
/// @param y Output E-vector
/// @param useAbs Apply absolute-value operator
void AddMultWithMarkers(const BilinearFormIntegrator &integ,
const Vector &x,
const Array<int> *markers,
const Array<int> &attributes,
const bool transpose,
Vector &y,
const bool useAbs = false) const;
Vector &y) const;
/// @brief Performs the same function as AddMultWithMarkers, but takes as
/// input and output face normal derivatives.
@@ -160,15 +152,8 @@ public:
EABilinearFormExtension(BilinearForm *form);
void Assemble() override;
void Mult(const Vector &x, Vector &y) const override
{ MultInternal(x, y, false); }
void AbsMult(const Vector &x, Vector &y) const override
{ MultInternal(x, y, false, true); }
void MultTranspose(const Vector &x, Vector &y) const override
{ MultInternal(x, y, true); }
void AbsMultTranspose(const Vector &x, Vector &y) const override
{ MultInternal(x, y, true, true); }
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
/// @brief Populates @a element_matrices with the element matrices.
///
@@ -180,10 +165,6 @@ public:
void GetElementMatrices(DenseTensor &element_matrices,
ElementDofOrdering ordering,
bool add_bdr);
// This method needs to be public due to 'nvcc' restriction.
void MultInternal(const Vector &x, Vector &y, const bool useTranspose,
const bool useAbs = false) const;
};
/// Data and methods for fully-assembled bilinear forms
-29
View File
@@ -121,12 +121,6 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddAbsMultPA(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator:AddAbsMultPA:(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultNURBSPA(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::AddMultNURBSPA(...)\n"
@@ -139,13 +133,6 @@ void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddAbsMultTransposePA(const Vector &,
Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::AddAbsMultTransposePA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleMF(...)\n"
@@ -431,14 +418,6 @@ void SumIntegrator::AddMultPA(const Vector& x, Vector& y) const
}
}
void SumIntegrator::AddAbsMultPA(const Vector& x, Vector& y) const
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AddAbsMultPA(x, y);
}
}
void SumIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
for (int i = 0; i < integrators.Size(); i++)
@@ -447,14 +426,6 @@ void SumIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
}
}
void SumIntegrator::AddAbsMultTransposePA(const Vector &x, Vector &y) const
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AddAbsMultTransposePA(x, y);
}
}
void SumIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
for (int i = 0; i < integrators.Size(); i++)
+50 -170
View File
@@ -23,8 +23,6 @@
namespace mfem
{
class QuadratureSpace;
class FaceQuadratureSpace;
/// Abstract base class BilinearFormIntegrator
class BilinearFormIntegrator : public NonlinearFormIntegrator
@@ -80,8 +78,6 @@ public:
called. */
void AddMultPA(const Vector &x, Vector &y) const override;
virtual void AddAbsMultPA(const Vector &x, Vector &y) const;
/// Method for partially assembled action on NURBS patches.
virtual void AddMultNURBSPA(const Vector&x, Vector&y) const;
@@ -94,8 +90,6 @@ public:
called. */
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
virtual void AddAbsMultTransposePA(const Vector &x, Vector &y) const;
/// Method defining element assembly.
/** The result of the element assembly is added to the @a emat Vector if
@a add is true. Otherwise, if @a add is false, we set @a emat. */
@@ -502,12 +496,8 @@ public:
void AddMultTransposePA(const Vector &x, Vector &y) const override;
void AddAbsMultTransposePA(const Vector &x, Vector &y) const override;
void AddMultPA(const Vector& x, Vector& y) const override;
void AddAbsMultPA(const Vector& x, Vector& y) const override;
void AssembleMF(const FiniteElementSpace &fes) override;
void AddMultMF(const Vector &x, Vector &y) const override;
@@ -814,7 +804,7 @@ protected:
const FiniteElement & test_fe) const
{
return (trial_fe.GetDim() == 1 && test_fe.GetDim() == 1 &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -886,7 +876,7 @@ protected:
const FiniteElement & trial_fe,
const FiniteElement & test_fe) const
{
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -921,7 +911,7 @@ protected:
const FiniteElement & trial_fe,
const FiniteElement & test_fe) const
{
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -1602,7 +1592,7 @@ public:
{
return (trial_fe.GetCurlDim() == 3 && test_fe.GetRangeDim() == 3 &&
trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -1637,7 +1627,7 @@ public:
{
return (trial_fe.GetDim() == 2 && test_fe.GetDim() == 2 &&
trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -1671,7 +1661,7 @@ public:
{
return (trial_fe.GetDim() == 2 && test_fe.GetDim() == 2 &&
trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -1762,7 +1752,7 @@ public:
const FiniteElement & test_fe) const
{
return (trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -1795,7 +1785,7 @@ public:
const FiniteElement & test_fe) const
{
return (trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
test_fe.GetDerivType() == mfem::FiniteElement::DIV );
}
@@ -1834,7 +1824,7 @@ public:
const FiniteElement & test_fe) const
{
return (trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
test_fe.GetDerivType() == mfem::FiniteElement::GRAD
);
@@ -1975,7 +1965,7 @@ protected:
const FiniteElement & test_fe) const override
{
return (trial_fe.GetCurlDim() == 3 && test_fe.GetRangeDim() == 3 &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -2330,12 +2320,8 @@ public:
void AddMultPA(const Vector&, Vector&) const override;
void AddAbsMultPA(const Vector&, Vector&) const override;
void AddMultTransposePA(const Vector&, Vector&) const override;
void AddAbsMultTransposePA(const Vector&, Vector&) const override;
void AddMultNURBSPA(const Vector&, Vector&) const override;
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
@@ -2433,12 +2419,8 @@ public:
void AddMultPA(const Vector&, Vector&) const override;
void AddAbsMultPA(const Vector&, Vector&) const override;
void AddMultTransposePA(const Vector&, Vector&) const override;
void AddAbsMultTransposePA(const Vector&, Vector&) const override;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
const ElementTransformation &Trans);
@@ -2496,7 +2478,8 @@ private:
#endif
public:
ConvectionIntegrator(VectorCoefficient &q, real_t a = 1.0);
ConvectionIntegrator(VectorCoefficient &q, real_t a = 1.0)
: Q(&q) { alpha = a; }
void AssembleElementMatrix(const FiniteElement &,
ElementTransformation &,
@@ -2529,28 +2512,6 @@ public:
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
/// arguments: NE, B, G, Bt, Gt, pa_data, x, y, D1D, Q1D
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);
/// arguments: DIMS, D1D, Q1D
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
/// arguments: DIMS, D1D, Q1D
MFEM_REGISTER_KERNELS(ApplyPATKernels, ApplyKernelType, (int, int, int));
template <int DIM, int D1D, int Q1D>
static void AddSpecialization()
{
ApplyPAKernels::Specialization<DIM, D1D, Q1D>::Add();
ApplyPATKernels::Specialization<DIM, D1D, Q1D>::Add();
}
struct Kernels { Kernels(); };
protected:
const IntegrationRule* GetDefaultIntegrationRule(
const FiniteElement& trial_fe,
@@ -2821,13 +2782,15 @@ protected:
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
public:
CurlCurlIntegrator();
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; }
/// Construct a bilinear form integrator for Nedelec elements
CurlCurlIntegrator(Coefficient &q, const IntegrationRule *ir = nullptr);
CurlCurlIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(&q), DQ(NULL), MQ(NULL) { }
CurlCurlIntegrator(DiagonalMatrixCoefficient &dq,
const IntegrationRule *ir = nullptr);
CurlCurlIntegrator(MatrixCoefficient &mq,
const IntegrationRule *ir = nullptr);
const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(&dq), MQ(NULL) { }
CurlCurlIntegrator(MatrixCoefficient &mq, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(&mq) { }
/* Given a particular Finite Element, compute the
element curl-curl matrix elmat */
@@ -2853,38 +2816,9 @@ public:
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AddAbsMultPA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
const Coefficient *GetCoefficient() const { return Q; }
/// arguments: d1d, q1d, symmetric, NE, bo, bc, bot, bct, gc, gct, pa_data,
/// x, y, useAbs
using ApplyKernelType = void (*)(
const int, const int, const bool, const 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 bool);
/// arguments: d1d, q1d, symmetric, ne, Bo, Bc, Go, Gc, pa_data, diag
using DiagonalKernelType = void (*)(const int, const int, const bool,
const int, const Array<real_t> &,
const Array<real_t> &,
const Array<real_t> &,
const Array<real_t> &, const Vector &,
Vector &);
/// parameters: dim, d1d, q1d
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
/// parameters: dim, d1d, q1d
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
struct Kernels { Kernels(); };
template <int DIM, int D1D, int Q1D> static void AddSpecialization()
{
ApplyPAKernels::Specialization<DIM, D1D, Q1D>::Add();
DiagonalPAKernels::Specialization<DIM, D1D, Q1D>::Add();
}
};
/** Integrator for $(\mathrm{curl}(u), \mathrm{curl}(v))$ for FE spaces defined by 'dim' copies of a
@@ -2999,7 +2933,6 @@ public:
void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AddAbsMultPA(const Vector &x, Vector &y) const override;
void AddMultTransposePA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
@@ -3138,18 +3071,21 @@ private:
Vector vcoeff;
public:
VectorDiffusionIntegrator(const IntegrationRule *ir = nullptr);
VectorDiffusionIntegrator() { }
/** \brief Integrator with unit coefficient for caller-specified vector
dimension.
If the vector dimension does not match the true dimension of the space,
the resulting element matrix will be mathematically invalid. */
VectorDiffusionIntegrator(int vector_dimension);
VectorDiffusionIntegrator(int vector_dimension)
: vdim(vector_dimension) { }
VectorDiffusionIntegrator(Coefficient &q);
VectorDiffusionIntegrator(Coefficient &q)
: Q(&q) { }
VectorDiffusionIntegrator(Coefficient &q, const IntegrationRule *ir);
VectorDiffusionIntegrator(Coefficient &q, const IntegrationRule *ir)
: BilinearFormIntegrator(ir), Q(&q) { }
/** \brief Integrator with scalar coefficient for caller-specified vector
dimension.
@@ -3159,7 +3095,8 @@ public:
If the vector dimension does not match the true dimension of the space,
the resulting element matrix will be mathematically invalid. */
VectorDiffusionIntegrator(Coefficient &q, int vector_dimension);
VectorDiffusionIntegrator(Coefficient &q, int vector_dimension)
: Q(&q), vdim(vector_dimension) { }
/** \brief Integrator with \c VectorCoefficient. The vector dimension of the
\c FiniteElementSpace is assumed to be the same as the dimension of the
@@ -3170,7 +3107,8 @@ public:
If the vector dimension does not match the true dimension of the space,
the resulting element matrix will be mathematically invalid. */
VectorDiffusionIntegrator(VectorCoefficient &vq);
VectorDiffusionIntegrator(VectorCoefficient &vq)
: VQ(&vq), vdim(vq.GetVDim()) { }
/** \brief Integrator with \c MatrixCoefficient. The vector dimension of the
\c FiniteElementSpace is assumed to be the same as the dimension of the
@@ -3181,7 +3119,8 @@ public:
If the vector dimension does not match the true dimension of the space,
the resulting element matrix will be mathematically invalid. */
VectorDiffusionIntegrator(MatrixCoefficient& mq);
VectorDiffusionIntegrator(MatrixCoefficient& mq)
: MQ(&mq), vdim(mq.GetVDim()) { }
void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -3197,28 +3136,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(); }
/// 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));
template <int DIM, int VDIM, int D1D, int Q1D>
static void AddSpecialization()
{
ApplyPAKernels::Specialization<DIM, VDIM, D1D, Q1D>::Add();
}
struct Kernels
{
Kernels();
};
};
/** Integrator for the linear elasticity form:
@@ -3372,8 +3289,8 @@ public:
class DGTraceIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *rho = nullptr;
VectorCoefficient *u = nullptr;
Coefficient *rho;
VectorCoefficient *u;
real_t alpha, beta;
// PA extension
Vector pa_data;
@@ -3386,16 +3303,17 @@ private:
Vector tr_shape1, te_shape1, tr_shape2, te_shape2;
public:
DGTraceIntegrator(real_t a, real_t b);
/// Construct integrator with $\rho = 1$, $\beta = \alpha/2$.
DGTraceIntegrator(VectorCoefficient &u_, real_t a);
DGTraceIntegrator(VectorCoefficient &u_, real_t a)
{ rho = NULL; u = &u_; alpha = a; beta = 0.5*a; }
/// Construct integrator with $\rho = 1$.
DGTraceIntegrator(VectorCoefficient &u_, real_t a, real_t b);
DGTraceIntegrator(VectorCoefficient &u_, real_t a, real_t b)
{ rho = NULL; u = &u_; alpha = a; beta = b; }
DGTraceIntegrator(Coefficient &rho_, VectorCoefficient &u_,
real_t a, real_t b);
real_t a, real_t b)
{ rho = &rho_; u = &u_; alpha = a; beta = b; }
using BilinearFormIntegrator::AssembleFaceMatrix;
void AssembleFaceMatrix(const FiniteElement &el1,
@@ -3434,26 +3352,6 @@ public:
static const IntegrationRule &GetRule(Geometry::Type geom, int order,
const ElementTransformation &T);
/// arguments: nf, B, Bt, pa_data, x, y, dofs1D, quad1D
using ApplyKernelType = void (*)(const int, const Array<real_t> &,
const Array<real_t> &, const Vector &,
const Vector &, Vector &, const int,
const int);
/// arguments: DIM, d1d, q1d
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
/// arguments: DIM, d1d, q1d
MFEM_REGISTER_KERNELS(ApplyPATKernels, ApplyKernelType, (int, int, int));
template <int DIM, int D1D, int Q1D> static void AddSpecialization()
{
ApplyPAKernels::Specialization<DIM, D1D, Q1D>::Add();
ApplyPATKernels::Specialization<DIM, D1D, Q1D>::Add();
}
struct Kernels { Kernels(); };
private:
void SetupPA(const FiniteElementSpace &fes, FaceType type);
};
@@ -3500,8 +3398,8 @@ public:
class DGDiffusionIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *Q = nullptr;
MatrixCoefficient *MQ = nullptr;
Coefficient *Q;
MatrixCoefficient *MQ;
real_t sigma, kappa;
// these are not thread-safe!
@@ -3516,11 +3414,15 @@ protected:
IntegrationRules irs{0, Quadrature1D::GaussLobatto};
public:
DGDiffusionIntegrator(const real_t s, const real_t k);
DGDiffusionIntegrator(Coefficient &q, const real_t s, const real_t k);
DGDiffusionIntegrator(MatrixCoefficient &q, const real_t s, const real_t k);
DGDiffusionIntegrator(const real_t s, const real_t k)
: Q(NULL), MQ(NULL), sigma(s), kappa(k) { }
DGDiffusionIntegrator(Coefficient &q, const real_t s, const real_t k)
: Q(&q), MQ(NULL), sigma(s), kappa(k) { }
DGDiffusionIntegrator(MatrixCoefficient &q, const real_t s, const real_t k)
: Q(NULL), MQ(&q), sigma(s), kappa(k) { }
using BilinearFormIntegrator::AssembleFaceMatrix;
void AssembleFaceMatrix(const FiniteElement &el1, const FiniteElement &el2,
void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Trans,
DenseMatrix &elmat) override;
@@ -3539,28 +3441,6 @@ public:
const IntegrationRule &GetRule(int order, Geometry::Type geom);
real_t GetPenaltyParameter() const { return kappa; }
/// arguments: nf, B, Bt, G, Gt, sigma, pa_data, x, dxdn, y, dydn, dofs1D,
/// quad1D
using ApplyKernelType = void (*)(const int, const Array<real_t> &,
const Array<real_t> &,
const Array<real_t> &,
const Array<real_t> &, const real_t,
const Vector &, const Vector &_,
const Vector &, Vector &, Vector &,
const int, const int);
/// arguments: DIM, d1d, q1d
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
template <int DIM, int D1D, int Q1D> static void AddSpecialization()
{
ApplyPAKernels::Specialization<DIM, D1D, Q1D>::Add();
}
struct Kernels { Kernels(); };
private:
void SetupPA(const FiniteElementSpace &fes, FaceType type);
};
-715
View File
@@ -1,715 +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.
// Implementation of bounds
#include "bounds.hpp"
#include <limits>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
namespace mfem
{
using namespace std;
void PLBound::Setup(const int nb_i, const int ncp_i,
const int b_type_i, const int cp_type_i,
const real_t tol_i)
{
MFEM_VERIFY(b_type_i >= 0 && b_type_i <= 2, "Bases not supported. "
"Please read class description to see supported types.");
MFEM_VERIFY(cp_type_i == 0 || cp_type_i == 1,
"Control point type not supported. Please read class "
"description to see supported types.");
nb = nb_i;
ncp = ncp_i;
b_type = b_type_i;
cp_type = cp_type_i;
tol = tol_i;
lbound.SetSize(nb, ncp);
ubound.SetSize(nb, ncp);
nodes.SetSize(nb);
weights.SetSize(nb);
control_points.SetSize(ncp);
auto scalenodes = [](const Vector &in, const real_t a, const real_t b) -> Vector
{
Vector outVec(in.Size());
real_t maxv = in.Max();
real_t minv = in.Min();
for (int i = 0; i < in.Size(); i++)
{
outVec(i) = a + (b-a)*(in(i)-minv)/(maxv-minv);
}
return outVec;
};
MFEM_VERIFY(ncp >= 2,"At least 2 control points are required.");
if (cp_type == 0) // GL + End Point
{
control_points(0) = 0.0;
control_points(ncp-1) = 1.0;
if (ncp > 2)
{
const real_t *x = poly1d.GetPoints(ncp-3, 0);
MFEM_VERIFY(x, "Error in getting points.");
for (int i = 0; i < ncp-2; i++)
{
control_points(i+1) = x[i];
}
}
}
else if (cp_type == 1) // Chebyshev
{
auto GetChebyshevNodes = [](int n) -> Vector
{
Vector cheb(n);
for (int i = 0; i < n; ++i)
{
cheb(i) = -cos(M_PI * (static_cast<real_t>(i) / (n - 1)));
}
return cheb;
};
control_points = GetChebyshevNodes(ncp);
}
else
{
MFEM_ABORT("Unsupported interval points. Use [0,1].\n");
}
control_points = scalenodes(control_points, 0.0, 1.0); // rescale to [0,1]
Poly_1D::Basis &basis1d(poly1d.GetBasis(nb-1, b_type));
// Initialize bounds
lbound = 0.0;
ubound = 0.0;
Vector bmv(nb), bpv(nb), bv(nb); // basis values
Vector bdmv(nb), bdpv(nb), bdv(nb); // basis derivative values
Vector vals(3);
// See Section 3.1.1 of https://arxiv.org/pdf/2501.12349 for explanation of
// procedure below.
for (int j = 0; j < ncp; j++)
{
real_t x = control_points(j);
real_t xm = x;
if (j != 0)
{
xm = 0.5*(control_points(j-1)+control_points(j));
}
real_t xp = x;
if (j != ncp-1)
{
xp = 0.5*(control_points(j)+control_points(j+1));
}
basis1d.Eval(xm, bmv, bdmv);
basis1d.Eval(xp, bpv, bdpv);
basis1d.Eval(x, bv);
real_t dm = x-xm;
real_t dp = x-xp;
for (int i = 0; i < nb; i++)
{
if (j == 0)
{
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
}
else if (j == ncp-1)
{
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(i, j) = vals.Min()-tol; // tolerance for good measure
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
}
}
}
IntegrationRule irule(nb);
if (b_type == 0)
{
QuadratureFunctions1D::GaussLegendre(nb, &irule);
for (int i = 0; i < nb; i++)
{
weights(i) = irule.IntPoint(i).weight;
nodes(i) = irule.IntPoint(i).x;
}
}
else if (b_type == 1)
{
QuadratureFunctions1D::GaussLobatto(nb, &irule);
for (int i = 0; i < nb; i++)
{
weights(i) = irule.IntPoint(i).weight;
nodes(i) = irule.IntPoint(i).x;
}
}
else if (b_type == 2)
{
QuadratureFunctions1D::ClosedUniform(nb, &irule);
for (int i = 0; i < nb; i++)
{
weights(i) = irule.IntPoint(i).weight;
nodes(i) = irule.IntPoint(i).x;
}
}
if (b_type == 2)
{
nodes_int.SetSize(nb);
weights_int.SetSize(nb);
IntegrationRule irule_int(nb);
{
QuadratureFunctions1D::GaussLobatto(nb, &irule_int);
for (int i = 0; i < nb; i++)
{
weights_int(i) = irule_int.IntPoint(i).weight;
nodes_int(i) = irule_int.IntPoint(i).x;
}
}
SetupBernsteinBasisMat(basisMatNodes, nodes);
// Setup memory for lu factors
basisMatLU = basisMatNodes;
lu_ip.SetSize(nb);
// Compute lu factors
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
bool factor = lu.Factor(nb);
MFEM_VERIFY(factor,"Failure in LU factorization in PLBound.");
// Setup the Bernstein basis matrix for the GLL integration points. This
// is used to compute linear fit.
SetupBernsteinBasisMat(basisMatInt, nodes_int);
}
else
{
nodes_int.SetDataAndSize(nodes.GetData(), nb);
weights_int.SetDataAndSize(weights.GetData(), nb);
}
}
PLBound::PLBound(FiniteElementSpace *fes, int ncp_i, int cp_type_i)
{
MFEM_VERIFY(!fes->IsVariableOrder(),
"Variable order meshes not yet supported.");
const char *name = fes->FEColl()->Name();
string cname = name;
cp_type = cp_type_i;
b_type = BasisType::Invalid;
nb = fes->GetMaxElementOrder()+1;
tol = 0.0;
int minncp = 2;
if (nb > 12)
{
minncp = 2*nb;
}
else if (!strncmp(name, "H1_", 3) && strncmp(name, "H1_Trace_", 9))
{
// H1 GLL
b_type = BasisType::GaussLobatto;
minncp = min_ncp_gll_x[cp_type][nb-2];
}
else if (!strncmp(name, "H1Pos_", 6) && strncmp(name, "H1Pos_Trace_", 12))
{
// H1 Positive
b_type = BasisType::Positive;
minncp = min_ncp_pos_x[cp_type][nb-2];
}
else if (!strncmp(name, "L2_", 3) && strncmp(name, "L2_T", 4))
{
// L2 Gauss-Legendre
b_type = BasisType::GaussLegendre;
minncp = min_ncp_gl_x[cp_type][nb-2];
}
else if (!strncmp(name, "L2_T1", 5))
{
// L2 GLL
b_type = BasisType::GaussLobatto;
minncp = min_ncp_gll_x[cp_type][nb-2];
}
else if (!strncmp(name, "L2_T2", 5))
{
// L2 Positive
b_type = BasisType::Positive;
minncp = min_ncp_pos_x[cp_type][nb-2];
}
else
{
MFEM_ABORT("Only H1 GLL/Positive & L2 GL/GLL/Positive bases supported.");
}
ncp = std::max(minncp, ncp_i);
Setup(nb, ncp, b_type, cp_type, tol);
}
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(nb);
coeffm = 0.0;
real_t a0 = 0.0;
real_t a1 = 0.0;
Vector nodal_vals, nodal_integ_vals;
if (b_type == 2) // compute values at equispaced nodes and GLL nodes
{
nodal_vals.SetSize(nb);
nodal_integ_vals.SetSize(nb);
Vector shape(nb);
for (int i = 0; i < nb; i++)
{
basisMatNodes.GetRow(i, shape);
nodal_vals(i) = shape*coeff;
basisMatInt.GetRow(i, shape);
nodal_integ_vals(i) = shape*coeff;
}
}
else
{
nodal_vals.SetDataAndSize(coeff.GetData(), nb);
nodal_integ_vals.SetDataAndSize(coeff.GetData(), nb);
}
// compute L2 projection for linear bases: a0 + a1*x
if (proj)
{
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes_int(i)-1;
w = 2.0*weights_int(i);
a0 += 0.5*nodal_integ_vals(i)*w;
a1 += 1.5*nodal_integ_vals(i)*w*x;
}
// offset the linear fit from nodal values
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes(i)-1;
coeffm(i) = nodal_vals(i) - a0 - a1*x;
}
// compute coefficients for Bernstein
if (b_type == 2)
{
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
lu.Solve(nb, 1, coeffm.GetData());
}
// initialize the bounds to be the linear fit
for (int j = 0; j < ncp; j++)
{
x = 2.0*control_points(j)-1;
intmin(j) = a0 + a1*x;
intmax(j) = intmin(j);
}
}
else
{
coeffm.SetDataAndSize(coeff.GetData(), nb);
}
for (int i = 0; i < nb; i++)
{
real_t c = coeffm(i);
for (int j = 0; j < ncp; j++)
{
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(Vector &coeff, Vector &intmin, Vector &intmax) const
{
intmin.SetSize(ncp*ncp);
intmax.SetSize(ncp*ncp);
intmin = 0.0;
intmax = 0.0;
Vector intminT(ncp*nb);
Vector intmaxT(ncp*nb);
// Get bounds for each row of the solution
for (int i = 0; i < nb; i++)
{
Vector solcoeff(coeff.GetData()+i*nb, nb);
Vector intminrow(intminT.GetData()+i*ncp, ncp);
Vector intmaxrow(intmaxT.GetData()+i*ncp, ncp);
Get1DBounds(solcoeff, intminrow, intmaxrow);
}
Vector intminT2 = intminT;
// Compute a0 and a1 for each column of nodes
Vector a0V(ncp), a1V(ncp);
a0V = 0.0;
a1V = 0.0;
real_t x,w,t;
if (proj)
{
if (b_type == 2)
{
// Note: DenseMatrix uses column-major ordering so we will need to
// transpose the matrix.
DenseMatrix intminTM(intminT.GetData(), ncp, nb),
intmaxTM(intmaxT.GetData(), ncp, nb),
intmeanTM(ncp, nb);
DenseMatrix minvalsM(nb, ncp), maxvalsM(nb, ncp), meanintvalsM(nb, ncp);
MultABt(basisMatNodes, intminTM, minvalsM);
MultABt(basisMatNodes, intmaxTM, maxvalsM);
intmeanTM = intminTM;
intmeanTM += intmaxTM;
intmeanTM *= 0.5;
MultABt(basisMatInt, intmeanTM, meanintvalsM);
// Compute the linear fit along each column and then offset it from
// the bounds on the coefficient.
// Note: Since Bernstein bases are positive, we can use the lower
// bounds to compute the lower bounding polynomial and subtract the
// linear fit before finding the Bernstein coefficients corresponding
// to the perturbation. Same for upper bounds. If the bases were not
// always positive, it is not yet clear if the perturbation
// coefficients will be this straightforward to compute.
for (int j = 0; j < ncp; j++) // row of interval points
{
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes_int(i)-1; // x-coordinate
w = 2.0*weights_int(i); // weight
t = meanintvalsM(i,j);
a0V(j) += 0.5*t*w;
a1V(j) += 1.5*t*w*x;
}
// Offset linear fit
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes(i)-1; // x-coordinate
minvalsM(i,j) -= a0V(j) + a1V(j)*x;
maxvalsM(i,j) -= a0V(j) + a1V(j)*x;
}
// Compute Bernstein coefficients
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
lu.Solve(nb, 1, minvalsM.GetColumn(j));
lu.Solve(nb, 1, maxvalsM.GetColumn(j));
for (int i = 0; i < nb; i++)
{
intminT(i*ncp+j) = minvalsM(i,j);
intmaxT(i*ncp+j) = maxvalsM(i,j);
}
}
}
else
{
for (int j = 0; j < nb; j++) // row of nodes
{
x = 2.0*nodes(j)-1; // x-coordinate
w = 2.0*weights(j); // weight
for (int i = 0; i < ncp; i++) // column of interval points
{
t = 0.5*(intminT(j*ncp+i)+intmaxT(j*ncp+i));
a0V(i) += 0.5*t*w;
a1V(i) += 1.5*t*w*x;
}
}
// offset the linear fit from nodal values
for (int j = 0; j < nb; j++) // row of nodes
{
x = 2.0*nodes(j)-1; // x-coordinate
for (int i = 0; i < ncp; i++) // column of interval points
{
t = a0V(i) + a1V(i)*x;
intminT(j*ncp+i) -= t;
intmaxT(j*ncp+i) -= t;
}
}
}
// Initialize bounds using a0 and a1 values
for (int j = 0; j < ncp; j++) // row j
{
x = 2.0*control_points(j)-1;
for (int i = 0; i < ncp; i++) // column i
{
intmin(j*ncp+i) = a0V(i) + a1V(i)*x;
intmax(j*ncp+i) = intmin(j*ncp+i);
}
}
}
// Compute bounds
int id1 = 0, id2 = 0;
Vector vals(4);
for (int j = 0; j < nb; j++)
{
for (int i = 0; i < ncp; i++) // ith column
{
real_t w0 = intminT(id1++);
real_t w1 = intmaxT(id2++);
for (int k = 0; k < ncp; k++) // kth row
{
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();
}
}
}
}
void PLBound::Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
{
int nb2 = nb*nb,
ncp2 = ncp*ncp,
ncp3 = ncp*ncp*ncp;
intmin.SetSize(ncp3);
intmax.SetSize(ncp3);
intmin = 0.0;
intmax = 0.0;
Vector intminT(ncp2*nb);
Vector intmaxT(ncp2*nb);
// Get bounds for each slice of the solution
for (int i = 0; i < nb; i++)
{
Vector solcoeff(coeff.GetData()+i*nb2, nb2);
Vector intminrow(intminT.GetData()+i*ncp2, ncp2);
Vector intmaxrow(intmaxT.GetData()+i*ncp2, ncp2);
Get2DBounds(solcoeff, intminrow, intmaxrow);
}
DenseMatrix intminTM(intminT.GetData(), ncp2, nb),
intmaxTM(intmaxT.GetData(), ncp2, nb);
// Compute a0 and a1 for each tower of nodes
Vector a0V(ncp2), a1V(ncp2);
a0V = 0.0;
a1V = 0.0;
real_t x,w,t;
if (proj)
{
if (b_type == 2) // Bernstein bases
{
// Compute the mean coefficients along each tower.
for (int j = 0; j < ncp2; j++) // slice of interval points
{
Vector meanBounds(nb), minBounds(nb), maxBounds(nb);
intminTM.GetRow(j, minBounds);
intmaxTM.GetRow(j, maxBounds);
for (int i = 0; i < nb; i++) // column of nodes
{
meanBounds(i) = 0.5*(minBounds(i)+maxBounds(i));
}
Vector meanNodalIntVals(nb);
Vector minNodalVals(nb);
Vector maxNodalVals(nb);
Vector row(nb);
for (int i = 0; i < nb; i++)
{
basisMatNodes.GetRow(i, row);
minNodalVals(i) = row*minBounds;
maxNodalVals(i) = row*maxBounds;
basisMatInt.GetRow(i, row);
meanNodalIntVals(i) = row*meanBounds;
}
// linear fit along each tower
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes_int(i)-1; // x-coordinate
w = 2.0*weights_int(i); // weight
a0V(j) += 0.5*meanNodalIntVals(i)*w;
a1V(j) += 1.5*meanNodalIntVals(i)*w*x;
}
// offset the linear fit from bounding coefficients
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes(i)-1; // x-coordinate
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, minBounds.GetData());
lu.Solve(nb, 1, maxBounds.GetData());
for (int i = 0; i < nb; i++)
{
intminT(i*ncp2+j) = minBounds(i);
intmaxT(i*ncp2+j) = maxBounds(i);
}
}
}
else
{
// nodal bases
for (int j = 0; j < nb; j++) // tower of nodes
{
x = 2.0*nodes(j)-1; // x-coordinate
w = 2.0*weights(j); // weight
for (int i = 0; i < ncp2; i++) // slice of interval points
{
t = 0.5*(intminT(j*ncp2+i)+intmaxT(j*ncp2+i));
a0V(i) += 0.5*t*w;
a1V(i) += 1.5*t*w*x;
}
}
// offset the linear fit from nodal values
for (int j = 0; j < nb; j++) // row of nodes
{
x = 2.0*nodes(j)-1; // x-coordinate
for (int i = 0; i < ncp2; i++) // column of interval points
{
t = a0V(i) + a1V(i)*x;
intminT(j*ncp2+i) -= t;
intmaxT(j*ncp2+i) -= t;
}
}
}
// Initialize bounds using a0 and a1 values
for (int j = 0; j < ncp; j++) // slice j
{
x = 2.0*control_points(j)-1;
for (int i = 0; i < ncp2; i++) // tower i
{
intmin(j*ncp2+i) = a0V(i) + a1V(i)*x;
intmax(j*ncp2+i) = a0V(i) + a1V(i)*x;
}
}
}
// Compute bounds
int id1 = 0, id2 = 0;
Vector vals(4);
for (int j = 0; j < nb; j++)
{
for (int i = 0; i < ncp2; i++) // ith tower
{
real_t w0 = intminT(id1++);
real_t w1 = intmaxT(id2++);
for (int k = 0; k < ncp; k++) // kth slice
{
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();
}
}
}
}
void PLBound::GetNDBounds(int rdim, Vector &coeff,
Vector &intmin, Vector &intmax) const
{
if (rdim == 1)
{
Get1DBounds(coeff, intmin, intmax);
}
else if (rdim == 2)
{
Get2DBounds(coeff, intmin, intmax);
}
else if (rdim == 3)
{
Get3DBounds(coeff, intmin, intmax);
}
else
{
MFEM_ABORT("Currently not supported.");
}
}
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
Array<int> ordering = el.GetLexicographicOrdering();
basisMat.SetSize(nbern, nbern);
Vector shape(nbern);
IntegrationPoint ip;
for (int i = 0; i < nbern; i++)
{
ip.x = nodesBern(i);
el.CalcShape(ip, shape);
basisMat.SetRow(i, shape);
}
}
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];
int PLBound::GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
int cp_type_i) const
{
MFEM_VERIFY(b_type_i >= 0 && b_type_i <= 2, "Invalid node type. Specify 0 "
"for GL, 1 for GLL, and 2 for positive " "bases.");
MFEM_VERIFY(cp_type_i == 0 || cp_type_i == 1, "Invalid control point type. "
"Specify 0 for GL+end points, 1 for Chebyshev.");
if (nb_i > 12)
{
MFEM_ABORT("GetMinimumPointsForGivenBases can only be used for maximum "
"order = 11, i.e. nb=12. 2*nb points should be sufficient to "
"bound the bases up to nb = 30.");
}
else if (b_type_i == 0)
{
return min_ncp_gl_x[cp_type_i][nb_i-2];
}
else if (b_type_i == 1)
{
return min_ncp_gll_x[cp_type_i][nb_i-2];
}
else if (b_type_i == 2)
{
return min_ncp_pos_x[cp_type_i][nb_i-2];
}
return 0;
}
void PLBound::Print(std::ostream &outp) const
{
outp << "PLBound nb: " << nb << std::endl;
outp << "PLBound ncp: " << ncp << std::endl;
outp << "PLBound b_type: " << b_type << std::endl;
outp << "PLBound cp_type: " << cp_type << std::endl;
outp << "Print nodes: " << std::endl;
nodes.Print(outp);
outp << "Print weights: " << std::endl;
weights.Print(outp);
outp << "Print control_points: " << std::endl;
control_points.Print(outp);
outp << "Print lower bounds: " << std::endl;
lbound.Print(outp);
outp << "Print upper bounds: " << std::endl;
ubound.Print(outp);
}
}
-136
View File
@@ -1,136 +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.
#ifndef MFEM_BOUND
#define MFEM_BOUND
#include "../config/config.hpp"
#include "fespace.hpp"
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:
(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
2 - Positive/Bernstein bases on uniformly distributed nodes,
(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.
Note: @b nb and @b b_type are inferred directly from the grid-function.
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.
Finally, only tensor-product elements are currently supported.
For more technical details see:
Mittal et al., "General Field Evaluation in High-Order Meshes on GPUs" &
Dzanic et al., "A method for bounding high-order finite element
functions: Applications to mesh validity and bounds-preserving limiters".
*/
class PLBound
{
private:
int nb; // #mesh nodes in 1D
int ncp; // #control points in 1D
int b_type; // bases type: 0 - GL, 1 - GLL, 2 - Bernstein
int cp_type; // control points type: 0 - GL+Ends, 1 - Chebyshev
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; // 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
Vector nodes_int, weights_int; // Integration nodes and weights
DenseMatrix basisMatLU; // Used to compute LU factors for Bernstein
mutable Array<int> lu_ip;
// stores min_ncp for nb = 2..12 for Lagrange interpolants on GL nodes
// with GL+end points and Chebyshev points as control points
static constexpr int min_ncp_gl_x[2][11]= {{3,5,6,8,9,10,11,11,12,13,14},
{3,5,8,9,11,12,14,15,17,18,20}
};
// stores min_ncp for nb = 2..12 for Lagrange interpolants on GLL nodes
// with GL+end points and Chebyshev points as control points
static constexpr int min_ncp_gll_x[2][11]= {{3,5,7,8,9,10,12,13,14,15,16},
{3,5,8,10,12,13,15,17,19,21,22}
};
// stores min_ncp for nb = 2..12 for Bernstein bases with GL+end points
// and Chebyshev points as control points
static constexpr int min_ncp_pos_x[2][11]= {{3,5,7,8,8,9,10,10,11,12,13},
{3,5,8,9,11,12,13,13,14,15,16}
};
public:
// Constructor
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
const int cp_type_i, const real_t tol_i)
{
Setup(nb_i, ncp_i, b_type_i, cp_type_i, tol_i);
}
// Constructor
PLBound(FiniteElementSpace *fes, int ncp_i = -1, int cp_type_i = 0);
// 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
void Print(std::ostream &outp = mfem::out) const;
// 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_; }
/// 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; }
private:
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D.
void Get1DBounds(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;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 3D.
void Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
/// 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,
const int cp_type_i, const real_t tol_i);
};
} // namespace mfem
#endif // MFEM_BOUND
@@ -8,7 +8,6 @@
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include <ceed/types.h>
/// A structure used to pass additional data to f_build_conv and f_apply_conv
struct ConvectionContext {
@@ -92,7 +91,7 @@ CEED_QFUNCTION(f_build_conv_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for building quadrature data for a convection operator
@@ -168,7 +167,7 @@ CEED_QFUNCTION(f_build_conv_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a conv operator
@@ -234,7 +233,7 @@ CEED_QFUNCTION(f_apply_conv)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a conv operator
@@ -382,7 +381,7 @@ CEED_QFUNCTION(f_apply_conv_mf_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
CEED_QFUNCTION(f_apply_conv_mf_quad)(void *ctx, CeedInt Q,
@@ -526,5 +525,5 @@ CEED_QFUNCTION(f_apply_conv_mf_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
@@ -8,7 +8,7 @@
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include <ceed/types.h>
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct DiffusionContext { CeedInt dim, space_dim, vdim; CeedScalar coeff; };
@@ -85,7 +85,7 @@ CEED_QFUNCTION(f_build_diff_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for building quadrature data for a diffusion operator
@@ -161,7 +161,7 @@ CEED_QFUNCTION(f_build_diff_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a diff operator
@@ -241,7 +241,7 @@ CEED_QFUNCTION(f_apply_diff)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a diff operator
@@ -394,7 +394,7 @@ CEED_QFUNCTION(f_apply_diff_mf_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
CEED_QFUNCTION(f_apply_diff_mf_quad)(void *ctx, CeedInt Q,
@@ -549,5 +549,5 @@ CEED_QFUNCTION(f_apply_diff_mf_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
+6 -6
View File
@@ -8,7 +8,7 @@
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include <ceed/types.h>
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct MassContext { CeedInt dim, space_dim, vdim; CeedScalar coeff; };
@@ -53,7 +53,7 @@ CEED_QFUNCTION(f_build_mass_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for building quadrature data for a mass operator with a
@@ -95,7 +95,7 @@ CEED_QFUNCTION(f_build_mass_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a mass operator
@@ -135,7 +135,7 @@ CEED_QFUNCTION(f_apply_mass)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a diff operator
@@ -199,7 +199,7 @@ CEED_QFUNCTION(f_apply_mass_mf_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
CEED_QFUNCTION(f_apply_mass_mf_quad)(void *ctx, CeedInt Q,
@@ -266,5 +266,5 @@ CEED_QFUNCTION(f_apply_mass_mf_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
@@ -8,7 +8,6 @@
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include <ceed/types.h>
/// A structure used to pass additional data to f_build_conv and f_apply_conv
struct NLConvectionContext { CeedInt dim, space_dim, vdim; CeedScalar coeff; };
@@ -88,7 +87,7 @@ CEED_QFUNCTION(f_build_conv_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for building quadrature data for a convection operator
@@ -168,7 +167,7 @@ CEED_QFUNCTION(f_build_conv_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a conv operator
@@ -248,7 +247,7 @@ CEED_QFUNCTION(f_apply_conv)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
/// libCEED Q-function for applying a conv operator
@@ -363,7 +362,7 @@ CEED_QFUNCTION(f_apply_conv_mf_const)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
CEED_QFUNCTION(f_apply_conv_mf_quad)(void *ctx, CeedInt Q,
@@ -476,5 +475,5 @@ CEED_QFUNCTION(f_apply_conv_mf_quad)(void *ctx, CeedInt Q,
}
break;
}
return CEED_ERROR_SUCCESS;
return 0;
}
+1 -12
View File
@@ -18,21 +18,10 @@
#include <ceed.h>
#if !CEED_VERSION_GE(0, 12, 0)
#if !CEED_VERSION_GE(0,12,0)
#error MFEM requires a libCEED version >= 0.12.0
#endif
#if !CEED_VERSION_GE(0, 13, 0)
#define CeedOperatorCreateComposite(ceed, op) \
CeedCompositeOperatorCreate((ceed), (op))
#define CeedOperatorCompositeAddSub(op, sub) \
CeedCompositeOperatorAddSub((op), (sub))
#define CeedOperatorCompositeGetNumSub(op, num) \
CeedCompositeOperatorGetNumSub((op), (num))
#define CeedOperatorCompositeGetSubList(op, list) \
CeedCompositeOperatorGetSubList((op), (list))
#endif
namespace mfem
{
+2 -2
View File
@@ -83,7 +83,7 @@ public:
}
// Create composite CeedOperator
CeedOperatorCreateComposite(internal::ceed, &oper);
CeedCompositeOperatorCreate(internal::ceed, &oper);
// Create each sub-CeedOperator
sub_ops.reserve(element_indices.size());
@@ -101,7 +101,7 @@ public:
int nelem = *count[value.first];
sub_op->Assemble(info, fes, ir, nelem, indices, Q);
sub_ops.push_back(sub_op);
CeedOperatorCompositeAddSub(oper, sub_op->GetCeedOperator());
CeedCompositeOperatorAddSub(oper, sub_op->GetCeedOperator());
}
const int ndofs = fes.GetVDim() * fes.GetNDofs();
+6 -6
View File
@@ -140,7 +140,11 @@ int CeedOperatorGetActiveField(CeedOperator oper, CeedOperatorField *field)
CeedOperator *subops;
if (isComposite)
{
ierr = CeedOperatorCompositeGetSubList(oper, &subops); PCeedChk(ierr);
#if CEED_VERSION_GE(0, 10, 2)
ierr = CeedCompositeOperatorGetSubList(oper, &subops); PCeedChk(ierr);
#else
ierr = CeedOperatorGetSubList(oper, &subops); PCeedChk(ierr);
#endif
ierr = CeedOperatorGetQFunction(subops[0], &qf); PCeedChk(ierr);
}
else
@@ -167,11 +171,7 @@ int CeedOperatorGetActiveField(CeedOperator oper, CeedOperatorField *field)
for (int i = 0; i < numinputfields; ++i)
{
ierr = CeedOperatorFieldGetVector(inputfields[i], &if_vector); PCeedChk(ierr);
bool is_active = if_vector == CEED_VECTOR_ACTIVE;
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&if_vector); PCeedChk(ierr);
#endif
if (is_active)
if (if_vector == CEED_VECTOR_ACTIVE)
{
if (found)
{
+11 -6
View File
@@ -228,7 +228,7 @@ void AddToCompositeOperator(BilinearFormIntegrator *integ, CeedOperator op)
{
if (integ->SupportsCeed())
{
CeedOperatorCompositeAddSub(op, integ->GetCeedOp().GetCeedOperator());
CeedCompositeOperatorAddSub(op, integ->GetCeedOp().GetCeedOperator());
}
else
{
@@ -240,7 +240,7 @@ CeedOperator CreateCeedCompositeOperatorFromBilinearForm(BilinearForm &form)
{
int ierr;
CeedOperator op;
ierr = CeedOperatorCreateComposite(internal::ceed, &op); PCeedChk(ierr);
ierr = CeedCompositeOperatorCreate(internal::ceed, &op); PCeedChk(ierr);
MFEM_VERIFY(form.GetBBFI()->Size() == 0,
"Not implemented for this integrator!");
@@ -271,13 +271,18 @@ CeedOperator CoarsenCeedCompositeOperator(
MFEM_ASSERT(isComposite, "");
CeedOperator op_coarse;
ierr = CeedOperatorCreateComposite(internal::ceed,
ierr = CeedCompositeOperatorCreate(internal::ceed,
&op_coarse); PCeedChk(ierr);
int nsub;
CeedOperator *subops;
ierr = CeedOperatorCompositeGetNumSub(op, &nsub); PCeedChk(ierr);
ierr = CeedOperatorCompositeGetSubList(op, &subops); PCeedChk(ierr);
#if CEED_VERSION_GE(0, 10, 2)
ierr = CeedCompositeOperatorGetNumSub(op, &nsub); PCeedChk(ierr);
ierr = CeedCompositeOperatorGetSubList(op, &subops); PCeedChk(ierr);
#else
ierr = CeedOperatorGetNumSub(op, &nsub); PCeedChk(ierr);
ierr = CeedOperatorGetSubList(op, &subops); PCeedChk(ierr);
#endif
for (int isub=0; isub<nsub; ++isub)
{
CeedOperator subop = subops[isub];
@@ -289,7 +294,7 @@ CeedOperator CoarsenCeedCompositeOperator(
// refcounted by existing objects
ierr = CeedBasisDestroy(&basis_coarse); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basis_c2f); PCeedChk(ierr);
ierr = CeedOperatorCompositeAddSub(op_coarse, subop_coarse);
ierr = CeedCompositeOperatorAddSub(op_coarse, subop_coarse);
PCeedChk(ierr);
ierr = CeedOperatorDestroy(&subop_coarse); PCeedChk(ierr);
}
+14 -52
View File
@@ -81,27 +81,12 @@ int CeedSingleOperatorFullAssemble(CeedOperator op, SparseMatrix *out)
ierr = CeedOperatorFieldGetVector(input_fields[i], &vec); PCeedChk(ierr);
if (vec == CEED_VECTOR_ACTIVE)
{
CeedBasis basis;
ierr = CeedOperatorFieldGetBasis(input_fields[i], &basis); PCeedChk(ierr);
if (!basisin)
{
ierr = CeedBasisReferenceCopy(basis, &basisin); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedBasisDestroy(&basis); PCeedChk(ierr);
#endif
ierr = CeedOperatorFieldGetBasis(input_fields[i], &basisin);
PCeedChk(ierr);
ierr = CeedBasisGetNumComponents(basisin, &ncomp); PCeedChk(ierr);
ierr = CeedBasisGetDimension(basisin, &dim); PCeedChk(ierr);
CeedElemRestriction rstr;
ierr = CeedOperatorFieldGetElemRestriction(input_fields[i], &rstr);
ierr = CeedOperatorFieldGetElemRestriction(input_fields[i], &rstrin);
PCeedChk(ierr);
if (!rstrin)
{
ierr = CeedElemRestrictionReferenceCopy(rstr, &rstrin); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedElemRestrictionDestroy(&rstr); PCeedChk(ierr);
#endif
CeedEvalMode emode;
ierr = CeedQFunctionFieldGetEvalMode(qffields[i], &emode);
PCeedChk(ierr);
@@ -127,9 +112,6 @@ int CeedSingleOperatorFullAssemble(CeedOperator op, SparseMatrix *out)
break; // Caught by QF Assembly
}
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&vec); PCeedChk(ierr);
#endif
}
// Determine active output basis
@@ -145,25 +127,11 @@ int CeedSingleOperatorFullAssemble(CeedOperator op, SparseMatrix *out)
ierr = CeedOperatorFieldGetVector(output_fields[i], &vec); PCeedChk(ierr);
if (vec == CEED_VECTOR_ACTIVE)
{
CeedBasis basis;
ierr = CeedOperatorFieldGetBasis(output_fields[i], &basis); PCeedChk(ierr);
if (!basisout)
{
ierr = CeedBasisReferenceCopy(basis, &basisout); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedBasisDestroy(&basis); PCeedChk(ierr);
#endif
CeedElemRestriction rstr;
ierr = CeedOperatorFieldGetElemRestriction(output_fields[i], &rstr);
ierr = CeedOperatorFieldGetBasis(output_fields[i], &basisout);
PCeedChk(ierr);
ierr = CeedOperatorFieldGetElemRestriction(output_fields[i], &rstrout);
PCeedChk(ierr);
PCeedChk(ierr);
if (!rstrout)
{
ierr = CeedElemRestrictionReferenceCopy(rstr, &rstrout); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedElemRestrictionDestroy(&rstr); PCeedChk(ierr);
#endif
CeedEvalMode emode;
ierr = CeedQFunctionFieldGetEvalMode(qffields[i], &emode);
PCeedChk(ierr);
@@ -189,9 +157,6 @@ int CeedSingleOperatorFullAssemble(CeedOperator op, SparseMatrix *out)
break; // Caught by QF Assembly
}
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&vec); PCeedChk(ierr);
#endif
}
CeedInt nelem, elemsize, nqpts;
@@ -235,11 +200,7 @@ int CeedSingleOperatorFullAssemble(CeedOperator op, SparseMatrix *out)
PCeedChk(ierr);
CeedInt layout[3];
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedElemRestrictionGetELayout(rstr_q, layout); PCeedChk(ierr);
#else
ierr = CeedElemRestrictionGetELayout(rstr_q, &layout); PCeedChk(ierr);
#endif
ierr = CeedElemRestrictionDestroy(&rstr_q); PCeedChk(ierr);
// enforce structurally symmetric for later elimination
@@ -324,10 +285,6 @@ int CeedSingleOperatorFullAssemble(CeedOperator op, SparseMatrix *out)
ierr = CeedVectorRestoreArrayRead(assembledqf, &assembledqfarray);
PCeedChk(ierr);
ierr = CeedVectorDestroy(&assembledqf); PCeedChk(ierr);
ierr = CeedElemRestrictionDestroy(&rstrin); PCeedChk(ierr);
ierr = CeedElemRestrictionDestroy(&rstrout); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basisin); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basisout); PCeedChk(ierr);
ierr = CeedHackFree(&emodein); PCeedChk(ierr);
ierr = CeedHackFree(&emodeout); PCeedChk(ierr);
@@ -353,8 +310,13 @@ int CeedOperatorFullAssemble(CeedOperator op, SparseMatrix **mat)
{
CeedInt numsub;
CeedOperator *subops;
ierr = CeedOperatorCompositeGetNumSub(op, &numsub); PCeedChk(ierr);
ierr = CeedOperatorCompositeGetSubList(op, &subops); PCeedChk(ierr);
#if CEED_VERSION_GE(0, 10, 2)
CeedCompositeOperatorGetNumSub(op, &numsub);
ierr = CeedCompositeOperatorGetSubList(op, &subops); PCeedChk(ierr);
#else
CeedOperatorGetNumSub(op, &numsub);
ierr = CeedOperatorGetSubList(op, &subops); PCeedChk(ierr);
#endif
for (int i = 0; i < numsub; ++i)
{
ierr = CeedSingleOperatorFullAssemble(subops[i], out); PCeedChk(ierr);
+1 -25
View File
@@ -120,11 +120,7 @@ int CeedATPMGElemRestriction(int order,
}
ierr = CeedVectorRestoreArray(in_lvec, &lvec_data); PCeedChk(ierr);
CeedInt in_layout[3];
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedElemRestrictionGetELayout(er_in, in_layout); PCeedChk(ierr);
#else
ierr = CeedElemRestrictionGetELayout(er_in, &in_layout); PCeedChk(ierr);
#endif
if (in_layout[0] == 0 && in_layout[1] == 0 && in_layout[2] == 0)
{
return CeedError(ceed, 1, "Cannot interpret e-vector ordering of given"
@@ -668,11 +664,7 @@ int CeedATPMGOperator(CeedOperator oper, int order_reduction,
for (int i = 0; i < numinputfields; ++i)
{
#if CEED_VERSION_GE(0, 13, 0)
const char * fieldname;
#else
char * fieldname;
#endif
ierr = CeedQFunctionFieldGetName(inputqfields[i], &fieldname); PCeedChk(ierr);
if (if_vector[i] == CEED_VECTOR_ACTIVE)
{
@@ -684,19 +676,10 @@ int CeedATPMGOperator(CeedOperator oper, int order_reduction,
ierr = CeedOperatorSetField(coper, fieldname, er_input[i], basis_input[i],
if_vector[i]); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&if_vector[i]); PCeedChk(ierr);
ierr = CeedElemRestrictionDestroy(&er_input[i]); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basis_input[i]); PCeedChk(ierr);
#endif
}
for (int i = 0; i < numoutputfields; ++i)
{
#if CEED_VERSION_GE(0, 13, 0)
const char * fieldname;
#else
char * fieldname;
#endif
ierr = CeedQFunctionFieldGetName(outputqfields[i], &fieldname); PCeedChk(ierr);
if (of_vector[i] == CEED_VECTOR_ACTIVE)
{
@@ -708,11 +691,6 @@ int CeedATPMGOperator(CeedOperator oper, int order_reduction,
ierr = CeedOperatorSetField(coper, fieldname, er_output[i], basis_output[i],
of_vector[i]); PCeedChk(ierr);
}
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedVectorDestroy(&of_vector[i]); PCeedChk(ierr);
ierr = CeedElemRestrictionDestroy(&er_output[i]); PCeedChk(ierr);
ierr = CeedBasisDestroy(&basis_output[i]); PCeedChk(ierr);
#endif
}
delete [] er_input;
delete [] er_output;
@@ -763,9 +741,7 @@ int CeedOperatorGetOrder(CeedOperator oper, CeedInt * order)
int P1d;
ierr = CeedBasisGetNumNodes1D(basis, &P1d); PCeedChk(ierr);
*order = P1d - 1;
#if CEED_VERSION_GE(0, 13, 0)
ierr = CeedBasisDestroy(&basis); PCeedChk(ierr);
#endif
return 0;
}
-99
View File
@@ -12,7 +12,6 @@
// Implementation of Coefficient class
#include "fem.hpp"
#include "../general/forall.hpp"
#include <cmath>
#include <limits>
@@ -81,49 +80,6 @@ real_t PWConstCoefficient::Eval(ElementTransformation & T,
return (constants(att-1));
}
void PWConstCoefficient::Project(QuadratureFunction &qf)
{
auto &qs = *qf.GetSpace();
const bool compressed =
qs.Offsets(QSpaceOffsetStorage::COMPRESSED).Size() == 1;
const int *offsets = qs.Offsets(QSpaceOffsetStorage::COMPRESSED).Read();
const int ne = qs.GetNE();
const int *attributes = [&]()
{
if (dynamic_cast<QuadratureSpace*>(&qs) != nullptr)
{
return qs.GetMesh()->GetElementAttributes().Read();
}
else if (auto *qs_f = dynamic_cast<FaceQuadratureSpace*>(&qs))
{
MFEM_VERIFY(qs_f->GetFaceType() == FaceType::Boundary,
"Interior faces do not have attributes.");
return qs.GetMesh()->GetBdrFaceAttributes().Read();
}
else
{
MFEM_ABORT("Unsupported case.");
}
}();
const real_t *d_c = constants.Read();
real_t *d_qf = qf.Write();
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
const int a = attributes[e];
const real_t elementConstant = d_c[a - 1];
const int begin = compressed ? e*offsets[0] : offsets[e];
const int end = compressed ? (e+1)*offsets[0] : offsets[e+1];
for (int i = begin; i < end; ++i)
{
d_qf[i] = elementConstant;
}
});
}
void PWCoefficient::InitMap(const Array<int> & attr,
const Array<Coefficient*> & coefs)
{
@@ -563,26 +519,6 @@ void GradientGridFunctionCoefficient::Eval(
}
}
void GradientGridFunctionCoefficient::Project(QuadratureFunction &qf)
{
const FiniteElementSpace &fes = *GridFunc->FESpace();
const Mesh &mesh = *fes.GetMesh();
const int sdim = mesh.SpaceDimension();
const int gf_vdim = fes.GetVDim(); // assumed to be 1 in this class
qf.SetVDim(sdim*gf_vdim);
if (mesh.GetNE() == 0) { return; }
// All mesh element must be the same type:
MFEM_VERIFY(mesh.GetNumGeometries(mesh.Dimension()) == 1,
"All mesh elements must be the same type!");
const IntegrationRule &ir = qf.GetIntRule(0);
// All elements must use the same quadrature rule:
MFEM_VERIFY(qf.Size() == sdim*gf_vdim*ir.GetNPoints()*mesh.GetNE(),
"All mesh elements must use the same quadrature rule!");
// QuadratureFunction uses the layout qf_vdim x nq x ne, i.e.
// gf_vdim x sdim x nq x nq, so we need to request QVectorLayout::byVDIM:
GridFunc->GetGradients(ir, qf, QVectorLayout::byVDIM);
}
CurlGridFunctionCoefficient::CurlGridFunctionCoefficient(
const GridFunction *gf)
: VectorCoefficient(0)
@@ -1129,41 +1065,6 @@ real_t InnerProductCoefficient::Eval(ElementTransformation &T,
return va * vb;
}
void InnerProductCoefficient::Project(QuadratureFunction &qf)
{
MFEM_VERIFY(a->GetVDim() == b->GetVDim(),
"Incompatible vector coefficients: a->GetVDim(): "
<< a->GetVDim() << ", b->GetVDim(): " << b->GetVDim());
const int vdim = a->GetVDim();
MFEM_VERIFY(vdim >= 1, "invalid vdim: " << vdim);
// When running on device, make sure the output data is allocated before any
// local temporary data to reduce potential heap fragmentation:
auto dot_d = qf.Write();
QuadratureFunction qf_a(qf.GetSpace(), vdim);
QuadratureFunction qf_b(qf.GetSpace(), vdim);
a->Project(qf_a);
b->Project(qf_b);
auto a_d = qf_a.Read();
auto b_d = qf_b.Read();
mfem::forall(qf.GetSpace()->GetSize(), [=] MFEM_HOST_DEVICE (int i)
{
const real_t *ai = a_d + i*vdim;
const real_t *bi = b_d + i*vdim;
real_t dot = ai[0]*bi[0];
for (int d = 1; d < vdim; d++)
{
dot += ai[d]*bi[d];
}
dot_d[i] = dot;
});
}
VectorRotProductCoefficient::VectorRotProductCoefficient(VectorCoefficient &A,
VectorCoefficient &B)
: a(&A), b(&B), va(A.GetVDim()), vb(B.GetVDim())
+2 -16
View File
@@ -132,9 +132,6 @@ public:
/// Evaluate the coefficient.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
/// Fill the QuadratureFunction @a qf with the piecewise constant values.
void Project(QuadratureFunction &qf) override;
};
/** @brief A piecewise coefficient with the pieces keyed off the element
@@ -243,9 +240,7 @@ public:
Vector argument instead of Vector. */
MFEM_DEPRECATED FunctionCoefficient(real_t (*f)(Vector &))
{
// Cast first to (void*) to suppress a warning from newer version of
// Clang when using -Wextra.
Function = reinterpret_cast<real_t(*)(const Vector&)>((void*)f);
Function = reinterpret_cast<real_t(*)(const Vector&)>(f);
TDFunction = NULL;
}
@@ -255,10 +250,7 @@ public:
MFEM_DEPRECATED FunctionCoefficient(real_t (*tdf)(Vector &, real_t))
{
Function = NULL;
// Cast first to (void*) to suppress a warning from newer version of
// Clang when using -Wextra.
TDFunction =
reinterpret_cast<real_t(*)(const Vector&,real_t)>((void*)tdf);
TDFunction = reinterpret_cast<real_t(*)(const Vector&,real_t)>(tdf);
}
/// Evaluate the coefficient at @a ip.
@@ -897,9 +889,6 @@ public:
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir) override;
/// @copydoc VectorCoefficient::Project(QuadratureFunction &)
void Project(QuadratureFunction &qf) override;
virtual ~GradientGridFunctionCoefficient() { }
};
@@ -1777,9 +1766,6 @@ public:
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
/// @copydoc Coefficient::Project(QuadratureFunction &)
void Project(QuadratureFunction &qf) override;
};
/// Scalar coefficient defined as a cross product of two vectors in the xy-plane.
+4 -4
View File
@@ -912,7 +912,7 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
if (vdim == 1) // scalar case
{
n_field["values"].set_external(const_cast<real_t *>(gf->HostRead()),
n_field["values"].set_external(gf->GetData(),
ndofs);
}
else // vector case
@@ -925,18 +925,18 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
int vdim_stride = (ordering == Ordering::byNODES ? ndofs : 1);
index_t offset = 0;
index_t stride = sizeof(real_t) * entry_stride;
index_t stride = sizeof(double) * entry_stride;
for (int d = 0; d < 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 *>(gf->HostRead()),
n_field["values"][comp_name].set_external(gf->GetData(),
ndofs,
offset,
stride);
offset += sizeof(real_t) * vdim_stride;
offset += sizeof(double) * vdim_stride;
}
}
+55 -123
View File
@@ -12,7 +12,6 @@
#include "fem.hpp"
#include "../mesh/nurbs.hpp"
#include "../mesh/vtk.hpp"
#include "../mesh/vtkhdf.hpp"
#include "../general/binaryio.hpp"
#include "../general/text.hpp"
#include "picojson.h"
@@ -759,59 +758,35 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
}
}
ParaViewDataCollectionBase::ParaViewDataCollectionBase(
const std::string &name, Mesh *mesh) : DataCollection(name, mesh)
ParaViewDataCollection::ParaViewDataCollection(const std::string&
collection_name,
Mesh *mesh_)
: DataCollection(collection_name, mesh_),
levels_of_detail(1),
pv_data_format(VTKFormat::BINARY),
high_order_output(false),
restart_mode(false)
{
cycle = 0;
cycle = 0; // always include a valid cycle index in file names
compression_level = -1; // default zlib compression level, equivalent to 6
#ifdef MFEM_USE_ZLIB
// If we have zlib, enable compression. Otherwise, compression is disabled in
// the DataCollection base class constructor.
compression = true;
compression = true; // if we have zlib, enable compression
#else
compression = false; // otherwise, disable compression
#endif
}
void ParaViewDataCollectionBase::SetLevelsOfDetail(int levels_of_detail_)
void ParaViewDataCollection::SetLevelsOfDetail(int levels_of_detail_)
{
levels_of_detail = levels_of_detail_;
}
void ParaViewDataCollectionBase::SetHighOrderOutput(bool high_order_output_)
void ParaViewDataCollection::Load(int )
{
high_order_output = high_order_output_;
MFEM_WARNING("ParaViewDataCollection::Load() is not implemented!");
}
void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
{
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
"Compression level must be between -1 and 9 (inclusive).");
if (compression_level_ != 0) { SetCompression(true);}
compression_level = compression_level_;
}
int ParaViewDataCollectionBase::GetCompressionLevel() const
{
return compression ? compression_level : 0;
}
void ParaViewDataCollectionBase::SetDataFormat(VTKFormat fmt)
{
pv_data_format = fmt;
}
bool ParaViewDataCollectionBase::IsBinaryFormat() const
{
return pv_data_format != VTKFormat::ASCII;
}
void ParaViewDataCollectionBase::UseRestartMode(bool restart_mode_)
{
restart_mode = restart_mode_;
}
ParaViewDataCollection::ParaViewDataCollection(
const std::string& collection_name, Mesh *mesh_)
: ParaViewDataCollectionBase(collection_name, mesh_) { }
std::string ParaViewDataCollection::GenerateCollectionPath()
{
return prefix_path + DataCollection::GetCollectionName();
@@ -926,7 +901,7 @@ void ParaViewDataCollection::Save()
// Initialize new pvd file.
pvd_stream.open(pvdname,std::ios::out|std::ios::trunc);
pvd_stream << "<?xml version=\"1.0\"?>\n";
pvd_stream << "<VTKFile type=\"Collection\" version=\"2.2\"";
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
pvd_stream << "<Collection>" << std::endl;
}
@@ -1026,7 +1001,7 @@ void ParaViewDataCollection::WritePVTUHeader(std::ostream &os)
{
os << "<?xml version=\"1.0\"?>\n";
os << "<VTKFile type=\"PUnstructuredGrid\"";
os << " version =\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << " version =\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
os << "<PPoints>\n";
@@ -1067,7 +1042,7 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
{
os << " compressor=\"vtkZLibDataCompressor\"";
}
os << " version=\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << " version=\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << "<UnstructuredGrid>\n";
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel());
@@ -1140,6 +1115,39 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
os << "</DataArray>" << std::endl;
}
void ParaViewDataCollection::SetDataFormat(VTKFormat fmt)
{
pv_data_format = fmt;
}
bool ParaViewDataCollection::IsBinaryFormat() const
{
return pv_data_format != VTKFormat::ASCII;
}
void ParaViewDataCollection::SetHighOrderOutput(bool high_order_output_)
{
high_order_output = high_order_output_;
}
void ParaViewDataCollection::SetCompressionLevel(int compression_level_)
{
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
"Compression level must be between -1 and 9 (inclusive).");
compression_level = compression_level_;
compression = compression_level_ != 0;
}
void ParaViewDataCollection::SetCompression(bool compression_)
{
compression = compression_;
}
void ParaViewDataCollection::UseRestartMode(bool restart_mode_)
{
restart_mode = restart_mode_;
}
const char *ParaViewDataCollection::GetDataFormatString() const
{
if (pv_data_format == VTKFormat::ASCII)
@@ -1164,85 +1172,9 @@ const char *ParaViewDataCollection::GetDataTypeString() const
}
}
#ifdef MFEM_USE_HDF5
ParaViewHDFDataCollection::ParaViewHDFDataCollection(
const std::string &collection_name, Mesh *mesh)
: ParaViewDataCollectionBase(collection_name, mesh)
int ParaViewDataCollection::GetCompressionLevel() const
{
compression = true;
return compression ? compression_level : 0;
}
void ParaViewHDFDataCollection::SetCompression(bool compression_)
{
compression = compression_;
}
void ParaViewHDFDataCollection::EnsureVTKHDF()
{
if (!vtkhdf)
{
if (!prefix_path.empty())
{
const int error_code = create_directory(prefix_path, mesh, myid);
MFEM_VERIFY(error_code == 0, "Error creating directory " << prefix_path);
}
std::string fname = prefix_path + name + ".vtkhdf";
bool use_mpi = false;
#ifdef MFEM_USE_MPI
if (ParMesh *pmesh = dynamic_cast<ParMesh*>(mesh))
{
use_mpi = true;
#ifdef MFEM_PARALLEL_HDF5
vtkhdf.reset(new VTKHDF(fname, pmesh->GetComm(), {restart_mode, time}));
#else
MFEM_ABORT("Requires HDF5 library with parallel support enabled");
#endif
}
#endif
if (!use_mpi)
{
vtkhdf.reset(new VTKHDF(fname, {restart_mode, time}));
}
}
}
template <typename FP_T>
void ParaViewHDFDataCollection::TSave()
{
EnsureVTKHDF();
if (compression)
{
vtkhdf->EnableCompression(compression_level >= 0 ? compression_level : 6);
}
else
{
vtkhdf->DisableCompression();
}
vtkhdf->SaveMesh<FP_T>(*mesh, high_order_output, levels_of_detail);
for (const auto &field : field_map)
{
vtkhdf->SaveGridFunction<FP_T>(*field.second, field.first);
}
vtkhdf->UpdateSteps(time);
vtkhdf->Flush();
}
void ParaViewHDFDataCollection::Save()
{
switch (pv_data_format)
{
case VTKFormat::BINARY32: TSave<float>(); break;
case VTKFormat::BINARY: TSave<double>(); break;
default: MFEM_ABORT("Unsupported VTK format.");
}
}
ParaViewHDFDataCollection::~ParaViewHDFDataCollection() = default;
#endif
} // end namespace MFEM
+64 -112
View File
@@ -502,27 +502,60 @@ public:
};
/// Abstract base class for ParaViewDataCollection and ParaViewHDFDataCollection
class ParaViewDataCollectionBase : public DataCollection
/// Helper class for ParaView visualization data
class ParaViewDataCollection : public DataCollection
{
protected:
int levels_of_detail = 1;
int compression_level = -1;
bool high_order_output = false;
bool restart_mode = false;
VTKFormat pv_data_format = VTKFormat::BINARY;
public:
ParaViewDataCollectionBase(const std::string &name, Mesh *mesh);
private:
int levels_of_detail;
int compression_level;
std::fstream pvd_stream;
VTKFormat pv_data_format;
bool high_order_output;
bool restart_mode;
/// @brief Set the refinement level.
///
/// In "low-order mode", every element is uniformly split based on the levels
/// of detail. In "high-order mode", this sets the polynomial degree for the
/// element transformations.
///
/// The initial value is 1.
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);
const char *GetDataFormatString() const;
const char *GetDataTypeString() const;
/// @brief If compression is enabled, return the compression level, otherwise
/// return 0.
int GetCompressionLevel() const;
std::string GenerateCollectionPath();
std::string GenerateVTUFileName(const std::string &prefix, int rank);
std::string GenerateVTUPath();
std::string GeneratePVDFileName();
std::string GeneratePVTUFileName(const std::string &prefix);
std::string GeneratePVTUPath();
public:
/// Constructor. The collection name is used when saving the data.
/** If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
Before saving the data collection, some parameters in the collection can
be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc. */
ParaViewDataCollection(const std::string& collection_name,
mfem::Mesh *mesh_ = NULL);
/// Set refinement levels - every element is uniformly split based on
/// levels_of_detail_. The initial value is 1.
void SetLevelsOfDetail(int levels_of_detail_);
/// Save the collection - the directory name is constructed based on the
/// cycle value
void Save() override;
/// Set the data format for the ParaView output files. Possible options are
/// VTKFormat::ASCII, VTKFormat::BINARY, and VTKFormat::BINARY32.
/// The ASCII and BINARY options output double precision data, whereas the
/// BINARY32 option outputs single precision data.
///
/// The initial format is VTKFormat::BINARY.
void SetDataFormat(VTKFormat fmt);
/// @brief Set the zlib compression level.
///
/// 0 indicates no compression, -1 indicates the default compression level.
@@ -537,109 +570,28 @@ public:
/// Any nonzero compression level will enable compression.
void SetCompressionLevel(int compression_level_);
/// @brief Sets whether or not to output the data as high-order elements
/// (false by default).
///
/// Reading high-order data requires ParaView 5.5 or later.
void SetHighOrderOutput(bool high_order_output_);
/// If compression is enabled, return the compression level, else return 0.
int GetCompressionLevel() const;
/// @brief Set the data format for the ParaView output files.
///
/// Possible options are VTKFormat::ASCII, VTKFormat::BINARY, and
/// VTKFormat::BINARY32. The ASCII and BINARY options output double precision
/// data, whereas the BINARY32 option outputs single precision data.
///
/// The initial format is VTKFormat::BINARY.
///
/// VTKFormat::ASCII is not supported by ParaViewHDFDataCollection.
void SetDataFormat(VTKFormat fmt);
/// Enable or disable zlib compression. If the input is true, use the default
/// zlib compression level (unless the compression level has previously been
/// set by calling SetCompressionLevel()).
void SetCompression(bool compression_) override;
/// Returns true if the output format is BINARY or BINARY32, false if ASCII.
bool IsBinaryFormat() const;
/// @brief Enable or disable restart mode.
///
/// If restart is enabled, new writes will preserve timestep metadata for any
/// solutions prior to the currently defined time.
/// Sets whether or not to output the data as high-order elements (false
/// by default). Reading high-order data requires ParaView 5.5 or later.
void SetHighOrderOutput(bool high_order_output_);
/// Enable or disable restart mode. 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_);
/// Load the collection - not implemented in the ParaView writer
void Load(int cycle_ = 0) override;
};
/// Writer for ParaView visualization (PVD and VTU format)
class ParaViewDataCollection : public ParaViewDataCollectionBase
{
private:
std::fstream pvd_stream;
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);
const char *GetDataFormatString() const;
const char *GetDataTypeString() const;
std::string GenerateCollectionPath();
std::string GenerateVTUFileName(const std::string &prefix, int rank);
std::string GenerateVTUPath();
std::string GeneratePVDFileName();
std::string GeneratePVTUFileName(const std::string &prefix);
std::string GeneratePVTUPath();
public:
/// Constructor. The collection name is used when saving the data.
/** If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
Before saving the data collection, some parameters in the collection can
be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc. */
ParaViewDataCollection(const std::string& collection_name,
Mesh *mesh_ = nullptr);
/// Save the collection - the directory name is constructed based on the
/// cycle value
void Save() override;
};
#ifdef MFEM_USE_HDF5
/// Writer for ParaView visualization (%VTKHDF format)
class ParaViewHDFDataCollection : public ParaViewDataCollectionBase
{
/// The low-level VTKHDF object for I/O (pointer to implementation idiom).
std::unique_ptr<class VTKHDF> vtkhdf;
/// Create the VTKHDF object if it doesn't exist already.
void EnsureVTKHDF();
/// Save the collection (templated on floating point type).
template <typename FP_T> void TSave();
public:
/// @brief Constructor. The collection name is used when saving the data.
///
/// If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
/// Before saving the data collection, some parameters in the collection can
/// be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc.
ParaViewHDFDataCollection(const std::string& collection_name,
Mesh *mesh_ = nullptr);
/// @brief Enable or disable compression.
///
/// The compression level can be set with SetCompressionLevel()). VTKHDF
/// compression does not require MFEM to be compiled with zlib support.
void SetCompression(bool compression_) override;
/// Save the collection.
void Save() override;
/// Destructor.
~ParaViewHDFDataCollection();
};
#endif
}
#endif
-266
View File
@@ -1,266 +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.
#include "derefmat_op.hpp"
#include "fes_kernels.hpp"
/// \cond DO_NOT_DOCUMENT
namespace mfem
{
namespace internal
{
template <Ordering::Type Order, bool Atomic>
static void DerefMultKernelImpl(const DerefineMatrixOp &op, const Vector &x,
Vector &y)
{
DerefineMatrixOpMultFunctor<Order, Atomic> func;
func.xptr = x.Read();
y.UseDevice();
y = 0.;
func.yptr = y.ReadWrite();
func.bsptr = op.block_storage.Read();
func.boptr = op.block_offsets.Read();
func.brptr = op.block_row_idcs_offsets.Read();
func.bcptr = op.block_col_idcs_offsets.Read();
func.rptr = op.row_idcs.Read();
func.cptr = op.col_idcs.Read();
func.vdims = op.fespace->GetVDim();
func.nblocks = op.block_offsets.Size();
func.width = op.Width() / func.vdims;
func.height = op.Height() / func.vdims;
func.Run(op.max_rows);
}
} // namespace internal
DerefineMatrixOp::DerefineMatrixOp(FiniteElementSpace &fespace_, int old_ndofs,
const Table *old_elem_dof,
const Table *old_elem_fos)
: Operator(fespace_.GetVSize(), old_ndofs * fespace_.GetVDim()),
fespace(&fespace_)
{
static Kernels kernels;
constexpr int max_team_size = 256;
/// TODO: Implement DofTransformation support
MFEM_VERIFY(fespace->Nonconforming(),
"Not implemented for conforming meshes.");
MFEM_VERIFY(old_ndofs, "Missing previous (finer) space.");
MFEM_VERIFY(fespace->GetNDofs() <= old_ndofs,
"Previous space is not finer.");
const CoarseFineTransformations &dtrans =
fespace->GetMesh()->ncmesh->GetDerefinementTransforms();
MFEM_ASSERT(dtrans.embeddings.Size() == old_elem_dof->Size(), "");
const bool is_dg = fespace->FEColl()->GetContType()
== FiniteElementCollection::DISCONTINUOUS;
DenseMatrix localRVO; // for variable-order only
DenseTensor localR[Geometry::NumGeom];
int total_rows = 0;
int total_cols = 0;
block_offsets.SetSize(dtrans.embeddings.Size());
block_offsets.HostWrite();
if (fespace->IsVariableOrder())
{
// TODO: any potential for some compression here?
// determine storage size and offsets
block_offsets[0] = 0;
int total_size = 0;
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
{
const Embedding &emb = dtrans.embeddings[k];
const FiniteElement *fe = fespace->GetFE(emb.parent);
const int ldof = fe->GetDof();
if (k + 1 < dtrans.embeddings.Size())
{
block_offsets[k + 1] = block_offsets[k] + ldof * ldof;
}
total_rows += ldof;
total_cols += ldof;
total_size += ldof * ldof;
}
block_storage.SetSize(total_size);
}
else
{
// compression scheme:
// block_offsets is the start of each block, potentially repeated
// only need to store localR for used shapes
Mesh::GeometryList elem_geoms(*fespace->GetMesh());
int geom_offsets[Geometry::NumGeom];
{
int size = 0;
for (int i = 0; i < elem_geoms.Size(); ++i)
{
fespace->GetLocalDerefinementMatrices(elem_geoms[i],
localR[elem_geoms[i]]);
geom_offsets[elem_geoms[i]] = size;
size += localR[elem_geoms[i]].TotalSize();
}
block_storage.SetSize(size);
// copy blocks into block_storage
auto bs_ptr = block_storage.HostWrite();
for (int i = 0; i < elem_geoms.Size(); ++i)
{
std::copy(localR[elem_geoms[i]].Data(),
localR[elem_geoms[i]].Data()
+ localR[elem_geoms[i]].TotalSize(),
bs_ptr);
bs_ptr += localR[elem_geoms[i]].TotalSize();
}
}
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
{
const Embedding &emb = dtrans.embeddings[k];
Geometry::Type geom =
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
auto size = localR[geom].SizeI() * localR[geom].SizeJ();
total_rows += localR[geom].SizeI();
total_cols += localR[geom].SizeJ();
// set block offsets and sizes
block_offsets[k] = geom_offsets[geom] + size * emb.matrix;
}
}
row_idcs.SetSize(total_rows);
row_idcs.HostWrite();
col_idcs.SetSize(total_cols);
col_idcs.HostWrite();
block_row_idcs_offsets.SetSize(dtrans.embeddings.Size() + 1);
block_row_idcs_offsets.HostWrite();
block_col_idcs_offsets.SetSize(dtrans.embeddings.Size() + 1);
block_col_idcs_offsets.HostWrite();
block_row_idcs_offsets[0] = 0;
block_col_idcs_offsets[0] = 0;
// compute index information
Array<int> dofs, old_dofs;
max_rows = 1;
{
Array<int> mark(fespace->GetNDofs());
mark = 0;
auto bs_ptr = block_storage.HostWrite();
int ridx = 0;
int cidx = 0;
int num_marked = 0;
for (int k = 0; k < dtrans.embeddings.Size(); k++)
{
const Embedding &emb = dtrans.embeddings[k];
Geometry::Type geom =
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
if (fespace->IsVariableOrder())
{
const FiniteElement *fe = fespace->GetFE(emb.parent);
const DenseTensor &pmats = dtrans.point_matrices[geom];
const int ldof = fe->GetDof();
IsoparametricTransformation isotr;
isotr.SetIdentityTransformation(geom);
localRVO.SetSize(ldof, ldof);
isotr.SetPointMat(pmats(emb.matrix));
// Local restriction is size ldofxldof assuming that the parent
// and child are of same polynomial order.
fe->GetLocalRestriction(isotr, localRVO);
// copy block
auto size = localRVO.Height() * localRVO.Width();
std::copy(localRVO.Data(), localRVO.Data() + size, bs_ptr);
bs_ptr += size;
}
DenseMatrix &lR =
fespace->IsVariableOrder() ? localRVO : localR[geom](emb.matrix);
block_row_idcs_offsets[k + 1] =
block_row_idcs_offsets[k] + lR.Height();
block_col_idcs_offsets[k + 1] = block_col_idcs_offsets[k] + lR.Width();
max_rows = std::max(lR.Height(), max_rows);
// index information
fespace->elem_dof->GetRow(emb.parent, dofs);
old_elem_dof->GetRow(k, old_dofs);
MFEM_VERIFY(old_dofs.Size() == dofs.Size(),
"Parent and child must have same #dofs.");
for (int i = 0; i < lR.Height(); ++i, ++ridx)
{
if (!std::isfinite(lR(i, 0)))
{
row_idcs[ridx] = INT_MAX;
continue;
}
int r = dofs[i];
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
row_idcs[ridx] = r;
mark[m] = 1;
++num_marked;
}
else
{
row_idcs[ridx] = INT_MAX;
}
}
for (int i = 0; i < lR.Width(); ++i, ++cidx)
{
col_idcs[cidx] = old_dofs[i];
}
}
if (!is_dg && !fespace->IsVariableOrder())
{
MFEM_VERIFY(num_marked * fespace->GetVDim() == Height(),
"internal error: not all rows were set.");
}
}
// if not using GPU, set max_rows/max_cols to zero
if (Device::Allows(Backend::DEVICE_MASK))
{
max_rows = std::min(max_rows, max_team_size);
}
else
{
max_rows = 1;
}
}
void DerefineMatrixOp::Mult(const Vector &x, Vector &y) const
{
const bool is_dg = fespace->FEColl()->GetContType()
== FiniteElementCollection::DISCONTINUOUS;
// DG needs atomic summation
MultKernel::Run(fespace->GetOrdering(), is_dg, *this, x, y);
}
DerefineMatrixOp::Kernels::Kernels()
{
MultKernel::Specialization<Ordering::byNODES, false>::Add();
MultKernel::Specialization<Ordering::byVDIM, false>::Add();
MultKernel::Specialization<Ordering::byNODES, true>::Add();
MultKernel::Specialization<Ordering::byVDIM, true>::Add();
}
template <Ordering::Type Order, bool Atomic>
DerefineMatrixOp::MultKernelType DerefineMatrixOp::MultKernel::Kernel()
{
return internal::DerefMultKernelImpl<Order, Atomic>;
}
DerefineMatrixOp::MultKernelType
DerefineMatrixOp::MultKernel::Fallback(Ordering::Type, bool)
{
MFEM_ABORT("invalid MultKernel parameters");
}
} // namespace mfem
/// \endcond DO_NOT_DOCUMENT
-65
View File
@@ -1,65 +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.
#ifndef MFEM_DEREFMAT_OP
#define MFEM_DEREFMAT_OP
#include "fespace.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
struct DerefineMatrixOp : public Operator
{
FiniteElementSpace *fespace;
/// offsets into block_storage
Array<int> block_offsets;
/// offsets into row_idcs
Array<int> block_row_idcs_offsets;
/// offsets into col_idcs
Array<int> block_col_idcs_offsets;
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
/// negative means the row data should be negated.
Array<int> row_idcs;
/// mapping for col dofs, negative means the col data should be negated.
Array<int> col_idcs;
/// dense block matrices which can be reused to construct the full matrix
/// operation. These are stored contiguously and blocks have no restrictions
/// on shape (can be rectangle and differ from block to block).
Vector block_storage;
/// maximum height of any block in block_storage for GPU
/// parallelization, or 1 for CPU runs.
int max_rows;
using MultKernelType = void (*)(const DerefineMatrixOp &, const Vector &,
Vector &);
/// template args: ordering, atomic
MFEM_REGISTER_KERNELS(MultKernel, MultKernelType, (Ordering::Type, bool));
struct Kernels
{
Kernels();
};
void Mult(const Vector &x, Vector &y) const;
DerefineMatrixOp(FiniteElementSpace &fespace_, int old_ndofs,
const Table *old_elem_dof, const Table *old_elem_fos);
};
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
-54
View File
@@ -1,54 +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.
#include "doperator.hpp"
#ifdef MFEM_USE_MPI
using namespace mfem;
using namespace mfem::future;
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
{
MFEM_ASSERT(parameters.size() == p.size(),
"number of parameters doesn't match descriptors");
for (size_t i = 0; i < parameters.size(); i++)
{
p[i]->Read();
parameters_l[i] = *p[i];
}
}
DifferentiableOperator::DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh) :
mesh(mesh),
solutions(solutions),
parameters(parameters)
{
fields.resize(solutions.size() + parameters.size());
fields_e.resize(fields.size());
solutions_l.resize(solutions.size());
parameters_l.resize(parameters.size());
for (size_t i = 0; i < solutions.size(); i++)
{
fields[i] = solutions[i];
}
for (size_t i = 0; i < parameters.size(); i++)
{
fields[i + solutions.size()] = parameters[i];
}
}
#endif // MFEM_USE_MPI
-829
View File
@@ -1,829 +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 <type_traits>
#include <utility>
#include "../../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "../fespace.hpp"
#include "util.hpp"
#include "interpolate.hpp"
#include "integrate.hpp"
#include "qfunction_apply.hpp"
namespace mfem::future
{
/// @brief Type alias for a function that computes the action of an operator
using action_t =
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, 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 sparse matrix of a
/// derivative operator
using assemble_derivative_hypreparmatrix_callback_t =
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
/// @brief Type alias for a function that applies the appropriate restriction to
/// the solution and parameters
using restriction_callback_t =
std::function<void(std::vector<Vector> &,
const std::vector<Vector> &,
std::vector<Vector> &)>;
/// Class representing the derivative (Jacobian) operator of a
/// DifferentiableOperator.
///
/// This class implements a derivative operator that computes directional
/// derivatives for a given set of solution and parameter fields. It supports
/// both forward and transpose operations, as well as assembly into sparse
/// matrices.
///
/// @note The derivative operator uses only forward mode differentiation in Mult
/// and MultTranspose. It does not support reverse mode differentiation. The
/// MultTranspose operation is achieved by using the transpose of the derivative
/// actions on each quadrature point.
///
/// @see DifferentiableOperator
class DerivativeOperator : public Operator
{
public:
/// Constructor for the DerivativeOperator class.
///
/// This is usually not called directly from a user. A DifferentiableOperator
/// calls this constructor when using
/// DifferentiableOperator::GetDerivative().
DerivativeOperator(
const int &height,
const int &width,
const std::vector<derivative_action_t> &derivative_actions,
const FieldDescriptor &direction,
const int &daction_l_size,
const std::vector<derivative_action_t> &derivative_actions_transpose,
const FieldDescriptor &transpose_direction,
const int &daction_transpose_l_size,
const std::vector<Vector *> &solutions_l,
const std::vector<Vector *> &parameters_l,
const restriction_callback_t &restriction_callback,
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
&assemble_derivative_hypreparmatrix_callbacks) :
Operator(height, width),
derivative_actions(derivative_actions),
direction(direction),
daction_l(daction_l_size),
daction_l_size(daction_l_size),
derivative_actions_transpose(derivative_actions_transpose),
transpose_direction(transpose_direction),
prolongation_transpose(prolongation_transpose),
assemble_derivative_hypreparmatrix_callbacks(
assemble_derivative_hypreparmatrix_callbacks)
{
std::vector<Vector> s_l(solutions_l.size());
for (size_t i = 0; i < s_l.size(); i++)
{
s_l[i] = *solutions_l[i];
}
std::vector<Vector> p_l(parameters_l.size());
for (size_t i = 0; i < p_l.size(); i++)
{
p_l[i] = *parameters_l[i];
}
fields_e.resize(solutions_l.size() + parameters_l.size());
restriction_callback(s_l, p_l, fields_e);
}
/// @brief Compute the action of the derivative operator on a given vector.
///
/// @param direction_t The direction vector in which to compute the
/// derivative. This has to be a T-dof vector.
/// @param result_t Result vector of the action of the derivative on
/// direction_t on T-dofs.
void Mult(const Vector &direction_t, Vector &result_t) const override
{
daction_l.SetSize(daction_l_size);
daction_l = 0.0;
prolongation(direction, direction_t, direction_l);
for (const auto &f : derivative_actions)
{
f(fields_e, direction_l, daction_l);
}
prolongation_transpose(daction_l, result_t);
};
/// @brief Compute the transpose of the derivative operator on a given
/// vector.
///
/// This function computes the transpose of the derivative operator on a
/// given vector by transposing the quadrature point local forward derivative
/// action. It does not use reverse mode automatic differentiation.
///
/// @param direction_t The direction vector in which to compute the
/// derivative. This has to be a T-dof vector.
/// @param result_t Result vector of the transpose action of the derivative on
/// direction_t on T-dofs.
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
{
MFEM_ASSERT(!derivative_actions_transpose.empty(),
"derivative can't be used to be multiplied in transpose mode");
daction_l.SetSize(width);
daction_l = 0.0;
prolongation(transpose_direction, direction_t, direction_l);
for (const auto &f : derivative_actions_transpose)
{
f(fields_e, direction_l, daction_l);
}
prolongation_transpose(daction_l, result_t);
};
/// @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)
{
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
"derivative can't be assembled into a matrix");
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
{
f(fields_e, A);
}
}
private:
/// Derivative action callbacks. Depending on the requested derivatives in
/// DifferentiableOperator the callbacks represent certain combinations of
/// actions of derivatives of the forward operator.
std::vector<derivative_action_t> derivative_actions;
FieldDescriptor direction;
mutable Vector daction_l;
const int daction_l_size;
/// Transpose Derivative action callbacks. Depending on the requested
/// derivatives in DifferentiableOperator the callbacks represent certain
/// combinations of actions of derivatives of the forward operator.
std::vector<derivative_action_t> derivative_actions_transpose;
FieldDescriptor transpose_direction;
mutable std::vector<Vector> fields_e;
mutable Vector direction_l;
std::function<void(Vector &, Vector &)> prolongation_transpose;
/// Callbacks that assemble derivatives into a HypreParMatrix.
std::vector<assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callbacks;
};
/// Class representing a differentiable operator which acts on solution and
/// parameter fields to compute residuals.
///
/// This class provides functionality to define differentiable operators by
/// composing functions that compute values at quadrature points. It supports
/// automatic differentiation to compute derivatives with respect to solutions
/// (Jacobians) and parameter fields (general derivative operators).
///
/// 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 f
/// those functionas and parameters at quadrature points.
///
/// Derivatives can be computed by obtaining a DerivativeOperator using
/// GetDerivative().
///
/// @see DerivativeOperator
class DifferentiableOperator : public Operator
{
public:
/// Constructor for the DifferentiableOperator class.
///
/// @param solutions The solution fields that the operator will act on.
/// @param parameters The parameter fields that define coefficients.
/// @param mesh The mesh on which the operator is defined.
DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh);
/// MultLevel enum to indicate if the T->L Operators are used in the
/// Mult method.
enum MultLevel
{
TVECTOR,
LVECTOR
};
/// @brief Set the MultLevel mode for the DifferentiableOperator.
/// The default is TVECTOR, which means that the Operator will use
/// T->L before Mult and L->T Operators after.
void SetMultLevel(MultLevel level)
{
mult_level = level;
}
/// @brief Compute the action of the operator on a given vector.
///
/// @param solutions_in The solution vector in which to compute the action.
/// This has to be a T-dof vector if MultLevel is set to TVECTOR, or L-dof
/// Vector if MultLevel is set to LVECTOR.
/// @param result_in Result vector of the action of the operator on
/// solutions. The result is a T-dof vector or L-dof vector depending on
/// the MultLevel.
void Mult(const Vector &solutions_in, Vector &result_in) const override
{
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
if (mult_level == MultLevel::LVECTOR)
{
get_lvectors(solutions, solutions_in, solutions_l);
result_in = 0.0;
for (auto &action : action_callbacks)
{
action(solutions_l, parameters_l, result_in);
}
}
else
{
prolongation(solutions, solutions_in, solutions_l);
residual_l = 0.0;
for (auto &action : action_callbacks)
{
action(solutions_l, parameters_l, residual_l);
}
prolongation_transpose(residual_l, result_in);
}
}
/// @brief Add a domain 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 domain_attributes Domain 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 AddDomainIntegrator(
qfunc_t &qfunc,
input_t inputs,
output_t outputs,
const IntegrationRule &integration_rule,
const Array<int> &domain_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().
///
/// @param p The parameters to be set. This should be a vector of pointers to
/// the parameter vectors. The vectors have to be L-vectors (e.g.
/// GridFunctions).
void SetParameters(std::vector<Vector *> p) const;
/// @brief Disable the use of tensor product structure.
///
/// This function disables the use of tensor product structure for the
/// operator. Usually, DifferentiableOperator creates callbacks based on
/// heuristics that achieve good performance for each element type. Some
/// functionality is not implemented for these performant algorithms but only
/// for generic assembly. Therefore the user can decide to use fallback
/// methods.
void DisableTensorProductStructure(bool disable = true)
{
use_tensor_product_structure = !disable;
}
/// @brief Get the derivative operator for a given derivative ID.
///
/// This function returns a shared pointer to a DerivativeOperator that
/// computes the derivative of the operator with respect to the given
/// derivative ID. The derivative ID is used to identify the specific
/// derivative action to be performed.
///
/// @param derivative_id The ID of the derivative to be computed.
/// @param sol_l The solution vectors to be used for the derivative
/// computation. This should be a vector of pointers to the solution
/// vectors. The vectors have to be L-vectors (e.g. GridFunctions).
/// @param par_l The parameter vectors to be used for the derivative
/// computation. This should be a vector of pointers to the parameter
/// vectors. The vectors have to be L-vectors (e.g. GridFunctions).
/// @return A shared pointer to the DerivativeOperator.
std::shared_ptr<DerivativeOperator> GetDerivative(
size_t derivative_id, std::vector<Vector *> sol_l, std::vector<Vector *> par_l)
{
MFEM_ASSERT(derivative_action_callbacks.find(derivative_id) !=
derivative_action_callbacks.end(),
"no derivative action has been found for ID " << derivative_id);
MFEM_ASSERT(sol_l.size() == solutions.size(),
"wrong number of solutions");
MFEM_ASSERT(par_l.size() == parameters.size(),
"wrong number of parameters");
const size_t derivative_idx = FindIdx(derivative_id, fields);
return std::make_shared<DerivativeOperator>(
height,
GetTrueVSize(fields[derivative_idx]),
derivative_action_callbacks[derivative_id],
fields[derivative_idx],
residual_l.Size(),
daction_transpose_callbacks[derivative_id],
fields[test_space_field_idx],
GetVSize(fields[test_space_field_idx]),
sol_l,
par_l,
restriction_callback,
prolongation_transpose,
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
}
private:
const ParMesh &mesh;
MultLevel mult_level = TVECTOR;
std::vector<action_t> action_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_hypreparmatrix_callback_t>>
assemble_derivative_hypreparmatrix_callbacks;
std::vector<FieldDescriptor> solutions;
std::vector<FieldDescriptor> parameters;
// solutions and parameters
std::vector<FieldDescriptor> fields;
mutable std::vector<Vector> solutions_l;
mutable std::vector<Vector> parameters_l;
mutable Vector residual_l;
mutable std::vector<Vector> fields_e;
mutable Vector residual_e;
std::function<void(Vector &, Vector &)> prolongation_transpose;
std::function<void(Vector &, Vector &)> output_restriction_transpose;
restriction_callback_t restriction_callback;
std::map<size_t, size_t> assembled_vector_sizes;
bool use_tensor_product_structure = true;
size_t test_space_field_idx = SIZE_MAX;
};
template <
typename qfunc_t,
typename input_t,
typename output_t,
typename derivative_ids_t>
void DifferentiableOperator::AddDomainIntegrator(
qfunc_t &qfunc,
input_t inputs,
output_t outputs,
const IntegrationRule &integration_rule,
const Array<int> &domain_attributes,
derivative_ids_t derivative_ids)
{
using entity_t = Entity::Element;
static constexpr size_t num_inputs =
tuple_size<decltype(inputs)>::value;
static constexpr size_t num_outputs =
tuple_size<decltype(outputs)>::value;
using qf_signature =
typename create_function_signature<decltype(&qfunc_t::operator())>::type;
using qf_param_ts = typename qf_signature::parameter_ts;
using qf_output_t = typename qf_signature::return_t;
// Consistency checks
if constexpr (num_outputs > 1)
{
static_assert(dfem::always_false<qfunc_t>,
"more than one output per quadrature functions is not supported right now");
}
if constexpr (std::is_same_v<qf_output_t, void>)
{
static_assert(dfem::always_false<qfunc_t>,
"quadrature function has no return value");
}
constexpr size_t num_qfinputs = tuple_size<qf_param_ts>::value;
static_assert(num_qfinputs == num_inputs,
"quadrature function inputs and descriptor inputs have to match");
constexpr size_t num_qf_outputs = tuple_size<qf_output_t>::value;
static_assert(num_qf_outputs == num_outputs,
"quadrature function outputs and descriptor outputs have to match");
constexpr auto inout_tuple =
merge_mfem_tuples_as_empty_std_tuple(inputs, outputs);
constexpr auto filtered_inout_tuple = filter_fields(inout_tuple);
static constexpr size_t num_fields =
count_unique_field_ids(filtered_inout_tuple);
MFEM_ASSERT(num_fields == solutions.size() + parameters.size(),
"Total number of fields doesn't match sum of solutions and parameters."
" This indicates that some fields are not used in the integrator,"
" which currently is not supported.");
auto dependency_map = make_dependency_map(inputs);
// pretty_print(dependency_map);
auto input_to_field =
create_descriptors_to_fields_map<entity_t>(fields, inputs);
auto output_to_field =
create_descriptors_to_fields_map<entity_t>(fields, outputs);
// TODO: factor out
std::vector<int> inputs_vdim(num_inputs);
for_constexpr<num_inputs>([&](auto i)
{
inputs_vdim[i] = get<i>(inputs).vdim;
});
Array<int> elem_attributes;
elem_attributes.SetSize(mesh.GetNE());
for (int i = 0; i < mesh.GetNE(); ++i)
{
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;
auto 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;
}
ElementDofOrdering element_dof_ordering = ElementDofOrdering::NATIVE;
DofToQuad::Mode doftoquad_mode = DofToQuad::Mode::FULL;
if (use_sum_factorization)
{
element_dof_ordering = ElementDofOrdering::LEXICOGRAPHIC;
doftoquad_mode = DofToQuad::Mode::TENSOR;
}
auto [output_rt,
output_e_sz] = get_restriction_transpose<entity_t>
(fields[test_space_field_idx],
element_dof_ordering, output_fop);
auto &output_e_size = output_e_sz;
output_restriction_transpose = output_rt;
residual_e.SetSize(output_e_size);
// The explicit captures are necessary to avoid dependency on
// the specific instance of this class (this pointer).
restriction_callback =
[=, solutions = this->solutions, parameters = this->parameters]
(std::vector<Vector> &sol,
const std::vector<Vector> &par,
std::vector<Vector> &f)
{
restriction<entity_t>(solutions, sol, f,
element_dof_ordering);
restriction<entity_t>(parameters, par, f,
element_dof_ordering,
solutions.size());
};
prolongation_transpose = get_prolongation_transpose(
fields[test_space_field_idx], output_fop, mesh.GetComm());
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();
if constexpr (is_sum_fop<decltype(output_fop)>::value)
{
residual_l.SetSize(1);
height = 1;
}
else
{
const int residual_lsize = GetVSize(fields[test_space_field_idx]);
residual_l.SetSize(residual_lsize);
height = GetTrueVSize(fields[test_space_field_idx]);
}
// TODO: Is this a hack?
width = GetTrueVSize(fields[0]);
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(std::pow(num_qp, 1.0/dimension) + 0.5);
const int residual_size_on_qp =
GetSizeOnQP<entity_t>(output_fop,
fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
const int test_vdim = output_fop.vdim;
const int test_op_dim = output_fop.size_on_qp / output_fop.vdim;
const int num_test_dof =
num_entities ? (output_e_size / output_fop.vdim / num_entities) : 0;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp =
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
auto action_shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
Vector shmem_cache(action_shmem_info.total_size);
// print_shared_memory_info(action_shmem_info);
ThreadBlocks thread_blocks;
if (dimension == 3)
{
if (use_sum_factorization)
{
thread_blocks.x = q1d;
thread_blocks.y = q1d;
thread_blocks.z = q1d;
}
}
else if (dimension == 2)
{
if (use_sum_factorization)
{
thread_blocks.x = q1d;
thread_blocks.y = q1d;
thread_blocks.z = 1;
}
}
action_callbacks.push_back(
// Explicitly capture everything we need, so we can make explicit choice
// how to capture every variable, by copy or by ref.
[
// capture by copy:
dimension, // int
num_entities, // int
num_test_dof, // int
num_qp, // int
q1d, // int
residual_size_on_qp, // int
test_vdim, // int (= output_fop.vdim)
test_op_dim, // int (derived from output_fop)
inputs, // mfem::future::tuple
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)
action_shmem_info, // SharedMemoryInfo
// TODO: make this Array<int> a member of the DifferentiableOperator
// and capture it by ref.
elem_attributes, // Array<int>
// capture by ref:
&restriction_cb = this->restriction_callback,
&fields_e = this->fields_e,
&residual_e = this->residual_e,
&output_restriction_transpose = this->output_restriction_transpose
]
(std::vector<Vector> &sol, const std::vector<Vector> &par, Vector &res)
mutable // mutable: needed to modify 'shmem_cache'
{
restriction_cb(sol, par, fields_e);
residual_e = 0.0;
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(fields_e,
action_shmem_info.field_sizes,
num_entities);
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_domain_attr[d_elem_attr[e] - 1]) { return; }
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
residual_shmem, scratch_shmem] =
unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
wrapped_fields_e, num_qp, e);
map_fields_to_quadrature_data(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
scratch_shmem, dimension, use_sum_factorization);
call_qfunction<qf_param_ts>(
qfunc, input_shmem, residual_shmem,
residual_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 y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(
y, fhat, output_fop, output_dtq_shmem[0],
scratch_shmem, dimension, use_sum_factorization);
}, num_entities, thread_blocks, action_shmem_info.total_size, shmem_cache.ReadWrite());
output_restriction_transpose(residual_e, res);
});
// Without this compile-time check, some valid instantiations of this method
// will fail.
if constexpr (derivative_ids_t::size() != 0)
{
// Create the action of the derivatives
for_constexpr([&, &or_transpose =
this->output_restriction_transpose](const std::size_t derivative_id)
{
const size_t d_field_idx = FindIdx(derivative_id, fields);
const auto direction = fields[d_field_idx];
const int da_size_on_qp =
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
auto shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>(
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
num_qp, input_size_on_qp, residual_size_on_qp,
element_dof_ordering, d_field_idx);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
Vector direction_e;
Vector derivative_action_e(output_e_size);
derivative_action_e = 0.0;
// Lookup the derivative_id key in the dependency map
auto it = dependency_map.find(derivative_id);
if (it == dependency_map.end())
{
MFEM_ABORT("Derivative ID not found in dependency map");
}
const auto input_is_dependent = it->second;
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
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)
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
da_size_on_qp, // int
// capture by 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);
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; }
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_fields_to_quadrature_data(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field,
inputs, ir_weights, scratch_shmem, dimension,
use_sum_factorization);
// TODO: Probably redundant
set_zero(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 y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(
y, fhat, output_fop, output_dtq_shmem[0],
scratch_shmem, dimension, use_sum_factorization);
}, num_entities, thread_blocks, shmem_info.total_size,
shmem_cache.ReadWrite());
or_transpose(derivative_action_e, der_action_l);
});
}, derivative_ids);
}
}
} // namespace mfem::future
#endif
-144
View File
@@ -1,144 +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 <type_traits>
namespace mfem::future
{
/// @brief Base class for FieldOperators.
///
/// This class serves as a base for different FieldOperator types which can be
/// applied to fields that are used with inputs to a quadrature point function.
/// See DifferentialOperator.
template <int FIELD_ID = -1>
class FieldOperator
{
public:
/// @brief Constructor for the FieldOperator.
///
/// This constructor initializes the FieldOperator with it's size on
/// quadrature points. The size on quadrature points has to be determined by
/// the FieldOperator type, the dimension and the vector dimension (number
/// of components). See the following examples
///
/// Scalar FiniteElementSpace with Value FieldOperator:
/// size = vdim x dim x 1 = 1 x dim x 1 = dim
///
/// Vector FiniteElementSpace with Gradient FieldOperator:
/// size = vdim x dim x dim = vdim x dim x dim = vdim * dim^2
///
/// ParameterSpace with Identity FieldOperator:
/// size = vdim = vdim
constexpr FieldOperator(int size_on_qp = 0) :
size_on_qp(size_on_qp) {};
/// @brief Get the field id this FieldOperator is attached to.
static constexpr int GetFieldId() { return FIELD_ID; }
/// @brief Get the size on quadrature point for this FieldOperator.
int size_on_qp = -1;
/// @brief Get the dimension of the FieldOperator.
int dim = -1;
/// @brief Get the vector dimension (number of components)
/// of the FieldOperator.
int vdim = -1;
};
/// @brief Identity FieldOperator.
///
/// This FieldOperator does nothing to the field. The field (usually a
/// ParametricFunction) transfers the values to the quadrature point data and
/// Identity can be viewed as an identity operation.
template <int FIELD_ID = -1>
class Identity : public FieldOperator<FIELD_ID>
{
public:
constexpr Identity() : FieldOperator<FIELD_ID>() {}
};
template< typename T >
struct is_identity_fop : std::false_type {};
template <int FIELD_ID>
struct is_identity_fop<Identity<FIELD_ID>> : std::true_type {};
/// @brief Weight FieldOperator.
///
/// This FieldOperator is used to signal that this field contains the quadrature
/// point weights.
class Weight : public FieldOperator<-1>
{
public:
constexpr Weight() : FieldOperator<-1>() {};
};
template< typename T >
struct is_weight_fop : std::false_type {};
template <>
struct is_weight_fop<Weight> : std::true_type {};
/// @brief Value FieldOperator.
///
/// This FieldOperator is used to signal that the field contains the
/// interpolated values of the degrees of freedom at the quadrature points.
template <int FIELD_ID = -1>
class Value : public FieldOperator<FIELD_ID>
{
public:
constexpr Value() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_value_fop : std::false_type {};
template <int FIELD_ID>
struct is_value_fop<Value<FIELD_ID>> : std::true_type {};
/// @brief Gradient FieldOperator.
///
/// This FieldOperator is used to signal that the field contains the
/// interpolated gradients of the degrees of freedom at the quadrature points.
template <int FIELD_ID = -1>
class Gradient : public FieldOperator<FIELD_ID>
{
public:
constexpr Gradient() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_gradient_fop : std::false_type {};
template <int FIELD_ID>
struct is_gradient_fop<Gradient<FIELD_ID>> : std::true_type {};
/// @brief Sum FieldOperator.
///
/// This FieldOperator is commonly used to signal that an output of a quadrature
/// function should be summed.
template <int FIELD_ID = -1>
class Sum : public FieldOperator<FIELD_ID>
{
public:
constexpr Sum() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_sum_fop : std::false_type {};
template <int FIELD_ID>
struct is_sum_fop<Sum<FIELD_ID>> : std::true_type {};
} // namespace mfem::future
-450
View File
@@ -1,450 +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
{
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_impl(
DeviceTensor<2, real_t> &y,
const DeviceTensor<3, real_t> &f,
const output_t &output,
const DofToQuadMap &dtq)
{
[[maybe_unused]] auto B = dtq.B;
[[maybe_unused]] auto G = dtq.G;
// assuming the quadrature point residual has to "play nice with
// the test function"
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
{
const auto [num_qp, cdim, num_dof] = B.GetShape();
const int vdim = output.vdim > 0 ? output.vdim : cdim ;
for (int dof = 0; dof < num_dof; dof++)
{
for (int vd = 0; vd < vdim; vd++)
{
real_t acc = 0.0;
for (int qp = 0; qp < num_qp; qp++)
{
acc += B(qp, 0, dof) * f(vd, 0, qp);
}
y(dof, vd) += acc;
}
}
}
else if constexpr (
is_gradient_fop<std::decay_t<output_t>>::value)
{
const auto [num_qp, dim, num_dof] = G.GetShape();
const int vdim = output.vdim;
for (int dof = 0; dof < num_dof; dof++)
{
for (int vd = 0; vd < vdim; vd++)
{
real_t acc = 0.0;
for (int d = 0; d < dim; d++)
{
for (int qp = 0; qp < num_qp; qp++)
{
acc += G(qp, d, dof) * f(vd, d, qp);
}
}
y(dof, vd) += acc;
}
}
}
else if constexpr (is_sum_fop<std::decay_t<output_t>>::value)
{
// This is the "integral over all quadrature points type" applying
// B = 1 s.t. B^T * C \in R^1.
const auto [num_qp, unused, unused1] = B.GetShape();
auto cc = Reshape(&f(0, 0, 0), num_qp);
for (int i = 0; i < num_qp; i++)
{
y(0, 0) += cc(i);
}
}
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
{
const auto [num_qp, unused, num_dof] = B.GetShape();
const auto vdim = output.vdim;
auto cc = Reshape(&f(0, 0, 0), num_qp * vdim);
auto yy = Reshape(&y(0, 0), num_qp * vdim);
for (int i = 0; i < num_qp * vdim; i++)
{
yy(i) = cc(i);
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_tensor_impl_2d(
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, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t acc = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
acc += fqp(vd, 0, qx, qy) * B(qx, 0, dx);
}
s0(qy, dx) = acc;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t acc = 0.0;
for (int qy = 0; qy < q1d; qy++)
{
acc += s0(qy, dx) * B(qy, 0, dy);
}
yd(dx, dy, vd) += acc;
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = G.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uv[2] = {0.0, 0.0};
for (int qx = 0; qx < q1d; qx++)
{
uv[0] += fqp(vd, 0, qx, qy) * G(qx, 0, dx);
uv[1] += fqp(vd, 1, qx, qy) * B(qx, 0, dx);
}
s0(qy, dx) = uv[0];
s1(qy, dx) = uv[1];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uv[2] = {0.0, 0.0};
for (int qy = 0; qy < q1d; qy++)
{
uv[0] += s0(qy, dx) * B(qy, 0, dy);
uv[1] += s1(qy, dx) * G(qy, 0, dy);
}
yd(dx, dy, vd) += uv[0] + uv[1];
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
// // TODO: Check if this is the right fix for all cases
// 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;
// }
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d);
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d);
for (int sq = 0; sq < output.size_on_qp; sq++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
yqp(sq, qx, qy) = fqp(sq, qx, qy);
}
}
MFEM_SYNC_THREAD;
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_tensor_impl_3d(
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, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
real_t acc = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
}
s0(qz, qy, dx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
real_t acc = 0.0;
for (int qy = 0; qy < q1d; qy++)
{
acc += s0(qz, qy, dx) * B(qy, 0, dy);
}
s1(qz, dy, dx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
real_t acc = 0.0;
for (int qz = 0; qz < q1d; qz++)
{
acc += s1(qz, dy, dx) * B(qz, 0, dz);
}
yd(dx, dy, dz, vd) += acc;
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = G.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, q1d, d1d);
auto s2 = Reshape(&scratch_mem[2](0), q1d, q1d, d1d);
auto s3 = Reshape(&scratch_mem[3](0), q1d, d1d, d1d);
auto s4 = Reshape(&scratch_mem[4](0), q1d, d1d, d1d);
auto s5 = Reshape(&scratch_mem[5](0), q1d, d1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qx = 0; qx < q1d; qx++)
{
uvw[0] += fqp(vd, 0, qx, qy, qz) * G(qx, 0, dx);
uvw[1] += fqp(vd, 1, qx, qy, qz) * B(qx, 0, dx);
uvw[2] += fqp(vd, 2, qx, qy, qz) * B(qx, 0, dx);
}
s0(qz, qy, dx) = uvw[0];
s1(qz, qy, dx) = uvw[1];
s2(qz, qy, dx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qy = 0; qy < q1d; qy++)
{
uvw[0] += s0(qz, qy, dx) * B(qy, 0, dy);
uvw[1] += s1(qz, qy, dx) * G(qy, 0, dy);
uvw[2] += s2(qz, qy, dx) * B(qy, 0, dy);
}
s3(qz, dy, dx) = uvw[0];
s4(qz, dy, dx) = uvw[1];
s5(qz, dy, dx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qz = 0; qz < q1d; qz++)
{
uvw[0] += s3(qz, dy, dx) * B(qz, 0, dz);
uvw[1] += s4(qz, dy, dx) * B(qz, 0, dz);
uvw[2] += s5(qz, dy, dx) * G(qz, 0, dz);
}
yd(dx, dy, dz, vd) += uvw[0] + uvw[1] + uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_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, q1d, q1d);
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d, q1d);
for (int sq = 0; sq < output.size_on_qp; sq++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
yqp(sq, qx, qy, qz) = fqp(sq, qx, qy, qz);
}
}
}
MFEM_SYNC_THREAD;
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields(
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,
const int &dimension,
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
if (dimension == 2)
{
map_quadrature_data_to_fields_tensor_impl_2d(y, f, output, dtq, scratch_mem);
}
else if (dimension == 3)
{
map_quadrature_data_to_fields_tensor_impl_3d(y, f, output, dtq, scratch_mem);
}
else { MFEM_ABORT_KERNEL("dimension not supported"); }
}
else
{
map_quadrature_data_to_fields_impl(y, f, output, dtq);
}
}
} // namespace mfem::future
-573
View File
@@ -1,573 +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
{
template <typename field_operator_t>
MFEM_HOST_DEVICE inline
void map_field_to_quadrature_data_tensor_product_3d(
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, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t acc = 0.0;
for (int dx = 0; dx < d1d; dx++)
{
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
}
s0(dz, dy, qx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
real_t acc = 0.0;
for (int dy = 0; dy < d1d; dy++)
{
acc += s0(dz, dy, qx) * B(qy, 0, dy);
}
s1(dz, qy, qx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t acc = 0.0;
for (int dz = 0; dz < d1d; dz++)
{
acc += s1(dz, qy, qx) * B(qz, 0, dz);
}
fqp(vd, qx, qy, qz) = acc;
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (
is_gradient_fop<std::decay_t<field_operator_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const int dim = input.dim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, d1d, q1d);
auto s2 = Reshape(&scratch_mem[2](0), d1d, q1d, q1d);
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uv[2] = {0.0, 0.0};
for (int dx = 0; dx < d1d; dx++)
{
const real_t f = field(dx, dy, dz, vd);
uv[0] += f * B(qx, 0, dx);
uv[1] += f * G(qx, 0, dx);
}
s0(dz, dy, qx) = uv[0];
s1(dz, dy, qx) = uv[1];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int dy = 0; dy < d1d; dy++)
{
const real_t s0i = s0(dz, dy, qx);
uvw[0] += s1(dz, dy, qx) * B(qy, 0, dy);
uvw[1] += s0i * G(qy, 0, dy);
uvw[2] += s0i * B(qy, 0, dy);
}
s2(dz, qy, qx) = uvw[0];
s3(dz, qy, qx) = uvw[1];
s4(dz, qy, qx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int dz = 0; dz < d1d; dz++)
{
uvw[0] += s2(dz, qy, qx) * B(qz, 0, dz);
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
}
fqp(vd, 0, qx, qy, qz) = uvw[0];
fqp(vd, 1, qx, qy, qz) = uvw[1];
fqp(vd, 2, qx, qy, qz) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
}
// TODO: Create separate function for clarity
else if constexpr (
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
{
const int num_qp = integration_weights.GetShape()[0];
// TODO: eeek
const int q1d = (int)floor(std::pow(num_qp, 1.0/input.dim) + 0.5);
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
f(qx, qy, qz) = w(qx, qy, qz);
}
}
}
MFEM_SYNC_THREAD;
}
else if constexpr (is_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 * q1d * 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 inline
void map_field_to_quadrature_data_tensor_product_2d(
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, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
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, dy, vd);
}
s0(dy, qx) = acc;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
real_t acc = 0.0;
for (int dy = 0; dy < d1d; dy++)
{
acc += s0(dy, qx) * B(qy, 0, dy);
}
fqp(vd, qx, qy) = acc;
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (
is_gradient_fop<std::decay_t<field_operator_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const int dim = input.dim;
const auto field = Reshape(&field_e[0], d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uv[2] = {0.0, 0.0};
for (int dx = 0; dx < d1d; dx++)
{
const real_t f = field(dx, dy, vd);
uv[0] += f * B(qx, 0, dx);
uv[1] += f * G(qx, 0, dx);
}
s0(dy, qx) = uv[0];
s1(dy, qx) = uv[1];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uv[2] = {0.0, 0.0};
for (int dy = 0; dy < d1d; dy++)
{
const real_t s0i = s0(dy, qx);
uv[0] += s1(dy, qx) * B(qy, 0, dy);
uv[1] += s0i * G(qy, 0, dy);
}
fqp(vd, 0, qx, qy) = uv[0];
fqp(vd, 1, qx, qy) = uv[1];
}
}
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, q1d);
auto f = Reshape(&field_qp[0], q1d, q1d);
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
f(qx, qy) = w(qx, qy);
}
}
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 * 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(
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)
{
[[maybe_unused]] auto B = dtq.B;
[[maybe_unused]] auto G = dtq.G;
if constexpr (is_value_fop<field_operator_t>::value)
{
auto [num_qp, dim, num_dof] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e(0), num_dof, vdim);
for (int vd = 0; vd < vdim; vd++)
{
for (int qp = 0; qp < num_qp; qp++)
{
real_t acc = 0.0;
for (int dof = 0; dof < num_dof; dof++)
{
acc += B(qp, 0, dof) * field(dof, vd);
}
field_qp(vd, qp) = acc;
}
}
}
else if constexpr (is_gradient_fop<field_operator_t>::value)
{
const auto [num_qp, dim, num_dof] = G.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e(0), num_dof, vdim);
auto f = Reshape(&field_qp[0], vdim, dim, num_qp);
for (int vd = 0; vd < vdim; vd++)
{
for (int qp = 0; qp < num_qp; qp++)
{
for (int d = 0; d < dim; d++)
{
real_t acc = 0.0;
for (int dof = 0; dof < num_dof; dof++)
{
acc += G(qp, d, dof) * field(dof, vd);
}
f(vd, d, qp) = acc;
}
}
}
}
else if constexpr (std::is_same_v<field_operator_t, Weight>)
{
const int num_qp = integration_weights.GetShape()[0];
auto f = Reshape(&field_qp[0], num_qp);
for (int qp = 0; qp < num_qp; qp++)
{
f(qp) = integration_weights(qp);
}
}
else if constexpr (is_identity_fop<field_operator_t>::value)
{
auto [num_qp, unused, num_dof] = B.GetShape();
const int size_on_qp = input.size_on_qp;
const auto field = Reshape(&field_e[0], size_on_qp * num_qp);
auto f = Reshape(&field_qp[0], size_on_qp * num_qp);
for (int i = 0; i < size_on_qp * num_qp; i++)
{
f(i) = field(i);
}
}
else
{
static_assert(dfem::always_false<field_operator_t>,
"can't map field to quadrature data");
}
}
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
MFEM_HOST_DEVICE inline
void map_fields_to_quadrature_data(
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
const std::array<DeviceTensor<1>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
const std::array<int, num_inputs> &input_to_field,
const field_operator_ts &fops,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const int &dimension,
const bool &use_sum_factorization = false)
{
// When the input_to_field map returns -1, this means the requested input
// is the integration weight. Weights don't have a user defined field
// attached to them and we create a dummy field which is not accessed
// inside the functions it is passed to.
const auto dummy_field_weight = DeviceTensor<1>(nullptr, 0);
for_constexpr<num_inputs>([&](auto i)
{
const DeviceTensor<1> &field_e =
(input_to_field[i] == -1) ? dummy_field_weight : fields_e[input_to_field[i]];
if (use_sum_factorization)
{
if (dimension == 2)
{
map_field_to_quadrature_data_tensor_product_2d(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
integration_weights, scratch_mem);
}
else if (dimension == 3)
{
map_field_to_quadrature_data_tensor_product_3d(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
integration_weights, scratch_mem);
}
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("unsupported dimension");
#endif
}
}
else
{
map_field_to_quadrature_data(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
integration_weights);
}
});
}
template <typename field_operator_t>
MFEM_HOST_DEVICE
void map_field_to_quadrature_data_conditional(
DeviceTensor<2> &field_qp,
const DeviceTensor<1> &field_e,
const DofToQuadMap &dtqmap,
field_operator_t &fop,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const bool &condition,
const int &dimension,
const bool &use_sum_factorization = false)
{
if (condition)
{
if (use_sum_factorization)
{
if (dimension == 2)
{
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_2d(
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
}
}
else
{
map_field_to_quadrature_data(
field_qp, dtqmap, field_e, fop, integration_weights);
}
}
}
template <size_t num_fields, size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_fields_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
const std::array<DeviceTensor<1, const real_t>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_inputs> &conditions,
const bool &use_sum_factorization = false)
{
for_constexpr<num_inputs>([&](auto i)
{
map_field_to_quadrature_data_conditional(
fields_qp[i], fields_e[i], dtqmaps[i], get<i>(fops), integration_weights,
scratch_mem, conditions[i], use_sum_factorization);
});
}
template <size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_direction_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
const DeviceTensor<1> &direction_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_inputs> &conditions,
const int &dimension,
const bool &use_sum_factorization = false)
{
for_constexpr<num_inputs>([&](auto i)
{
if (conditions[i])
{
if (use_sum_factorization)
{
if (dimension == 2)
{
map_field_to_quadrature_data_tensor_product_2d(
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
integration_weights, scratch_mem);
}
else if (dimension == 3)
{
map_field_to_quadrature_data_tensor_product_3d(
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
integration_weights, scratch_mem);
}
}
else
{
map_field_to_quadrature_data(
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
integration_weights);
}
}
});
}
}
-154
View File
@@ -1,154 +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 "../fe/fe_base.hpp"
#include "../../fem/fespace.hpp"
namespace mfem::future
{
/// Base class for parametric spaces
class ParameterSpace
{
public:
ParameterSpace(int vdim = 1) : vdim(vdim) {}
/// @brief Get vector dimension at each point
///
/// This is the number of components at each point in the parametric space.
int GetVDim() const { return vdim; }
/// Get DofToQuad information
const DofToQuad& GetDofToQuad() const { return dtq; }
/// Get total size of the space (T-vector size)
///
/// returns the true size vsize of the space
virtual int GetTrueVSize() const = 0;
/// Get local vector size (L-vector size)
///
/// returns the local size of the space
virtual int GetVSize() const = 0;
/// Get spatial dimension
///
/// returns always 1.
int Dimension() const
{
return 1;
}
/// @brief Get T-vector to L-vector transformation
///
/// returns identity by default that is lazy evaluated.
virtual const Operator* GetProlongationMatrix() const
{
if (!prolongation)
{
prolongation.reset(new IdentityOperator(GetTrueVSize()));
}
return prolongation.get();
}
/// @brief Get L-vector to E-vector transformation
/// @note This is a mock call to replicate interface of FiniteElementSpace.
/// It should not be used by a user.
///
/// returns identity by default that is lazy evaluated.
virtual const Operator* GetElementRestriction(ElementDofOrdering o) const
{
if (!elem_restr)
{
elem_restr.reset(new IdentityOperator(GetVSize()));
}
return elem_restr.get();
}
protected:
int vdim;
DofToQuad dtq;
mutable std::unique_ptr<Operator> prolongation;
mutable std::unique_ptr<Operator> elem_restr;
};
/// @brief Uniform parameter space
class UniformParameterSpace : public ParameterSpace
{
public:
/// @brief Constructor for a uniform parameter space
///
/// @param mesh The mesh to determine dimension and number of elements.
/// @param ir The integration rule to determine the number of quadrature points.
/// @param vdim The vector dimension at each point.
/// @param used_in_tensor_product If true, the number of quadrature points is
/// calculated as the nth root of the number of points in the integration rule,
/// where n is the mesh dimension. If false, the number of quadrature points is
/// taken directly from the integration rule.
UniformParameterSpace(Mesh &mesh, const IntegrationRule &ir, int vdim,
bool used_in_tensor_product = true) :
ParameterSpace(vdim)
{
// Setup DofToQuad information
dtq.nqpt = (int)floor(std::pow(ir.GetNPoints(), 1.0 / mesh.Dimension()) + 0.5);
dtq.ndof = dtq.nqpt;
dtq.mode = used_in_tensor_product ? DofToQuad::TENSOR : DofToQuad::FULL;
// Calculate sizes
const int num_qp = used_in_tensor_product ?
static_cast<int>(std::pow(dtq.nqpt, mesh.Dimension())) :
ir.GetNPoints();
tsize = vdim * num_qp * mesh.GetNE();
lsize = tsize;
}
int GetTrueVSize() const override
{
return tsize;
}
int GetVSize() const override
{
return lsize;
}
private:
/// T-vector size
int tsize;
/// L-vector size
int lsize;
};
class ParameterFunction : public Vector
{
public:
ParameterFunction(ParameterSpace &space) :
Vector(space.GetTrueVSize()),
space(space)
{}
/// @brief Get the ParameterSpace
const ParameterSpace& GetParameterSpace() const
{
return space;
}
using Vector::operator=;
private:
/// the parametric space
ParameterSpace &space;
};
} // namespace mfem::future
-298
View File
@@ -1,298 +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"
#include "qfunction_transform.hpp"
namespace mfem::future
{
/// @brief Call a qfunction with the given parameters.
///
/// @param qfunc the qfunction to call.
/// @param input_shmem the input shared memory.
/// @param residual_shmem the residual shared memory.
/// @param rs_qp the size of the residual.
/// @param num_qp the number of quadrature points.
/// @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(
qfunc_t &qfunc,
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
DeviceTensor<2> &residual_shmem,
const int &rs_qp,
const int &num_qp,
const int &q1d,
const int &dimension,
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
if (dimension == 2)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const int q = qx + q1d * qy;
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 == 3)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto qf_args = decay_tuple<qf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), rs_qp);
apply_kernel(r, qfunc, qf_args, input_shmem, q);
}
}
}
}
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("unsupported dimension for sum factorization");
#endif
}
MFEM_SYNC_THREAD;
}
else
{
MFEM_FOREACH_THREAD(q, x, num_qp)
{
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);
}
}
}
/// @brief Call a qfunction with the given parameters and
/// compute it's derivative action.
///
/// @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 das_qp the size of the derivative action.
/// @param num_qp the number of quadrature points.
/// @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_action(
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,
const int &das_qp,
const int &num_qp,
const int &q1d,
const int &dimension,
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
if (dimension == 2)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const int q = qx + q1d * qy;
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 == 3)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto r = Reshape(&residual_shmem(0, q), 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
}
}
}
}
MFEM_SYNC_THREAD;
}
else
{
MFEM_FOREACH_THREAD(q, x, num_qp)
{
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
}
MFEM_SYNC_THREAD;
}
}
template <typename qfunc_t, typename args_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel(
DeviceTensor<1, real_t> &f_qp,
const qfunc_t &qfunc,
args_ts &args,
const std::array<DeviceTensor<2>, num_args> &u,
int qp)
{
process_qf_args(u, args, qp);
process_qf_result(f_qp, get<0>(apply(qfunc, args)));
}
template <typename qfunc_t, typename arg_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel_native_dual(
DeviceTensor<1, real_t> &f_qp,
const qfunc_t &qfunc,
arg_ts &args,
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
const int &qp_idx)
{
process_qf_args(u, v, args, qp_idx);
auto r = get<0>(apply(qfunc, args));
process_derivative_from_native_dual(f_qp, r);
}
#ifdef MFEM_USE_ENZYME
template <typename func_t, typename... arg_ts>
MFEM_HOST_DEVICE inline
auto qfunction_wrapper(const func_t &f, arg_ts &&...args)
{
return f(args...);
}
// Version for active function arguments only
//
// This is an Enzyme regression and can be removed in later versions.
template <typename qfunc_t, typename arg_ts, std::size_t... Is,
typename inactive_arg_ts>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme_indexed(qfunc_t &qfunc, arg_ts &&args,
arg_ts &&shadow_args,
std::index_sequence<Is...>,
inactive_arg_ts &&inactive_args,
std::index_sequence<>)
{
using qf_return_t = typename create_function_signature<
decltype(&qfunc_t::operator())>::type::return_t;
return __enzyme_fwddiff<qf_return_t>(
qfunction_wrapper<qfunc_t, decltype(get<Is>(args))...>, enzyme_const,
(void *)&qfunc, enzyme_dup, &get<Is>(args)..., enzyme_interleave,
&get<Is>(shadow_args)...);
}
// Interleave function arguments for enzyme
template <typename qfunc_t, typename arg_ts, std::size_t... Is,
typename inactive_arg_ts, std::size_t... Js>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme_indexed(qfunc_t &qfunc, arg_ts &&args,
arg_ts &&shadow_args,
std::index_sequence<Is...>,
inactive_arg_ts &&inactive_args,
std::index_sequence<Js...>)
{
using qf_return_t = typename create_function_signature<
decltype(&qfunc_t::operator())>::type::return_t;
return __enzyme_fwddiff<qf_return_t>(
qfunction_wrapper<qfunc_t, decltype(get<Is>(args))...,
decltype(get<Js>(inactive_args))...>,
enzyme_const, (void *)&qfunc, enzyme_dup, &get<Is>(args)...,
enzyme_const, &get<Js>(inactive_args)..., enzyme_interleave,
&get<Is>(shadow_args)...);
}
template <typename qfunc_t, typename arg_ts, typename inactive_arg_ts>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme(qfunc_t &qfunc, arg_ts &&args,
arg_ts &&shadow_args,
inactive_arg_ts &&inactive_args)
{
auto arg_indices = std::make_index_sequence<
tuple_size<std::remove_reference_t<arg_ts>>::value> {};
auto inactive_arg_indices = std::make_index_sequence<
tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
return fwddiff_apply_enzyme_indexed(qfunc, args, shadow_args, arg_indices,
inactive_args, inactive_arg_indices);
}
template <typename qfunc_t, typename arg_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel_fwddiff_enzyme(
DeviceTensor<1, real_t> &f_qp,
qfunc_t &qfunc,
arg_ts &args,
arg_ts &shadow_args,
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
int qp_idx)
{
process_qf_args(u, args, qp_idx);
process_qf_args(v, shadow_args, qp_idx);
process_qf_result(f_qp,
get<0>(fwddiff_apply_enzyme(qfunc, args, shadow_args, tuple<> {})));
}
#endif // MFEM_USE_ENZYME
} // namespace mfem::future
-338
View File
@@ -1,338 +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"
#include "../../linalg/tensor.hpp"
namespace mfem::future
{
template <typename T0, typename T1, typename T2>
MFEM_HOST_DEVICE
void process_qf_arg(const T0 &, const T1 &, T2 &)
{
static_assert(dfem::always_false<T0, T1, T2>,
"process_qf_arg not implemented for arg type");
}
template <typename T>
MFEM_HOST_DEVICE
void process_qf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
T &arg)
{
arg = u(0);
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
tensor<dual<T, T>, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
}
}
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
dual<T, T> &arg)
{
arg.value = u(0);
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
dual<T, T> &arg)
{
arg.value = u(0);
arg.gradient = v(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
tensor<dual<T, T>, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i).value = u(i);
arg(i).gradient = v(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
tensor<dual<T, T>, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
arg(j, i).gradient = v((i * m) + j);
}
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1, T> &r,
const tensor<dual<T, T>, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i).value;
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1, T> &r,
const tensor<dual<T, T>, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j).value;
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<2> &u,
const DeviceTensor<2> &v,
arg_type &arg,
const int &qp)
{
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
const auto v_qp = Reshape(&v(0, qp), v.GetShape()[0]);
process_qf_arg(u_qp, v_qp, arg);
}
template <size_t num_fields, typename qf_args>
MFEM_HOST_DEVICE inline
void process_qf_args(
const std::array<DeviceTensor<2>, num_fields> &u,
const std::array<DeviceTensor<2>, num_fields> &v,
qf_args &args,
const int &qp)
{
for_constexpr<tuple_size<qf_args>::value>([&](auto i)
{
process_qf_arg(u[i], v[i], get<i>(args), qp);
});
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const tensor<dual<T, T>, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j).gradient;
}
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const tensor<dual<T, T>, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i).gradient;
}
}
template <typename T0, typename T1>
MFEM_HOST_DEVICE inline
void process_qf_arg(const T0 &, T1 &)
{
static_assert(dfem::always_false<T0, T1>,
"process_qf_arg not implemented for arg type");
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1, T> &u,
T &arg)
{
arg = u(0);
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1, T> &u,
tensor<T> &arg)
{
arg(0) = u(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
tensor<T, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i) = u(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1> &u,
tensor<T, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE inline
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
{
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
process_qf_arg(u_qp, arg);
}
template <size_t num_fields, typename qf_args>
MFEM_HOST_DEVICE inline
void process_qf_args(
const std::array<DeviceTensor<2>, num_fields> &u,
qf_args &args,
const int &qp)
{
for_constexpr<tuple_size<qf_args>::value>([&](auto i)
{
process_qf_arg(u[i], get<i>(args), qp);
});
}
template <typename T0, typename T1>
MFEM_HOST_DEVICE inline
Vector process_qf_result(T0, T1)
{
static_assert(dfem::always_false<T0, T1>,
"process_qf_result not implemented for result type");
return Vector{};
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1, T> &r,
const T &x)
{
r(0) = x;
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1> &r,
const dual<T, T> &x)
{
r(0) = x.value;
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1, T> &r,
const tensor<T> &x)
{
r(0) = x(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1, T> &r,
const tensor<T, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_result(
DeviceTensor<1, T> &r,
const tensor<T, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j);
}
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
tensor<T, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
}
}
}
} // namespace mfem::future
-885
View File
@@ -1,885 +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
// This is serac's tuple implementation
#include <ostream>
#include "../../config/config.hpp"
#include <utility>
// Define a portable unreachable macro
#if defined(__GNUC__) || defined(__clang__)
#if defined(__CUDACC_VER_MAJOR__)
#if __CUDACC_VER_MAJOR__ <= 11 && __CUDACC_VER_MINOR__ < 3
// nvcc didn't add __builtin_unreachable() until cuda 11.3
#define MFEM_UNREACHABLE()
#else
// nvcc >= 11.3
#define MFEM_UNREACHABLE() __builtin_unreachable()
#endif
#else
// host-only version
#define MFEM_UNREACHABLE() __builtin_unreachable()
#endif
#elif defined(_MSC_VER)
#define MFEM_UNREACHABLE() __assume(0)
#endif
namespace mfem::future
{
/**
* @tparam T the types stored in the tuple
* @brief This is a class that mimics most of std::tuple's interface,
* except that it is usable in CUDA kernels and admits some arithmetic operator overloads.
*
* see https://en.cppreference.com/w/cpp/utility/tuple for more information about std::tuple
*/
template <typename... T>
struct tuple
{
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
*/
template <typename T0>
struct tuple<T0>
{
T0 v0; ///< The first member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
*/
template <typename T0, typename T1>
struct tuple<T0, T1>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
*/
template <typename T0, typename T1, typename T2>
struct tuple<T0, T1, T2>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3>
struct tuple<T0, T1, T2, T3>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4>
struct tuple<T0, T1, T2, T3, T4>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
struct tuple<T0, T1, T2, T3, T4, T5>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
* @tparam T6 The seventh type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
struct tuple<T0, T1, T2, T3, T4, T5, T6>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
* @tparam T6 The seventh type stored in the tuple
* @tparam T7 The eighth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
T7 v7; ///< The eighth member of the tuple
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
T7 v7; ///< The eighth member of the tuple
T8 v8;
};
/**
* @brief Class template argument deduction rule for tuples
* @tparam T The variadic template parameter for tuple types
*/
template <typename... T>
MFEM_HOST_DEVICE
tuple(T...) -> tuple<T...>;
/**
* @brief helper function for combining a list of values into a tuple
* @tparam T types of the values to be tuple-d
* @param args the actual values to be put into a tuple
*/
template <typename... T>
MFEM_HOST_DEVICE tuple<T...> make_tuple(const T&... args)
{
return tuple<T...> {args...};
}
template <class... Types>
struct tuple_size
{
};
template <class... Types>
struct tuple_size<tuple<Types...>> :
std::integral_constant<std::size_t, sizeof...(Types)>
{
};
/**
* @tparam i the tuple index to access
* @tparam T the types stored in the tuple
* @brief return a reference to the ith tuple entry
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
{
static_assert(i < sizeof...(T));
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
MFEM_UNREACHABLE();
}
/**
* @tparam i the tuple index to access
* @tparam T the types stored in the tuple
* @brief return a copy of the ith tuple entry
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
{
static_assert(i < sizeof...(T));
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
MFEM_UNREACHABLE();
}
/**
* @brief a function intended to be used for extracting the ith type from a tuple.
*
* @note type<i>(my_tuple) returns a value, whereas get<i>(my_tuple) returns a reference
*
* @tparam i the index of the tuple to query
* @tparam T the types stored in the tuple
* @param values the tuple of values
* @return a copy of the ith entry of the input
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
{
static_assert(i < sizeof...(T));
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @brief A helper function for the + operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple sum
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto plus_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) + get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise sum of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator+(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return plus_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the += operator of tuples
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be incremented
* @param y tuple of increment values
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr void plus_equals_helper(tuple<T...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
((get<i>(x) += get<i>(y)), ...);
}
/**
* @tparam T the types stored in the tuples x and y
* @param x a tuple of values
* @param y a tuple of values
* @brief add values contained in y, to the tuple x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
const tuple<T...>& y)
{
return plus_equals_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the -= operator of tuples
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be subracted from
* @param y tuple of values to subtract from x
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr void minus_equals_helper(tuple<T...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
((get<i>(x) -= get<i>(y)), ...);
}
/**
* @tparam T the types stored in the tuples x and y
* @param x a tuple of values
* @param y a tuple of values
* @brief add values contained in y, to the tuple x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator-=(tuple<T...>& x,
const tuple<T...>& y)
{
return minus_equals_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the - operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple difference
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto minus_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) - get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise difference of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return minus_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the - operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @return the returned tuple difference
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto unary_minus_helper(const tuple<T...>& x,
std::integer_sequence<int, i...>)
{
return tuple{-get<i>(x)...};
}
/**
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @brief return a tuple of values defined by applying the unary minus operator to each element of x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<T...>& x)
{
return unary_minus_helper(x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple ratio
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) / get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise division of x by y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return div_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant numerator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
const tuple<T...>& x, std::integer_sequence<int, i...>)
{
return tuple{a / get<i>(x)...};
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant denomenator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
const real_t a, std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) / a...};
}
/**
* @tparam T the types stored in the tuple x
* @param a the numerator
* @param x a tuple of denominator values
* @brief return a tuple of values defined by division of a by the elements of x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const real_t a, const tuple<T...>& x)
{
return div_helper(a, x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple y
* @param x a tuple of numerator values
* @param a a denominator
* @brief return a tuple of values defined by elementwise division of x by a
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const real_t a)
{
return div_helper(x, a,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple product
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) * get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise multiplication of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return mult_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a a constant multiplier
* @return the returned tuple product
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const real_t a,
const tuple<T...>& x, std::integer_sequence<int, i...>)
{
return tuple{a * get<i>(x)...};
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a a constant multiplier
* @return the returned tuple product
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
const real_t a, std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) * a...};
}
/**
* @tparam T the types stored in the tuple
* @param a a scaling factor
* @param x the tuple object
* @brief multiply each component of x by the value a on the left
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const real_t a, const tuple<T...>& x)
{
return mult_helper(a, x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple
* @param x the tuple object
* @param a a scaling factor
* @brief multiply each component of x by the value a on the right
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const real_t a)
{
return mult_helper(x, a,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple
* @tparam i a list of indices used to acces each element of the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief helper used to implement printing a tuple of values
*/
template <typename... T, std::size_t... i>
auto& print_helper(std::ostream& out, const tuple<T...>& A,
std::integer_sequence<size_t, i...>)
{
out << "tuple{";
(..., (out << (i == 0 ? "" : ", ") << get<i>(A)));
out << "}";
return out;
}
/**
* @tparam T the types stored in the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief print a tuple of values
*/
template <typename... T>
auto& operator<<(std::ostream& out, const tuple<T...>& A)
{
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
}
/**
* @brief A helper to apply a lambda to a tuple
*
* @tparam lambda The functor type
* @tparam T The tuple types
* @tparam i The integer sequence to i
* @param f The functor to apply to the tuple
* @param args The input tuple
* @return The functor output
*/
template <typename lambda, typename... T, int... i>
MFEM_HOST_DEVICE auto apply_helper(lambda f, tuple<T...>& args,
std::integer_sequence<int, i...>)
{
return f(get<i>(args)...);
}
/**
* @tparam lambda a callable type
* @tparam T the types of arguments to be passed in to f
* @param f the callable object
* @param args a tuple of arguments
* @brief a way of passing an n-tuple to a function that expects n separate arguments
*
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
*/
template <typename lambda, typename... T>
MFEM_HOST_DEVICE auto apply(lambda f, tuple<T...>& args)
{
return apply_helper(f, std::move(args),
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @overload
*/
template <typename lambda, typename... T, int... i>
MFEM_HOST_DEVICE auto apply_helper(lambda f, const tuple<T...>& args,
std::integer_sequence<int, i...>)
{
return f(get<i>(args)...);
}
/**
* @tparam lambda a callable type
* @tparam T the types of arguments to be passed in to f
* @param f the callable object
* @param args a tuple of arguments
* @brief a way of passing an n-tuple to a function that expects n separate arguments
*
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
*/
template <typename lambda, typename... T>
MFEM_HOST_DEVICE auto apply(lambda f, const tuple<T...>& args)
{
return apply_helper(f, std::move(args),
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief a struct used to determine the type at index I of a tuple
*
* @note see: https://en.cppreference.com/w/cpp/utility/tuple/tuple_element
*
* @tparam I the index of the desired type
* @tparam T a tuple of different types
*/
template <size_t I, class T>
struct tuple_element;
// recursive case
/// @overload
template <size_t I, class Head, class... Tail>
struct tuple_element<I, tuple<Head, Tail...>> : tuple_element<I - 1,
tuple<Tail...>>
{
};
// base case
/// @overload
template <class Head, class... Tail>
struct tuple_element<0, tuple<Head, Tail...>>
{
using type = Head; ///< the type at the specified index
};
/**
* @brief Trait for checking if a type is a @p mfem::tuple
*/
template <typename T>
struct is_tuple : std::false_type
{
};
/// @overload
template <typename... T>
struct is_tuple<tuple<T...>> : std::true_type
{
};
/**
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
*/
template <typename T>
struct is_tuple_of_tuples : std::false_type
{
};
/**
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
*/
template <typename... T>
struct is_tuple_of_tuples<tuple<T...>>
{
static constexpr bool value = (is_tuple<T>::value &&
...); ///< true/false result of type check
};
/** @brief Auxiliary template function that merges (concatenates) two
mfem::future::tuple types into a single std::tuple that is empty, i.e. it is
value initialized. */
template <typename... T1s, typename... T2s>
constexpr auto merge_mfem_tuples_as_empty_std_tuple(
const mfem::future::tuple<T1s...> &,
const mfem::future::tuple<T2s...> &)
{
return std::tuple<T1s..., T2s...> {};
}
} // namespace mfem::future
-2275
View File
File diff suppressed because it is too large Load Diff
+52 -65
View File
@@ -17,10 +17,7 @@
namespace mfem
{
struct DGMassInvKernels { DGMassInvKernels(); };
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
Coefficient *coeff,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
const IntegrationRule *ir,
int btype)
: Solver(fes_orig.GetTrueVSize()),
@@ -30,8 +27,6 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
fes_orig.GetTypicalFE()->GetMapType()),
fes(fes_orig.GetMesh(), &fec)
{
static DGMassInvKernels kernels;
MFEM_VERIFY(fes.IsDGSpace(), "Space must be DG.");
MFEM_VERIFY(!fes.IsVariableOrder(), "Variable orders not supported.");
@@ -51,7 +46,7 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
const FiniteElement &fe = *fes.GetTypicalFE();
d2q = &fe_orig.GetDofToQuad(fe.GetNodes(), mode);
const int n = d2q->ndof;
int n = d2q->ndof;
Array<real_t> B_inv = d2q->B; // deep copy
Array<int> ipiv(n);
// solver basis to original
@@ -76,7 +71,7 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
// Only need transformed RHS if basis is different
if (btype_orig != btype) { b2_.SetSize(height); }
M.reset(new BilinearForm(&fes));
M = new BilinearForm(&fes);
M->AddDomainIntegrator(m); // M assumes ownership of m
M->SetAssemblyLevel(AssemblyLevel::PARTIAL);
@@ -84,19 +79,19 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
Update();
}
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
int btype)
: DGMassInverse(fes_, &coeff, nullptr, btype) { }
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
const IntegrationRule &ir, int btype)
: DGMassInverse(fes_, &coeff, &ir, btype) { }
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_,
const IntegrationRule &ir, int btype)
: DGMassInverse(fes_, nullptr, &ir, btype) { }
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, int btype)
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, int btype)
: DGMassInverse(fes_, nullptr, nullptr, btype) { }
void DGMassInverse::SetOperator(const Operator &op)
@@ -117,7 +112,10 @@ void DGMassInverse::Update()
diag_inv.Reciprocal();
}
DGMassInverse::~DGMassInverse() = default;
DGMassInverse::~DGMassInverse()
{
delete M;
}
template<int DIM, int D1D, int Q1D>
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
@@ -271,58 +269,47 @@ void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
const int d1d = m->dofs1D;
const int q1d = m->quad1D;
CGKernels::Run(dim, d1d, q1d, *this, Mu, u);
const int id = (d1d << 4) | q1d;
if (dim == 2)
{
switch (id)
{
case 0x11: return DGMassCGIteration<2,1,1>(Mu, u);
case 0x22: return DGMassCGIteration<2,2,2>(Mu, u);
case 0x33: return DGMassCGIteration<2,3,3>(Mu, u);
case 0x35: return DGMassCGIteration<2,3,5>(Mu, u);
case 0x44: return DGMassCGIteration<2,4,4>(Mu, u);
case 0x46: return DGMassCGIteration<2,4,6>(Mu, u);
case 0x55: return DGMassCGIteration<2,5,5>(Mu, u);
case 0x57: return DGMassCGIteration<2,5,7>(Mu, u);
case 0x66: return DGMassCGIteration<2,6,6>(Mu, u);
case 0x68: return DGMassCGIteration<2,6,8>(Mu, u);
default: return DGMassCGIteration<2>(Mu, u); // Fallback
}
}
else if (dim == 3)
{
switch (id)
{
case 0x22: return DGMassCGIteration<3,2,2>(Mu, u);
case 0x23: return DGMassCGIteration<3,2,3>(Mu, u);
case 0x33: return DGMassCGIteration<3,3,3>(Mu, u);
case 0x34: return DGMassCGIteration<3,3,4>(Mu, u);
case 0x35: return DGMassCGIteration<3,3,5>(Mu, u);
case 0x44: return DGMassCGIteration<3,4,4>(Mu, u);
case 0x45: return DGMassCGIteration<3,4,5>(Mu, u);
case 0x46: return DGMassCGIteration<3,4,6>(Mu, u);
case 0x48: return DGMassCGIteration<3,4,8>(Mu, u);
case 0x55: return DGMassCGIteration<3,5,5>(Mu, u);
case 0x56: return DGMassCGIteration<3,5,6>(Mu, u);
case 0x57: return DGMassCGIteration<3,5,7>(Mu, u);
case 0x58: return DGMassCGIteration<3,5,8>(Mu, u);
case 0x66: return DGMassCGIteration<3,6,6>(Mu, u);
case 0x67: return DGMassCGIteration<3,6,7>(Mu, u);
default: return DGMassCGIteration<3>(Mu, u); // Fallback
}
}
}
DGMassInvKernels::DGMassInvKernels()
{
using k = DGMassInverse::CGKernels;
// 2D
k::Specialization<2,1,1>::Add();
k::Specialization<2,2,2>::Add();
k::Specialization<2,3,3>::Add();
k::Specialization<2,3,5>::Add();
k::Specialization<2,4,4>::Add();
k::Specialization<2,4,6>::Add();
k::Specialization<2,5,5>::Add();
k::Specialization<2,5,7>::Add();
k::Specialization<2,6,6>::Add();
k::Specialization<2,6,8>::Add();
// 3D
k::Specialization<3,2,2>::Add();
k::Specialization<3,2,3>::Add();
k::Specialization<3,3,3>::Add();
k::Specialization<3,3,4>::Add();
k::Specialization<3,3,5>::Add();
k::Specialization<3,4,4>::Add();
k::Specialization<3,4,5>::Add();
k::Specialization<3,4,6>::Add();
k::Specialization<3,4,8>::Add();
k::Specialization<3,5,5>::Add();
k::Specialization<3,5,6>::Add();
k::Specialization<3,5,7>::Add();
k::Specialization<3,5,8>::Add();
k::Specialization<3,6,6>::Add();
k::Specialization<3,6,7>::Add();
}
/// @cond Suppress_Doxygen_warnings
template <int DIM, int D1D, int Q1D>
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
{
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
}
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
int dim, int, int)
{
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
else { MFEM_ABORT("Unsupported dimension."); }
}
/// @endcond
} // namespace mfem
+9 -15
View File
@@ -14,8 +14,6 @@
#include "../linalg/operator.hpp"
#include "fespace.hpp"
#include "kernel_dispatch.hpp"
#include <memory>
namespace mfem
{
@@ -34,7 +32,7 @@ protected:
const DofToQuad *d2q; ///< Change of basis. Not owned.
Array<real_t> B_; ///< Inverse of change of basis.
Array<real_t> Bt_; ///< Inverse of change of basis, transposed.
std::unique_ptr<class BilinearForm> M; ///< Mass bilinear form.
class BilinearForm *M; ///< Mass bilinear form, owned.
class MassIntegrator *m; ///< Mass integrator, owned by the form @ref M.
Vector diag_inv; ///< Jacobi preconditioner.
real_t rel_tol = 1e-12; ///< Relative CG tolerance.
@@ -50,7 +48,7 @@ protected:
///
/// Custom coefficient and integration rule are used if @a coeff and @a ir
/// are non-NULL.
DGMassInverse(const FiniteElementSpace &fes_, Coefficient *coeff,
DGMassInverse(FiniteElementSpace &fes_, Coefficient *coeff,
const IntegrationRule *ir, int btype);
public:
/// @brief Construct the DG inverse mass operator for @a fes_.
@@ -63,37 +61,36 @@ public:
/// The solution and right-hand side used for the solver are not affected by
/// this basis (they correspond to the basis of @a fes_). @a btype is only
/// used internally, and only has an effect on the convergence rate.
DGMassInverse(const FiniteElementSpace &fes_,
int btype=BasisType::GaussLegendre);
DGMassInverse(FiniteElementSpace &fes_, int btype=BasisType::GaussLegendre);
/// @brief Construct the DG inverse mass operator for @a fes_ with
/// Coefficient @a coeff.
///
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
/// btype.
DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
int btype=BasisType::GaussLegendre);
/// @brief Construct the DG inverse mass operator for @a fes_ with
/// Coefficient @a coeff and IntegrationRule @a ir.
///
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
/// btype.
DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
const IntegrationRule &ir, int btype=BasisType::GaussLegendre);
/// @brief Construct the DG inverse mass operator for @a fes_ with
/// IntegrationRule @a ir.
///
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
/// btype.
DGMassInverse(const FiniteElementSpace &fes_, const IntegrationRule &ir,
DGMassInverse(FiniteElementSpace &fes_, const IntegrationRule &ir,
int btype=BasisType::GaussLegendre);
/// @brief Solve the system M b = u.
///
/// If @ref iterative_mode is @a true, @a u is used as an initial guess.
void Mult(const Vector &b, Vector &u) const override;
void Mult(const Vector &b, Vector &u) const;
/// Same as Mult() since the mass matrix is symmetric.
void MultTranspose(const Vector &b, Vector &u) const override { Mult(b, u); }
void MultTranspose(const Vector &b, Vector &u) const { Mult(b, u); }
/// Not implemented. Aborts.
void SetOperator(const Operator &op) override;
void SetOperator(const Operator &op);
/// Set the relative tolerance.
void SetRelTol(const real_t rel_tol_);
/// Set the absolute tolerance.
@@ -110,9 +107,6 @@ public:
/// extended lambda used in an mfem::forall kernel (nvcc limitation)
template<int DIM, int D1D = 0, int Q1D = 0>
void DGMassCGIteration(const Vector &b_, Vector &u_) const;
using CGKernelType = void(DGMassInverse::*)(const Vector &b_, Vector &u) const;
MFEM_REGISTER_KERNELS(CGKernels, CGKernelType, (int, int, int));
};
} // namespace mfem

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