Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea02b5bd36 | ||
|
|
ae93d82ccd | ||
|
|
4eb88fbfa5 | ||
|
|
5a90acfed6 | ||
|
|
69cea35209 | ||
|
|
026493bea7 | ||
|
|
fef354dfc5 | ||
|
|
15519823c4 | ||
|
|
127390c6a7 | ||
|
|
a62661f3cc | ||
|
|
e76ffd82e6 | ||
|
|
6288f0b741 | ||
|
|
54f8470412 | ||
|
|
5059d631ba | ||
|
|
a3d652e2c2 | ||
|
|
0de221816e | ||
|
|
cbdd94b0ee | ||
|
|
b09e705f66 | ||
|
|
0a115bf6a8 | ||
|
|
3835a5e5e8 | ||
|
|
1ea4785ab3 | ||
|
|
a109c7e8ad | ||
|
|
984b269972 | ||
|
|
4b110894fc | ||
|
|
de7b80ce67 | ||
|
|
5c7503466c | ||
|
|
6b2658bb7b | ||
|
|
c319b8fa04 | ||
|
|
8ba1f17f72 | ||
|
|
e5f5a79e66 | ||
|
|
43f1b19767 | ||
|
|
7bebe4528f | ||
|
|
da63657cdd | ||
|
|
ee7d9726df | ||
|
|
5657f6ebe8 | ||
|
|
19543b6b16 | ||
|
|
b56e994ecd | ||
|
|
ae8e5aa88d | ||
|
|
08f3c86b8a | ||
|
|
52bc915120 | ||
|
|
e66a61c198 | ||
|
|
f8b3c78b19 | ||
|
|
4749746171 | ||
|
|
11fce4235b | ||
|
|
fd341e07da | ||
|
|
d59e2a229c |
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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,17 +121,7 @@ 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)
|
||||
|
||||
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 }}
|
||||
|
||||
@@ -144,8 +131,8 @@ jobs:
|
||||
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
|
||||
@@ -157,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"
|
||||
@@ -285,18 +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 llvm@20 enzyme
|
||||
echo "LLVM_PREFIX=$(brew --prefix llvm@20)" >> $GITHUB_ENV
|
||||
echo "OMPI_CC=$(brew --prefix llvm@20)/bin/clang" >> $GITHUB_ENV
|
||||
echo "OMPI_CXX=$(brew --prefix llvm@20)/bin/clang++" >> $GITHUB_ENV
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
|
||||
@@ -45,15 +45,14 @@ jobs:
|
||||
|
||||
- name: Get MPI (Linux)
|
||||
run: |
|
||||
sudo apt-get install openmpi-bin libopenmpi-dev
|
||||
export OMPI_MCA_rmaps_base_oversubscribe=1
|
||||
sudo apt-get install mpich libmpich-dev
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -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
|
||||
@@ -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}}
|
||||
@@ -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}}"
|
||||
@@ -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
|
||||
+6
-18
@@ -201,9 +201,6 @@ 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
|
||||
@@ -211,7 +208,7 @@ miniapps/electromagnetics/joule
|
||||
miniapps/electromagnetics/Volta-AMR*
|
||||
miniapps/electromagnetics/Tesla-AMR*
|
||||
miniapps/electromagnetics/Maxwell-Parallel*
|
||||
miniapps/electromagnetics/Joule_[0-9]*
|
||||
miniapps/electromagnetics/Joule_*
|
||||
|
||||
miniapps/gslib/field-diff
|
||||
miniapps/gslib/field-interp
|
||||
@@ -232,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
|
||||
@@ -263,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
|
||||
@@ -277,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
|
||||
@@ -300,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
|
||||
@@ -319,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
|
||||
@@ -341,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
|
||||
@@ -408,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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -18,7 +18,7 @@
|
||||
setup_baseline:
|
||||
tags:
|
||||
- shell
|
||||
- dane
|
||||
- ruby
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,75 +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.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
|
||||
- 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`.
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
Version 4.8, released on Apr 9, 2025
|
||||
====================================
|
||||
|
||||
+11
-41
@@ -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,11 +256,7 @@ 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()
|
||||
@@ -405,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)
|
||||
@@ -553,6 +531,7 @@ if (MFEM_USE_ENZYME)
|
||||
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
|
||||
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
|
||||
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
|
||||
set(ENZYME_FOUND 1)
|
||||
endif()
|
||||
|
||||
# MFEM_TIMER_TYPE
|
||||
@@ -592,26 +571,20 @@ 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)
|
||||
|
||||
# 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)
|
||||
@@ -686,10 +659,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()
|
||||
|
||||
+1
-5
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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".
|
||||
|
||||
@@ -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@)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -9,18 +9,15 @@
|
||||
# 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()
|
||||
@@ -36,55 +33,6 @@ if (HYPRE_FOUND OR TARGET HYPRE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (HYPRE_FETCH OR FETCH_TPLS)
|
||||
set(HYPRE_FETCH_VERSION 2.33.0)
|
||||
add_library(HYPRE STATIC IMPORTED)
|
||||
# set options and associated dependencies
|
||||
set(CMAKE_OPTIONS)
|
||||
list(APPEND CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (MFEM_USE_CUDA)
|
||||
list(APPEND CMAKE_OPTIONS -DHYPRE_WITH_CUDA:BOOL=ON)
|
||||
find_package(CUDAToolkit REQUIRED)
|
||||
target_link_libraries(HYPRE INTERFACE CUDA::cusparse CUDA::curand CUDA::cublas)
|
||||
elseif (MFEM_USE_HIP)
|
||||
list(APPEND CMAKE_OPTIONS -DHYPRE_WITH_HIP:BOOL=ON)
|
||||
find_package(rocsparse REQUIRED)
|
||||
find_package(rocrand REQUIRED)
|
||||
target_link_libraries(HYPRE INTERFACE rocsparse rocrand)
|
||||
endif()
|
||||
if (MFEM_USE_SINGLE)
|
||||
list(APPEND 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_VERSION} to be built with ${CMAKE_OPTIONS}")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/hypre)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(hypre
|
||||
GIT_REPOSITORY https://github.com/hypre-space/hypre.git
|
||||
GIT_TAG v${HYPRE_FETCH_VERSION}
|
||||
GIT_SHALLOW TRUE
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
SOURCE_SUBDIR src
|
||||
PREFIX ${PREFIX}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${PREFIX} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${CMAKE_OPTIONS})
|
||||
file(MAKE_DIRECTORY ${PREFIX}/include)
|
||||
# set imported library target properties
|
||||
add_dependencies(HYPRE hypre)
|
||||
set_target_properties(HYPRE PROPERTIES
|
||||
IMPORTED_LOCATION ${PREFIX}/lib/libHYPRE.a
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${PREFIX}/include)
|
||||
# convert HYPRE version to integer
|
||||
string(REGEX MATCHALL "[0-9]+" HYPRE_SPLIT_VERSION ${HYPRE_FETCH_VERSION})
|
||||
list(GET HYPRE_SPLIT_VERSION 0 HYPRE_MAJOR_VERSION)
|
||||
list(GET HYPRE_SPLIT_VERSION 1 HYPRE_MINOR_VERSION)
|
||||
list(GET HYPRE_SPLIT_VERSION 2 HYPRE_PATCH_VERSION)
|
||||
math(EXPR HYPRE_VERSION "10000*${HYPRE_MAJOR_VERSION} + 100*${HYPRE_MINOR_VERSION} + ${HYPRE_PATCH_VERSION}")
|
||||
# set cache variables that would otherwise be set after mfem_find_package call
|
||||
set(HYPRE_VERSION ${HYPRE_VERSION} CACHE STRING "HYPRE version." FORCE)
|
||||
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."
|
||||
@@ -138,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}")
|
||||
|
||||
@@ -9,38 +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
|
||||
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"
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
-2
@@ -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@
|
||||
@@ -98,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@
|
||||
|
||||
@@ -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.")
|
||||
|
||||
+22
-40
@@ -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)
|
||||
# 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 =
|
||||
@@ -625,19 +605,21 @@ TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mi
|
||||
|
||||
# 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
|
||||
ENZYME_LLVM_VERSION = 19
|
||||
ENZYME_OPT = -fplugin=$(ENZYME_DIR)/lib/ClangEnzyme-$(ENZYME_LLVM_VERSION).$(SO_EXT)
|
||||
ENZYME_LIB = ""
|
||||
ifeq ($(MFEM_USE_ENZYME),YES)
|
||||
BASE_FLAGS = -std=c++17
|
||||
endif
|
||||
|
||||
# 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
@@ -115,7 +115,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: H1_3D_P2
|
||||
FiniteElementCollection: Quadratic
|
||||
VDim: 3
|
||||
Ordering: 0
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: H1_3D_P2
|
||||
FiniteElementCollection: Quadratic
|
||||
VDim: 3
|
||||
Ordering: 0
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: H1_2D_P2
|
||||
FiniteElementCollection: Quadratic
|
||||
VDim: 2
|
||||
Ordering: 0
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ vertices
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: H1_2D_P2
|
||||
FiniteElementCollection: Quadratic
|
||||
VDim: 2
|
||||
Ordering: 0
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -249,3 +249,5 @@ endif()
|
||||
if(MFEM_USE_MOONOLITH)
|
||||
add_subdirectory(moonolith)
|
||||
endif()
|
||||
|
||||
add_subdirectory(dfem)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# 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.
|
||||
|
||||
set(DFEM_EXAMPLES_SRCS)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
list(APPEND DFEM_EXAMPLES_SRCS
|
||||
plasticity.cpp
|
||||
laghos.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
|
||||
# Add "test_dfem" target, see below.
|
||||
add_custom_target(test_dfem
|
||||
${CMAKE_CTEST_COMMAND} -R dfem USES_TERMINAL)
|
||||
|
||||
# Add one executable per cpp file, adding "dfem_" as prefix so the CMake
|
||||
# target is unique from those in the non-dFEM examples. Also sets
|
||||
# "test_dfem" as a target that depends on the given dFEM examples.
|
||||
set(PFX dfem_)
|
||||
add_mfem_examples(DFEM_EXAMPLES_SRCS ${PFX} "" test_dfem)
|
||||
|
||||
# Remove "dfem_" prefix from exectuable name for consistency with GNU build
|
||||
# system.
|
||||
foreach(SRC_FILE ${DFEM_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TARGET_NAME "${PFX}${SRC_FILENAME}")
|
||||
string(REPLACE ${PFX} "" EXE_NAME ${TARGET_NAME})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${EXE_NAME})
|
||||
endforeach()
|
||||
|
||||
# Testing.
|
||||
# The dFEM tests can be run separately using the target "test_dfem"
|
||||
# which builds the examples and runs:
|
||||
# ctest -R dfem
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
# Command line options for the tests.
|
||||
# Example 9: test CVODE with CV_ADAMS (non-stiff implicit) time stepping
|
||||
# set(EX9_COMMON_OPTS -m ../../data/periodic-hexagon.mesh -p 0 -s 7)
|
||||
# set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 -dt 0.0018 -vs 25)
|
||||
# set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS} -rp 1 -dt 0.0009 -vs 50)
|
||||
# Example 10: test CVODE with CV_BDF (stiff implicit) time stepping
|
||||
# set(EX10_COMMON_OPTS -m ../../data/beam-quad.mesh -o 2 -s 5 -dt 0.15 -tf 6 -vs 10)
|
||||
# set(EX10_TEST_OPTS ${EX10_COMMON_OPTS} -r 2)
|
||||
# set(EX10P_TEST_OPTS ${EX10_COMMON_OPTS} -rp 1)
|
||||
# Example 16: test ARKODE with implicit time stepping using mass form
|
||||
# set(EX16_COMMON_OPTS -s 15)
|
||||
# set(EX16_TEST_OPTS ${EX16_COMMON_OPTS})
|
||||
# set(EX16P_TEST_OPTS ${EX16_COMMON_OPTS})
|
||||
|
||||
# Add the tests: one test per source file.
|
||||
foreach(SRC_FILE ${DFEM_EXAMPLES_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
set(TEST_NAME ${PFX}${TEST_NAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Add CUDA/HIP tests.
|
||||
set(DEVICE_EXAMPLES
|
||||
# parallel examples with device support:
|
||||
# ex9p
|
||||
)
|
||||
set(MFEM_TEST_DEVICE)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_TEST_DEVICE "cuda")
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_TEST_DEVICE "hip")
|
||||
endif()
|
||||
if (MFEM_TEST_DEVICE)
|
||||
foreach(TEST_NAME ${DEVICE_EXAMPLES})
|
||||
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis" "-d" "${MFEM_TEST_DEVICE}")
|
||||
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${PFX}${TEST_NAME}_${MFEM_TEST_DEVICE}_ser
|
||||
COMMAND ${PFX}${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${PFX}${TEST_NAME}_${MFEM_TEST_DEVICE}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${PFX}${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif(MFEM_TEST_DEVICE)
|
||||
endif(MFEM_ENABLE_TESTING)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/dfem/,)
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/dfem/,)
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
@@ -21,7 +21,7 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES =
|
||||
PAR_EXAMPLES = dfem-minimal-surface
|
||||
PAR_EXAMPLES = laghos
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
@@ -30,7 +30,7 @@ endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
.PHONY: all clean clean-build
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
@@ -41,27 +41,30 @@ endif
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
ifeq ($(MFEM_USE_ENZYME),NO)
|
||||
$(EXAMPLES):
|
||||
$(error MFEM is not configured with ENZYME)
|
||||
endif
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
RUN_MPI_NP = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP)
|
||||
RUN_MPI = $(RUN_MPI_NP) $(MFEM_MPI_NP)
|
||||
SERIAL_NAME := Serial dFEM example
|
||||
PARALLEL_NAME := Parallel dFEM example
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
@$(call mfem-test,$<,, $(SERIAL_NAME))
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
clean: clean-build
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -rf dfem-minimal-surface-output
|
||||
@@ -0,0 +1,587 @@
|
||||
// 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 <mfem.hpp>
|
||||
|
||||
// TODO: Do we want this to be included from mfem.hpp automatically now?
|
||||
#include <fem/dfem/doperator.hpp>
|
||||
#include <linalg/tensor.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::internal::tensor;
|
||||
|
||||
constexpr int DIMENSION = 2;
|
||||
|
||||
template <typename T, int dim>
|
||||
MFEM_HOST_DEVICE inline
|
||||
tensor<T, 3, 3> tensor_to_3D(const tensor<T, dim, dim>& A)
|
||||
{
|
||||
tensor<T, 3, 3> A3D{};
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
for (int j = 0; j < dim; j++)
|
||||
{
|
||||
A3D[i][j] = A[i][j];
|
||||
}
|
||||
}
|
||||
return A3D;
|
||||
}
|
||||
|
||||
template <typename Material, int dim = DIMENSION>
|
||||
struct InternalStateQFunction
|
||||
{
|
||||
InternalStateQFunction() = default;
|
||||
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto operator()(
|
||||
const tensor<real_t, dim, dim> &dudxi,
|
||||
const tensor<real_t, dim, dim> &J,
|
||||
const tensor<real_t, 10> &internal_state,
|
||||
const double &w) const
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
auto dudX = dudxi * invJ;
|
||||
auto dudX3D = tensor_to_3D(dudX);
|
||||
//auto internal_state_new = get<1>(material(dudX3D, internal_state));
|
||||
auto [stress, internal_state_new] = material(dudX3D, internal_state);
|
||||
// real_t vm = sqrt(1.5)*norm(dev(stress));
|
||||
// out << vm << " " << internal_state_new[9] << std::endl;
|
||||
return mfem::tuple{internal_state_new};
|
||||
}
|
||||
|
||||
Material material;
|
||||
};
|
||||
|
||||
template <typename Material, int dim = DIMENSION>
|
||||
struct MomentumRefStateQFunction
|
||||
{
|
||||
MomentumRefStateQFunction() = default;
|
||||
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto operator()(
|
||||
const tensor<real_t, dim, dim> &dudxi,
|
||||
const tensor<real_t, dim, dim> &J,
|
||||
const tensor<real_t, 10> &internal_state,
|
||||
const double &w) const
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
auto dudX = dudxi * invJ;
|
||||
auto dudX3D = tensor_to_3D(dudX);
|
||||
auto [P3D, Qnew] = material(dudX3D, internal_state);
|
||||
auto P = mfem::internal::make_tensor<dim, dim>([&P3D](int i, int j) { return P3D[i][j]; });
|
||||
auto JxW = det(J) * w * transpose(invJ);
|
||||
return mfem::tuple{P * JxW};
|
||||
}
|
||||
|
||||
Material material;
|
||||
};
|
||||
|
||||
|
||||
struct J2SmallStrain
|
||||
{
|
||||
static constexpr int dim = 3; ///< spatial dimension
|
||||
static constexpr int n_internal_states = 10;
|
||||
static constexpr double tol =
|
||||
1e-10; ///< relative tolerance on residual mag to judge convergence of return map
|
||||
|
||||
real_t E; ///< Young's modulus
|
||||
real_t nu; ///< Poisson's ratio
|
||||
real_t sigma_y; ///< Yield strength
|
||||
real_t Hi; ///< Isotropic hardening modulus
|
||||
real_t density; ///< Mass density
|
||||
|
||||
/// @brief variables required to characterize the hysteresis response
|
||||
struct InternalState
|
||||
{
|
||||
tensor<double, dim, dim> plastic_strain; ///< plastic strain
|
||||
double accumulated_plastic_strain; ///< uniaxial equivalent plastic strain
|
||||
};
|
||||
|
||||
MFEM_HOST_DEVICE inline
|
||||
InternalState unpack_internal_state(const tensor<real_t, n_internal_states> &
|
||||
packed_state) const
|
||||
{
|
||||
// we could use type punning here to avoid copies
|
||||
auto plastic_strain = mfem::internal::make_tensor<dim, dim>(
|
||||
[&packed_state](int i, int j) { return packed_state[dim*i + j]; });
|
||||
real_t accumulated_plastic_strain = packed_state[n_internal_states - 1];
|
||||
return {plastic_strain, accumulated_plastic_strain};
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline
|
||||
tensor<real_t, n_internal_states> pack_internal_state(const
|
||||
tensor<real_t, dim, dim> & plastic_strain,
|
||||
real_t accumulated_plastic_strain) const
|
||||
{
|
||||
tensor<real_t, n_internal_states> packed_state{};
|
||||
for (int i = 0, ij = 0; i < dim; i++)
|
||||
{
|
||||
for (int j = 0; j < dim; j++, ij++)
|
||||
{
|
||||
packed_state[ij] = plastic_strain[i][j];
|
||||
}
|
||||
}
|
||||
packed_state[n_internal_states - 1] = accumulated_plastic_strain;
|
||||
return packed_state;
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline
|
||||
tuple<tensor<real_t, dim, dim>, tensor<real_t, n_internal_states>>
|
||||
operator()(const tensor<real_t, dim, dim> & dudX,
|
||||
const tensor<real_t, n_internal_states> & internal_state) const
|
||||
{
|
||||
auto I = mfem::internal::Identity<dim>();
|
||||
const real_t K = E / (3.0 * (1.0 - 2.0 * nu));
|
||||
const real_t G = 0.5 * E / (1.0 + nu);
|
||||
|
||||
auto [plastic_strain, accumulated_plastic_strain] = unpack_internal_state(
|
||||
internal_state);
|
||||
|
||||
// (i) elastic predictor
|
||||
auto el_strain = sym(dudX) - plastic_strain;
|
||||
auto p = K * tr(el_strain);
|
||||
auto s = 2.0 * G * dev(el_strain);
|
||||
auto q = sqrt(1.5) * norm(s);
|
||||
[[maybe_unused]] real_t delta_eqps = 0.0;
|
||||
|
||||
[[maybe_unused]] auto flow_strength = [this](real_t eqps) { return this->sigma_y + this->Hi*eqps; };
|
||||
|
||||
// (ii) admissibility
|
||||
if (q - (sigma_y + Hi*accumulated_plastic_strain) > tol*sigma_y)
|
||||
{
|
||||
// (iii) return mapping
|
||||
real_t delta_eqps = (q - sigma_y - Hi*accumulated_plastic_strain)/(3*G + Hi);
|
||||
auto Np = 1.5 * s / q;
|
||||
s -= 2.0 * G * delta_eqps * Np;
|
||||
plastic_strain += delta_eqps * Np;
|
||||
accumulated_plastic_strain += delta_eqps;
|
||||
}
|
||||
auto stress = s + p * I;
|
||||
auto internal_state_new = pack_internal_state(plastic_strain,
|
||||
accumulated_plastic_strain);
|
||||
return {stress, internal_state_new};
|
||||
}
|
||||
};
|
||||
|
||||
class ElasticityOperator : public Operator
|
||||
{
|
||||
static constexpr int Displacement = 0;
|
||||
static constexpr int Coordinates = 1;
|
||||
static constexpr int InternalState = 2;
|
||||
|
||||
public:
|
||||
class ElasticityJacobianOperator : public Operator
|
||||
{
|
||||
public:
|
||||
ElasticityJacobianOperator(const ElasticityOperator *elasticity,
|
||||
const Vector &x) :
|
||||
Operator(elasticity->Height()),
|
||||
elasticity(elasticity),
|
||||
z(elasticity->Height())
|
||||
{
|
||||
ParGridFunction u(&elasticity->displacement_fes);
|
||||
u.SetFromTrueDofs(x);
|
||||
auto mesh_nodes = static_cast<ParGridFunction*>
|
||||
(elasticity->displacement_fes.GetParMesh()->GetNodes());
|
||||
momentum_du = elasticity->momentum->GetDerivative(Displacement, {&u}, {mesh_nodes, &elasticity->internal_state});
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
z = x;
|
||||
z.SetSubVector(elasticity->displacement_ess_tdof, 0.0);
|
||||
|
||||
momentum_du->Mult(z, y);
|
||||
|
||||
for (int i = 0; i < elasticity->displacement_ess_tdof.Size(); i++)
|
||||
{
|
||||
y[elasticity->displacement_ess_tdof[i]] =
|
||||
x[elasticity->displacement_ess_tdof[i]];
|
||||
}
|
||||
}
|
||||
|
||||
const ElasticityOperator *elasticity;
|
||||
std::shared_ptr<DerivativeOperator> momentum_du;
|
||||
mutable Vector z;
|
||||
};
|
||||
|
||||
template <typename Material>
|
||||
ElasticityOperator(ParFiniteElementSpace &displacement_fes,
|
||||
Array<int> &vel_ess_tdofs,
|
||||
const IntegrationRule &displacement_ir,
|
||||
ParametricFunction &internal_state,
|
||||
Material material) :
|
||||
Operator(displacement_fes.GetTrueVSize()),
|
||||
density(1.0e3),
|
||||
body_force(displacement_fes.GetTrueVSize()),
|
||||
displacement_ess_tdof(vel_ess_tdofs),
|
||||
displacement_fes(displacement_fes),
|
||||
displacement_ir(displacement_ir),
|
||||
internal_state(internal_state)
|
||||
{
|
||||
auto mesh = displacement_fes.GetParMesh();
|
||||
mesh_nodes = static_cast<ParGridFunction*>(mesh->GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
{
|
||||
auto solutions = std::vector
|
||||
{
|
||||
FieldDescriptor{Displacement, &displacement_fes},
|
||||
};
|
||||
|
||||
auto parameters = std::vector
|
||||
{
|
||||
FieldDescriptor{Coordinates, &mesh_fes},
|
||||
FieldDescriptor{InternalState, &internal_state.space}
|
||||
};
|
||||
|
||||
momentum =
|
||||
std::make_shared<DifferentiableOperator>(solutions, parameters, *mesh);
|
||||
momentum->DisableTensorProductStructure();
|
||||
|
||||
mfem::tuple inputs{Gradient<Displacement>{}, Gradient<Coordinates>{}, None<InternalState>{}, Weight{}};
|
||||
mfem::tuple outputs{Gradient<Displacement>{}};
|
||||
|
||||
auto momentum_qf = MomentumRefStateQFunction<Material, DIMENSION> {.material = material};
|
||||
auto derivatives = std::integer_sequence<size_t, Displacement> {};
|
||||
Array<int> solid_domain_attr(mesh->attributes.Max());
|
||||
solid_domain_attr[0] = 1;
|
||||
momentum->AddDomainIntegrator(
|
||||
momentum_qf, inputs, outputs, displacement_ir, solid_domain_attr, derivatives);
|
||||
}
|
||||
|
||||
{
|
||||
Vector g(DIMENSION);
|
||||
g = 0.0;
|
||||
|
||||
ParLinearForm body_force_lf(&displacement_fes);
|
||||
body_force_coef = new VectorConstantCoefficient(g);
|
||||
auto integ = new VectorDomainLFIntegrator(*body_force_coef);
|
||||
integ->SetIntRule(&displacement_ir);
|
||||
body_force_lf.AddDomainIntegrator(integ);
|
||||
body_force_lf.Assemble();
|
||||
body_force_lf.ParallelAssemble(body_force);
|
||||
}
|
||||
}
|
||||
|
||||
void Mult(const Vector &displacement, Vector &r) const override
|
||||
{
|
||||
momentum->SetParameters({mesh_nodes, &internal_state});
|
||||
momentum->Mult(displacement, r);
|
||||
r -= body_force;
|
||||
r.SetSubVector(displacement_ess_tdof, 0.0);
|
||||
}
|
||||
|
||||
void Reaction(const Vector &displacement, Vector &r) const
|
||||
{
|
||||
momentum->SetParameters({mesh_nodes, &internal_state});
|
||||
momentum->Mult(displacement, r);
|
||||
r -= body_force;
|
||||
r.Neg();
|
||||
}
|
||||
|
||||
Operator &GetGradient(const Vector &x) const override
|
||||
{
|
||||
jacobian_operator = std::make_shared<ElasticityJacobianOperator>(this, x);
|
||||
return *jacobian_operator;
|
||||
|
||||
// fd_jacobian = std::make_shared<FDJacobian>(*this, x);
|
||||
// return *fd_jacobian;
|
||||
}
|
||||
|
||||
real_t density;
|
||||
std::shared_ptr<DifferentiableOperator> momentum;
|
||||
mutable std::shared_ptr<HypreParMatrix> A;
|
||||
VectorConstantCoefficient *body_force_coef = nullptr;
|
||||
Vector body_force;
|
||||
|
||||
ParGridFunction *mesh_nodes;
|
||||
|
||||
const Array<int> displacement_ess_tdof;
|
||||
|
||||
ParFiniteElementSpace &displacement_fes;
|
||||
IntegrationRule displacement_ir;
|
||||
|
||||
ParametricFunction& internal_state;
|
||||
|
||||
mutable std::shared_ptr<ElasticityJacobianOperator> jacobian_operator;
|
||||
mutable std::shared_ptr<FDJacobian> fd_jacobian;
|
||||
};
|
||||
|
||||
|
||||
class InternalStateUpdater : public Operator
|
||||
{
|
||||
public:
|
||||
|
||||
static constexpr int Displacement = 0;
|
||||
static constexpr int Coordinates = 1;
|
||||
static constexpr int InternalState = 2;
|
||||
|
||||
template <typename Material>
|
||||
InternalStateUpdater(ParFiniteElementSpace &displacement_fes,
|
||||
const IntegrationRule &displacement_ir,
|
||||
ParametricFunction &internal_state,
|
||||
Material material) :
|
||||
Operator(displacement_fes.GetTrueVSize()),
|
||||
displacement_fes(displacement_fes),
|
||||
displacement_ir(displacement_ir),
|
||||
internal_state(internal_state)
|
||||
{
|
||||
auto mesh = displacement_fes.GetParMesh();
|
||||
mesh_nodes = static_cast<ParGridFunction*>(mesh->GetNodes());
|
||||
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
|
||||
|
||||
auto solutions = std::vector
|
||||
{
|
||||
FieldDescriptor{Displacement, &displacement_fes}
|
||||
};
|
||||
|
||||
auto parameters = std::vector
|
||||
{
|
||||
FieldDescriptor{Coordinates, &mesh_fes},
|
||||
FieldDescriptor{InternalState, &internal_state.space}
|
||||
};
|
||||
|
||||
op = std::make_shared<DifferentiableOperator>(solutions, parameters, *mesh);
|
||||
op->DisableTensorProductStructure();
|
||||
|
||||
mfem::tuple inputs{Gradient<Displacement>{}, Gradient<Coordinates>{}, None<InternalState>{}, Weight{}};
|
||||
mfem::tuple outputs{None<InternalState>{}};
|
||||
|
||||
auto qfunction = InternalStateQFunction<Material, DIMENSION> {.material = material};
|
||||
// just a placeholder for now. We want vjps wrt both displacement and old internal state eventually
|
||||
auto derivatives = std::integer_sequence<size_t, Displacement> {};
|
||||
Array<int> solid_domain_attr(mesh->attributes.Max());
|
||||
solid_domain_attr[0] = 1;
|
||||
op->AddDomainIntegrator(
|
||||
qfunction, inputs, outputs, displacement_ir, solid_domain_attr, derivatives);
|
||||
}
|
||||
|
||||
void Mult(const Vector &displacement, Vector& internal_state_new) const override
|
||||
{
|
||||
op->SetParameters({mesh_nodes, &internal_state});
|
||||
op->Mult(displacement, internal_state_new);
|
||||
}
|
||||
|
||||
void VjpDisplacement(ParGridFunction &u, Vector& internal_state_old,
|
||||
Vector& internal_state_new_bar, Vector& displacement_bar) const
|
||||
{
|
||||
// u, internal_state_old, internal_state_new_bar should be const
|
||||
out << "Sizes " << "u " << u.Size() << ", qold " << internal_state_old.Size() <<
|
||||
", qbar " << internal_state_new_bar.Size() << ", ubar " <<
|
||||
displacement_bar.Size() << std::endl;
|
||||
auto grad_op = op->GetDerivative(Displacement, {&u}, {mesh_nodes, &internal_state_old});
|
||||
out << "grad_op " << grad_op->Height() << " " << grad_op->Width() << std::endl;
|
||||
out << "grad_op^T " << grad_op->Width() << " " << grad_op->Height() <<
|
||||
std::endl;
|
||||
grad_op->MultTranspose(internal_state_new_bar, displacement_bar);
|
||||
}
|
||||
|
||||
ParGridFunction *mesh_nodes;
|
||||
ParFiniteElementSpace &displacement_fes;
|
||||
std::shared_ptr<DifferentiableOperator> op;
|
||||
IntegrationRule displacement_ir;
|
||||
ParametricFunction& internal_state;
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
constexpr int dim = 2;
|
||||
|
||||
Mpi::Init();
|
||||
|
||||
const char* device_config = "cpu";
|
||||
int polynomial_order = 1;
|
||||
int ir_order = 2;
|
||||
int refinements = 0;
|
||||
int nonlinear_solver_type = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&polynomial_order, "-o", "--order", "");
|
||||
args.AddOption(&refinements, "-r", "--refinements", "");
|
||||
args.AddOption(&ir_order, "-iro", "--integration-rule-order", "");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&nonlinear_solver_type, "-nls", "--nonlinear-solver", "");
|
||||
args.ParseCheck();
|
||||
|
||||
Device device(device_config);
|
||||
if (Mpi::Root() == 0)
|
||||
{
|
||||
device.Print();
|
||||
}
|
||||
|
||||
out << std::setprecision(8);
|
||||
|
||||
Mesh mesh_serial = Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL,
|
||||
false, 1.0, 0.1);
|
||||
mesh_serial.EnsureNodes();
|
||||
auto mesh_beam = ParMesh(MPI_COMM_WORLD, mesh_serial);
|
||||
|
||||
out << "#el: " << mesh_beam.GetNE() << "\n";
|
||||
|
||||
H1_FECollection displacement_fec(polynomial_order, dim);
|
||||
ParFiniteElementSpace displacement_fes(&mesh_beam, &displacement_fec, dim);
|
||||
|
||||
HYPRE_BigInt global_size = displacement_fes.GlobalTrueVSize();
|
||||
if (Mpi::Root())
|
||||
{
|
||||
out << "Number of unknowns: " << global_size << "\n";
|
||||
}
|
||||
|
||||
const IntegrationRule &displacement_ir =
|
||||
IntRules.Get(displacement_fes.GetFE(0)->GetGeomType(),
|
||||
2 * ir_order + displacement_fes.GetFE(0)->GetOrder());
|
||||
|
||||
constexpr int n_internal_state_variables = 10;
|
||||
ParametricSpace internal_state_space(dim, n_internal_state_variables,
|
||||
displacement_ir.GetNPoints(),
|
||||
n_internal_state_variables*displacement_ir.GetNPoints()*mesh_beam.GetNE());
|
||||
|
||||
ParametricFunction internal_state(internal_state_space);
|
||||
internal_state = 0.0;
|
||||
ParametricFunction internal_state_old(internal_state_space);
|
||||
internal_state_old = 0.0;
|
||||
|
||||
Array<int> bdr_attr_is_ess(mesh_beam.bdr_attributes.Max());
|
||||
Array<int> displacement_ess_tdof;
|
||||
Array<int> bc_tdof;
|
||||
|
||||
bdr_attr_is_ess = 0;
|
||||
bdr_attr_is_ess[0] = 1;
|
||||
displacement_fes.GetEssentialTrueDofs(bdr_attr_is_ess, bc_tdof, 1);
|
||||
for (auto td : bc_tdof) { displacement_ess_tdof.Append(td); };
|
||||
|
||||
bdr_attr_is_ess = 0;
|
||||
bdr_attr_is_ess[3] = 1;
|
||||
displacement_fes.GetEssentialTrueDofs(bdr_attr_is_ess, bc_tdof, 0);
|
||||
for (auto td : bc_tdof) { displacement_ess_tdof.Append(td); };
|
||||
|
||||
bdr_attr_is_ess = 0;
|
||||
bdr_attr_is_ess[1] = 1;
|
||||
displacement_fes.GetEssentialTrueDofs(bdr_attr_is_ess, bc_tdof, 0);
|
||||
for (auto td : bc_tdof) { displacement_ess_tdof.Append(td); };
|
||||
|
||||
ParGridFunction u(&displacement_fes);
|
||||
u = 0.0;
|
||||
|
||||
using Material = J2SmallStrain; // StVenantKirchhoff
|
||||
Material material{.E = 1000.0, .nu = 0.25, .sigma_y = 0.53333, .Hi = 40.0, .density = 1.0};
|
||||
// Material material{.mu = 0.5e6, .nu = 0.4};
|
||||
|
||||
ElasticityOperator elasticity(displacement_fes, displacement_ess_tdof,
|
||||
displacement_ir, internal_state, material);
|
||||
|
||||
CGSolver solver(MPI_COMM_WORLD);
|
||||
solver.SetAbsTol(0.0);
|
||||
solver.SetRelTol(1e-10);
|
||||
solver.SetMaxIter(1000);
|
||||
solver.SetPrintLevel(2);
|
||||
|
||||
std::shared_ptr<NewtonSolver> nonlinear_solver;
|
||||
if (nonlinear_solver_type == 0)
|
||||
{
|
||||
nonlinear_solver = std::make_shared<NewtonSolver>(MPI_COMM_WORLD);
|
||||
}
|
||||
// else if (nonlinear_solver_type == 1)
|
||||
// {
|
||||
// nonlinear_solver = std::make_shared<KINSolver>(MPI_COMM_WORLD, KIN_LINESEARCH);
|
||||
// }
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("invalid nonlinear solver type");
|
||||
}
|
||||
nonlinear_solver->SetOperator(elasticity);
|
||||
nonlinear_solver->SetRelTol(1e-9);
|
||||
nonlinear_solver->SetMaxIter(25);
|
||||
nonlinear_solver->SetSolver(solver);
|
||||
nonlinear_solver->SetPrintLevel(1);
|
||||
|
||||
// variables for output
|
||||
QuadratureSpace output_internal_state_space(mesh_beam, displacement_ir);
|
||||
QuadratureFunction output_internal_state(&output_internal_state_space,
|
||||
internal_state.GetData(), material.n_internal_states);
|
||||
Vector r(displacement_fes.GetTrueVSize());
|
||||
ParGridFunction reaction(&displacement_fes);
|
||||
Vector end_forces_x(bc_tdof.Size());
|
||||
|
||||
ParaViewDataCollection dc("dfem_plasticity", &mesh_beam);
|
||||
dc.SetHighOrderOutput(true);
|
||||
dc.SetLevelsOfDetail(1);
|
||||
dc.RegisterField("displacement", &u);
|
||||
dc.RegisterField("reaction", &reaction);
|
||||
dc.RegisterQField("internal_state", &output_internal_state);
|
||||
dc.SetCycle(0);
|
||||
dc.Save();
|
||||
|
||||
InternalStateUpdater internal_state_update(displacement_fes, displacement_ir,
|
||||
internal_state, material);
|
||||
//Vector q(internal_state_space.GetTotalSize());
|
||||
|
||||
auto applied_displacement = [](double t) { return 1.2e-2*t; };
|
||||
|
||||
real_t time = 0.0;
|
||||
std::ofstream history_file("history_output.csv");
|
||||
history_file << applied_displacement(time) << " " << 0.0 << std::endl;
|
||||
|
||||
Vector zero, x(displacement_fes.GetTrueVSize());
|
||||
|
||||
constexpr int max_cycles = 3;
|
||||
const real_t dt = 1.0/(max_cycles - 1);
|
||||
for (int cycle = 1; cycle < max_cycles; cycle++)
|
||||
{
|
||||
time += dt;
|
||||
out << "-------------------------------------------" << std::endl;
|
||||
out << "TIME STEP " << cycle << std::endl;
|
||||
out << "t = " << time << std::endl;
|
||||
|
||||
real_t ubc = applied_displacement(time);
|
||||
u.SetSubVector(bc_tdof, ubc);
|
||||
|
||||
u.GetTrueDofs(x);
|
||||
nonlinear_solver->Mult(zero, x);
|
||||
u.SetFromTrueDofs(x);
|
||||
|
||||
// update internal variables
|
||||
internal_state_old.Set(1.0, internal_state);
|
||||
internal_state_update.Mult(u, internal_state);
|
||||
|
||||
// Compute reactions
|
||||
elasticity.Reaction(x, r);
|
||||
reaction.SetFromTrueDofs(r);
|
||||
reaction.GetSubVector(bc_tdof, end_forces_x);
|
||||
real_t force = -end_forces_x.Sum();
|
||||
out << "u = " << applied_displacement(time) << ", Force = " << force <<
|
||||
std::endl;
|
||||
history_file << applied_displacement(time) << " " << force << std::endl;
|
||||
|
||||
output_internal_state = internal_state;
|
||||
|
||||
dc.SetCycle(cycle);
|
||||
dc.SetTime(time);
|
||||
dc.Save();
|
||||
}
|
||||
|
||||
// try to use the derivative to see if it works
|
||||
ParametricFunction internal_state_bar(internal_state_space);
|
||||
internal_state_bar = 1.0;
|
||||
//ParGridFunction u_bar(displacement_fes);
|
||||
Vector u_bar(displacement_fes.GetTrueVSize());
|
||||
internal_state_update.VjpDisplacement(u, internal_state_old, internal_state_bar,
|
||||
u_bar);
|
||||
|
||||
pretty_print(u_bar);
|
||||
|
||||
history_file.close();
|
||||
return 0;
|
||||
}
|
||||
@@ -68,6 +68,9 @@ endif
|
||||
ifeq ($(MFEM_USE_CALIPER),YES)
|
||||
SUBDIRS += caliper
|
||||
endif
|
||||
ifeq ($(MFEM_USE_ENZYME),YES)
|
||||
SUBDIRS += dfem
|
||||
endif
|
||||
|
||||
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
|
||||
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+2
-9
@@ -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,7 +161,6 @@ set(SRCS
|
||||
transfer.cpp
|
||||
hyperbolic.cpp
|
||||
integrator.cpp
|
||||
bounds.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
@@ -187,9 +183,8 @@ set(HDRS
|
||||
dfem/doperator.hpp
|
||||
dfem/fieldoperator.hpp
|
||||
dfem/integrate.hpp
|
||||
dfem/parameterspace.hpp
|
||||
dfem/qfunction_apply.hpp
|
||||
dfem/qfunction_transform.hpp
|
||||
dfem/parametricspace.hpp
|
||||
dfem/qfunction.hpp
|
||||
dfem/tuple.hpp
|
||||
dfem/util.hpp
|
||||
eltrans.hpp
|
||||
@@ -249,7 +244,6 @@ set(HDRS
|
||||
nonlinearform_ext.hpp
|
||||
nonlininteg.hpp
|
||||
qfunction.hpp
|
||||
qinterp/det.hpp
|
||||
qinterp/eval.hpp
|
||||
qinterp/eval_hdiv.hpp
|
||||
qinterp/grad.hpp
|
||||
@@ -276,7 +270,6 @@ set(HDRS
|
||||
transfer.hpp
|
||||
hyperbolic.hpp
|
||||
integrator.hpp
|
||||
bounds.hpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_SIDRE)
|
||||
|
||||
@@ -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
-60
@@ -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)
|
||||
{
|
||||
@@ -1275,22 +1280,6 @@ void BilinearForm::Update(FiniteElementSpace *nfes)
|
||||
height = width = fes->GetVSize();
|
||||
|
||||
if (ext) { ext->Update(); }
|
||||
for (int k = 0; k < domain_integs.Size(); ++k)
|
||||
{
|
||||
domain_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < interior_face_integs.Size(); ++k)
|
||||
{
|
||||
interior_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_face_integs[k]->Update();
|
||||
}
|
||||
}
|
||||
|
||||
void BilinearForm::SetDiagonalPolicy(DiagonalPolicy policy)
|
||||
@@ -1541,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();
|
||||
@@ -1563,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());
|
||||
@@ -1584,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);
|
||||
}
|
||||
}
|
||||
@@ -1612,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());
|
||||
@@ -1634,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);
|
||||
}
|
||||
}
|
||||
@@ -2353,31 +2348,6 @@ void MixedBilinearForm::Update()
|
||||
height = test_fes->GetVSize();
|
||||
width = trial_fes->GetVSize();
|
||||
if (ext) { ext->Update(); }
|
||||
|
||||
for (int k = 0; k < domain_integs.Size(); ++k)
|
||||
{
|
||||
domain_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < interior_face_integs.Size(); ++k)
|
||||
{
|
||||
interior_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_integs.Size(); ++k)
|
||||
{
|
||||
boundary_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < trace_face_integs.Size(); ++k)
|
||||
{
|
||||
trace_face_integs[k]->Update();
|
||||
}
|
||||
for (int k = 0; k < boundary_trace_face_integs.Size(); ++k)
|
||||
{
|
||||
boundary_trace_face_integs[k]->Update();
|
||||
}
|
||||
}
|
||||
|
||||
MixedBilinearForm::~MixedBilinearForm()
|
||||
@@ -2437,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();
|
||||
@@ -2459,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());
|
||||
@@ -2481,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,11 +21,6 @@ using namespace std;
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void BilinearFormIntegrator::Update()
|
||||
{
|
||||
// default no-op
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssemblePA(fes)\n"
|
||||
@@ -126,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"
|
||||
@@ -144,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"
|
||||
@@ -436,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++)
|
||||
@@ -452,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++)
|
||||
@@ -3465,12 +3431,6 @@ real_t ElasticityIntegrator::ComputeFluxEnergy(const FiniteElement &fluxelem,
|
||||
return energy;
|
||||
}
|
||||
|
||||
void DGTraceIntegrator::Update()
|
||||
{
|
||||
qspace[0].reset();
|
||||
qspace[1].reset();
|
||||
}
|
||||
|
||||
void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
|
||||
+10
-37
@@ -23,8 +23,6 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
class QuadratureSpace;
|
||||
class FaceQuadratureSpace;
|
||||
|
||||
/// Abstract base class BilinearFormIntegrator
|
||||
class BilinearFormIntegrator : public NonlinearFormIntegrator
|
||||
@@ -46,10 +44,6 @@ public:
|
||||
// make sense for the action of the nonlinear operator (but they all make
|
||||
// sense for its Jacobian).
|
||||
|
||||
/// Signal this integrator that something about either the trial or test space has changed.
|
||||
virtual void Update();
|
||||
|
||||
|
||||
/// Method defining partial assembly.
|
||||
/** The result of the partial assembly is stored internally so that it can be
|
||||
used later in the methods AddMultPA() and AddMultTransposePA(). */
|
||||
@@ -84,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;
|
||||
|
||||
@@ -98,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. */
|
||||
@@ -506,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;
|
||||
@@ -818,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 );
|
||||
}
|
||||
|
||||
@@ -890,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 );
|
||||
}
|
||||
|
||||
@@ -925,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 );
|
||||
}
|
||||
|
||||
@@ -1606,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 );
|
||||
}
|
||||
|
||||
@@ -1641,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 );
|
||||
}
|
||||
|
||||
@@ -1675,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 );
|
||||
}
|
||||
|
||||
@@ -1766,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 );
|
||||
}
|
||||
|
||||
@@ -1799,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 );
|
||||
}
|
||||
@@ -1838,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
|
||||
);
|
||||
@@ -1979,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 );
|
||||
}
|
||||
|
||||
@@ -2334,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;
|
||||
@@ -2437,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);
|
||||
@@ -2838,7 +2816,6 @@ 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; }
|
||||
@@ -2956,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,
|
||||
@@ -3317,7 +3293,6 @@ protected:
|
||||
VectorCoefficient *u;
|
||||
real_t alpha, beta;
|
||||
// PA extension
|
||||
std::unique_ptr<FaceQuadratureSpace> qspace[2];
|
||||
Vector pa_data;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const FaceGeometricFactors *geom; ///< Not owned
|
||||
@@ -3340,8 +3315,6 @@ public:
|
||||
real_t a, real_t b)
|
||||
{ rho = &rho_; u = &u_; alpha = a; beta = b; }
|
||||
|
||||
void Update() override;
|
||||
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
|
||||
-715
@@ -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
@@ -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
|
||||
+2
-7
@@ -240,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;
|
||||
}
|
||||
|
||||
@@ -252,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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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> ¶meters,
|
||||
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
|
||||
+743
-340
File diff suppressed because it is too large
Load Diff
+59
-64
@@ -10,74 +10,51 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem::future
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// @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>
|
||||
class None : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Identity() : FieldOperator<FIELD_ID>() {}
|
||||
constexpr None() : FieldOperator<FIELD_ID>() {}
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_identity_fop : std::false_type {};
|
||||
struct is_none_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_identity_fop<Identity<FIELD_ID>> : std::true_type {};
|
||||
struct is_none_fop<None<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct DisableAD
|
||||
{
|
||||
T& operator()() const { return fop; }
|
||||
T fop;
|
||||
};
|
||||
|
||||
/// @brief Weight FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that this field contains the quadrature
|
||||
/// point weights.
|
||||
class Weight : public FieldOperator<-1>
|
||||
{
|
||||
public:
|
||||
@@ -85,15 +62,17 @@ public:
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_weight_fop : std::false_type {};
|
||||
struct is_weight_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_weight_fop<Weight> : std::true_type {};
|
||||
struct is_weight_fop<Weight>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
/// @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>
|
||||
{
|
||||
@@ -102,15 +81,23 @@ public:
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_value_fop : std::false_type {};
|
||||
struct is_value_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_value_fop<Value<FIELD_ID>> : std::true_type {};
|
||||
struct is_value_fop<Value<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_value_fop<DisableAD<T>>
|
||||
{
|
||||
static const bool value = is_value_fop<T>::value;
|
||||
};
|
||||
|
||||
/// @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>
|
||||
{
|
||||
@@ -119,26 +106,34 @@ public:
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_gradient_fop : std::false_type {};
|
||||
struct is_gradient_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_gradient_fop<Gradient<FIELD_ID>> : std::true_type {};
|
||||
struct is_gradient_fop<Gradient<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
/// @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>
|
||||
class One : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Sum() : FieldOperator<FIELD_ID>() {};
|
||||
constexpr One() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_sum_fop : std::false_type {};
|
||||
struct is_one_fop
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_sum_fop<Sum<FIELD_ID>> : std::true_type {};
|
||||
struct is_one_fop<One<FIELD_ID>>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
} // namespace mfem::future
|
||||
} // namespace mfem
|
||||
|
||||
+46
-48
@@ -12,20 +12,19 @@
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
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,
|
||||
DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
auto B = dtq.B;
|
||||
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)
|
||||
@@ -36,7 +35,7 @@ void map_quadrature_data_to_fields_impl(
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * f(vd, 0, qp);
|
||||
@@ -54,7 +53,7 @@ void map_quadrature_data_to_fields_impl(
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
@@ -66,7 +65,7 @@ void map_quadrature_data_to_fields_impl(
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sum_fop<std::decay_t<output_t>>::value)
|
||||
else if constexpr (is_one_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.
|
||||
@@ -77,7 +76,7 @@ void map_quadrature_data_to_fields_impl(
|
||||
y(0, 0) += cc(i);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
else if constexpr (is_none_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const auto vdim = output.vdim;
|
||||
@@ -98,14 +97,14 @@ void map_quadrature_data_to_fields_impl(
|
||||
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,
|
||||
DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &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;
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
@@ -124,7 +123,7 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy) * B(qx, 0, dx);
|
||||
@@ -138,7 +137,7 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qy, dx) * B(qy, 0, dy);
|
||||
@@ -194,36 +193,36 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
else if constexpr (is_none_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);
|
||||
|
||||
// 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)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
yqp(sq, qx, qy) = fqp(sq, qx, qy);
|
||||
}
|
||||
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
|
||||
{
|
||||
@@ -235,14 +234,14 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
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,
|
||||
DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &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;
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
@@ -264,7 +263,7 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
|
||||
@@ -281,7 +280,7 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
@@ -299,7 +298,7 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
acc += s1(qz, dy, dx) * B(qz, 0, dz);
|
||||
@@ -390,7 +389,7 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
else if constexpr (is_none_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
@@ -421,8 +420,8 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
DeviceTensor<2, double> &y,
|
||||
const DeviceTensor<3, double> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
@@ -439,7 +438,6 @@ void map_quadrature_data_to_fields(
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_3d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("dimension not supported"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -447,4 +445,4 @@ void map_quadrature_data_to_fields(
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
}
|
||||
|
||||
+51
-45
@@ -12,7 +12,7 @@
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <typename field_operator_t>
|
||||
@@ -22,11 +22,11 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
@@ -45,7 +45,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
|
||||
@@ -62,7 +62,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dz, dy, qx) * B(qy, 0, dy);
|
||||
@@ -79,7 +79,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
acc += s1(dz, qy, qx) * B(qz, 0, dz);
|
||||
@@ -178,7 +178,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
{
|
||||
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);
|
||||
const int q1d = (int)floor(pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
@@ -193,7 +193,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
|
||||
else if constexpr (is_none_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d * q1d);
|
||||
@@ -201,7 +201,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
|
||||
static_assert(always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
@@ -213,11 +213,11 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
@@ -233,7 +233,7 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, vd);
|
||||
@@ -247,7 +247,7 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dy, qx) * B(qy, 0, dy);
|
||||
@@ -313,7 +313,7 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
{
|
||||
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);
|
||||
const int q1d = (int)floor(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)
|
||||
@@ -325,7 +325,7 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
|
||||
else if constexpr (is_none_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d);
|
||||
@@ -333,7 +333,7 @@ void map_field_to_quadrature_data_tensor_product_2d(
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
|
||||
static_assert(always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
@@ -345,10 +345,10 @@ void map_field_to_quadrature_data(
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights)
|
||||
const DeviceTensor<1, const double> &integration_weights)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
auto B = dtq.B;
|
||||
auto G = dtq.G;
|
||||
if constexpr (is_value_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, dim, num_dof] = B.GetShape();
|
||||
@@ -359,7 +359,7 @@ void map_field_to_quadrature_data(
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * field(dof, vd);
|
||||
@@ -381,7 +381,7 @@ void map_field_to_quadrature_data(
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
double acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += G(qp, d, dof) * field(dof, vd);
|
||||
@@ -391,6 +391,20 @@ void map_field_to_quadrature_data(
|
||||
}
|
||||
}
|
||||
}
|
||||
// else if constexpr (std::is_same_v<field_operator_t, FaceNormal>)
|
||||
// {
|
||||
// auto normal = geometric_factors.normal;
|
||||
// auto [num_qp, dim, num_entities] = normal.GetShape();
|
||||
// auto f = Reshape(&field_qp[0], dim, num_qp);
|
||||
// for (int qp = 0; qp < num_qp; qp++)
|
||||
// {
|
||||
// for (int d = 0; d < dim; d++)
|
||||
// {
|
||||
// f(d, qp) = normal(qp, d, entity_idx);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (std::is_same_v<field_operator_t, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
@@ -400,7 +414,7 @@ void map_field_to_quadrature_data(
|
||||
f(qp) = integration_weights(qp);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<field_operator_t>::value)
|
||||
else if constexpr (is_none_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const int size_on_qp = input.size_on_qp;
|
||||
@@ -413,7 +427,7 @@ void map_field_to_quadrature_data(
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<field_operator_t>,
|
||||
static_assert(always_false<field_operator_t>,
|
||||
"can't map field to quadrature data");
|
||||
|
||||
}
|
||||
@@ -427,33 +441,25 @@ void map_fields_to_quadrature_data(
|
||||
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 DeviceTensor<1, const double> &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),
|
||||
fields_qp[i], dtqmaps[i], fields_e[input_to_field[i]], mfem::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),
|
||||
fields_qp[i], dtqmaps[i], fields_e[input_to_field[i]], mfem::get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else
|
||||
@@ -466,7 +472,7 @@ void map_fields_to_quadrature_data(
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
fields_qp[i], dtqmaps[i], fields_e[input_to_field[i]], mfem::get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
});
|
||||
@@ -479,7 +485,7 @@ void map_field_to_quadrature_data_conditional(
|
||||
const DeviceTensor<1> &field_e,
|
||||
const DofToQuadMap &dtqmap,
|
||||
field_operator_t &fop,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const bool &condition,
|
||||
const int &dimension,
|
||||
@@ -512,10 +518,10 @@ 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<DeviceTensor<1, const double>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const bool &use_sum_factorization = false)
|
||||
@@ -523,7 +529,7 @@ void map_fields_to_quadrature_data_conditional(
|
||||
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,
|
||||
fields_qp[i], fields_e[i], dtqmaps[i], mfem::get<i>(fops), integration_weights,
|
||||
scratch_mem, conditions[i], use_sum_factorization);
|
||||
});
|
||||
}
|
||||
@@ -535,7 +541,7 @@ void map_direction_to_quadrature_data_conditional(
|
||||
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 DeviceTensor<1, const double> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const int &dimension,
|
||||
@@ -550,20 +556,20 @@ void map_direction_to_quadrature_data_conditional(
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
directions_qp[i], dtqmaps[i], direction_e, mfem::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),
|
||||
directions_qp[i], dtqmaps[i], direction_e, mfem::get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
directions_qp[i], dtqmaps[i], direction_e, mfem::get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ParametricSpace
|
||||
{
|
||||
|
||||
public:
|
||||
/// spatial_dim is the dimension of the spatial domain (e.g. 2 for 2D)
|
||||
/// local_size is the size of the data on a single quadrature point
|
||||
/// element_size is the size of the data on an element divided by vdim
|
||||
/// total_size is the size of the data for all elements
|
||||
ParametricSpace(int spatial_dim, int local_size, int element_size,
|
||||
int total_size) :
|
||||
spatial_dim(spatial_dim),
|
||||
local_size(local_size),
|
||||
element_size(element_size),
|
||||
total_size(total_size),
|
||||
identity(total_size)
|
||||
{
|
||||
// dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
|
||||
dtq.ndof = element_size;
|
||||
dtq.nqpt = dtq.ndof;
|
||||
}
|
||||
|
||||
ParametricSpace(int local_size) :
|
||||
local_size(local_size),
|
||||
element_size(local_size),
|
||||
total_size(local_size),
|
||||
identity(local_size)
|
||||
{
|
||||
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
|
||||
dtq.nqpt = dtq.ndof;
|
||||
}
|
||||
|
||||
ParametricSpace(int spatial_dim, int local_size, int element_size,
|
||||
int total_size, int d1d, int q1d) :
|
||||
spatial_dim(spatial_dim),
|
||||
local_size(local_size),
|
||||
element_size(element_size),
|
||||
total_size(total_size),
|
||||
identity(total_size)
|
||||
{
|
||||
dtq.ndof = d1d;
|
||||
dtq.nqpt = q1d;
|
||||
}
|
||||
|
||||
int Dimension() const
|
||||
{
|
||||
return spatial_dim;
|
||||
}
|
||||
|
||||
int GetLocalSize() const
|
||||
{
|
||||
return local_size;
|
||||
}
|
||||
|
||||
int GetElementSize() const
|
||||
{
|
||||
return element_size;
|
||||
}
|
||||
|
||||
int GetTotalSize() const
|
||||
{
|
||||
return total_size;
|
||||
}
|
||||
|
||||
const DofToQuad &GetDofToQuad() const
|
||||
{
|
||||
return dtq;
|
||||
}
|
||||
|
||||
const Operator *GetProlongation() const
|
||||
{
|
||||
return &identity;
|
||||
}
|
||||
|
||||
const Operator *GetRestriction() const
|
||||
{
|
||||
return &identity;
|
||||
}
|
||||
|
||||
private:
|
||||
int spatial_dim;
|
||||
|
||||
// Hint for the local dimension. E.g. the size on the quadrature point or vdim.
|
||||
int local_size;
|
||||
|
||||
// Size of the data on an element
|
||||
int element_size;
|
||||
|
||||
int total_size;
|
||||
|
||||
IdentityOperator identity;
|
||||
|
||||
DofToQuad dtq;
|
||||
};
|
||||
|
||||
class ParametricFunction : public Vector
|
||||
{
|
||||
public:
|
||||
ParametricFunction(ParametricSpace &space) :
|
||||
Vector(space.GetTotalSize()),
|
||||
space(space)
|
||||
{}
|
||||
|
||||
ParametricSpace &space;
|
||||
|
||||
using Vector::operator=;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
// 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
|
||||
{
|
||||
|
||||
template <typename func_t, typename... arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto qfunction_wrapper(const func_t &f, arg_ts &&...args)
|
||||
{
|
||||
return f(args...);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(const T0 &, T1 &)
|
||||
{
|
||||
static_assert(always_false<T0, T1>,
|
||||
"process_kf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
internal::tensor<T> &arg)
|
||||
{
|
||||
arg(0) = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
internal::tensor<T, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i) = u(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
internal::tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
process_kf_arg(u_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_fields, typename kf_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_args(
|
||||
const std::array<DeviceTensor<2>, num_fields> &u,
|
||||
kf_args &args,
|
||||
const int &qp)
|
||||
{
|
||||
for_constexpr<mfem::tuple_size<kf_args>::value>([&](auto i)
|
||||
{
|
||||
process_kf_arg(u[i], mfem::get<i>(args), qp);
|
||||
// out << mfem::get<i>(args) << ", ";
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
Vector process_kf_result(T0, T1)
|
||||
{
|
||||
static_assert(always_false<T0, T1>,
|
||||
"process_kf_result not implemented for result type");
|
||||
return Vector{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const double &x)
|
||||
{
|
||||
r(0) = x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<T> &x)
|
||||
{
|
||||
r(0) = x(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<T, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const internal::tensor<T, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
double &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_kf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
internal::tensor<double, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename args_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel(
|
||||
DeviceTensor<1, double> &f_qp,
|
||||
const qfunc_t &qfunc,
|
||||
args_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
int qp)
|
||||
{
|
||||
process_kf_args(u, args, qp);
|
||||
process_kf_result(f_qp, mfem::get<0>(mfem::apply(qfunc, args)));
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
// Version for active function arguments only
|
||||
//
|
||||
// This is an Enzyme regression and can be removed in later versions.
|
||||
template <typename 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(mfem::get<Is>(args))...>, enzyme_const,
|
||||
(void *)&qfunc, enzyme_dup, &mfem::get<Is>(args)..., enzyme_interleave,
|
||||
&mfem::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(mfem::get<Is>(args))...,
|
||||
decltype(mfem::get<Js>(inactive_args))...>,
|
||||
enzyme_const, (void *)&qfunc, enzyme_dup, &mfem::get<Is>(args)...,
|
||||
enzyme_const, &mfem::get<Js>(inactive_args)..., enzyme_interleave,
|
||||
&mfem::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<
|
||||
mfem::tuple_size<std::remove_reference_t<arg_ts>>::value> {};
|
||||
|
||||
auto inactive_arg_indices = std::make_index_sequence<
|
||||
mfem::tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
|
||||
|
||||
return fwddiff_apply_enzyme_indexed(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, double> &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)
|
||||
{
|
||||
// out << "\nargs: ";
|
||||
process_kf_args(u, args, qp_idx);
|
||||
// out << "\nshadow args: ";
|
||||
process_kf_args(v, shadow_args, qp_idx);
|
||||
// out << "\n";
|
||||
process_kf_result(f_qp,
|
||||
mfem::get<0>(fwddiff_apply_enzyme(qfunc, args, shadow_args, mfem::tuple<> {})));
|
||||
}
|
||||
#endif // MFEM_USE_ENZYME
|
||||
|
||||
} // namespace mfem
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,49 @@
|
||||
export LC_USER=andrej1
|
||||
module load rocmcc/6.3.1-cce-19.0.0-magic cmake/3.29.2
|
||||
|
||||
export MPICH_CC=amdclang
|
||||
export MPICH_CXX=amdclang++
|
||||
export ROCM_PATH=/opt/rocm-6.3.1
|
||||
export LLVM_DIR=$ROCM_PATH/lib/llvm
|
||||
export MPI_DIR=/usr/tce/packages/cray-mpich/cray-mpich-8.1.32-rocmcc-6.3.1-cce-19.0.0-magic
|
||||
|
||||
export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$ROCM_PATH/lib/cmake/hip:$ROCM_PATH/lib/cmake/hipblas:$ROCM_PATH/lib/cmake/hipblas-common:$ROCM_PATH/lib/cmake/hipsparse:$ROCM_PATH/lib/cmake/rocsparse:$ROCM_PATH/lib/cmake/rocrand
|
||||
|
||||
export BASE_DIR=/usr/workspace/$LC_USER/dfem-tuo-magic
|
||||
export LOCAL_DIR=/usr/workspace/$LC_USER/dfem-tuo-magic/local
|
||||
mkdir -p $LOCAL_DIR
|
||||
export PATH=$LOCAL_DIR/bin:$PATH
|
||||
cd $BASE_DIR
|
||||
|
||||
## Enzyme
|
||||
git clone --depth 1 https://github.com/EnzymeAD/Enzyme.git
|
||||
pushd Enzyme/enzyme
|
||||
CC=amdclang CXX=amdclang++ cmake -B build -DLLVM_DIR=$LLVM_DIR -DCMAKE_INSTALL_PREFIX=$LOCAL_DIR
|
||||
cmake --build build -j && cmake --install build
|
||||
popd
|
||||
|
||||
## hypre
|
||||
curl https://github.com/hypre-space/hypre/archive/refs/tags/v2.32.0.tar.gz -o hypre-v2.32.0.tar.gz -L
|
||||
tar xzf hypre-v2.32.0.tar.gz
|
||||
pushd hypre-2.32.0/src
|
||||
CC=mpicc CXX=mpicxx CXXFLAGS="std=c++17 -fPIC" CFLAGS="-fPIC" ROCM_PATH=$ROCM_PATH ./configure --disable-fortran --prefix=$LOCAL_DIR --with-MPI-libs="mpi mpich" --with-MPI-lib-dirs=$MPI_DIR/lib --with-MPI-include=$MPI_DIR/include --enable-shared --with-hip
|
||||
make -j install
|
||||
popd
|
||||
|
||||
## metis
|
||||
curl -OL https://github.com/mfem/tpls/raw/gh-pages/parmetis-4.0.3.tar.gz
|
||||
tar xzf parmetis-4.0.3.tar.gz
|
||||
pushd parmetis-4.0.3
|
||||
cmake -B build -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DGKLIB_PATH=$BASE_DIR/parmetis-4.0.3/metis/GKlib -DMETIS_PATH=$BASE_DIR/parmetis-4.0.3/metis -DCMAKE_INSTALL_PREFIX=$LOCAL_DIR -DSHARED=1 -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx
|
||||
cmake --build build -j && cmake --install build
|
||||
popd
|
||||
pushd parmetis-4.0.3/metis
|
||||
cmake -B build -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DGKLIB_PATH=$BASE_DIR/parmetis-4.0.3/metis/GKlib -DCMAKE_INSTALL_PREFIX=$LOCAL_DIR -DSHARED=1 -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_COMPILER=mpicxx
|
||||
cmake --build build -j && cmake --install build
|
||||
popd
|
||||
|
||||
git clone https://github.com/mfem/mfem.git
|
||||
git switch dfem-phase1-dev
|
||||
pushd mfem
|
||||
CXX=mpicxx cmake -B build-opt -DCMAKE_BUILD_TYPE=Release -DMFEM_USE_HIP=ON -DCMAKE_HIP_ARCHITECTURES="gfx942" -DCMAKE_HIP_PLATFORM="amd"
|
||||
cmake --build build-opt -j
|
||||
@@ -0,0 +1,31 @@
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING
|
||||
"Build type: Debug, Release, RelWithDebInfo, or MinSizeRel." FORCE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
# set(CMAKE_CXX_FLAGS "--save-temps -Rpass-analysis=kernel-resource-usage -mllvm -amdgpu-early-inline-all=true -mllvm -amdgpu-function-calls=false")
|
||||
|
||||
set(MFEM_PRECISION "double" CACHE STRING
|
||||
"Floating-point precision to use: single, or double")
|
||||
|
||||
option(BUILD_SHARED_LIBS "Enable shared library build of MFEM" ON)
|
||||
option(MFEM_USE_MPI "Enable MPI parallel build" ON)
|
||||
option(MFEM_USE_METIS "Enable METIS usage" ${MFEM_USE_MPI})
|
||||
option(MFEM_USE_ENZYME "Enable Enzyme" ON)
|
||||
option(MFEM_USE_HIP "Enable HIP" ON)
|
||||
|
||||
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
|
||||
|
||||
option(MFEM_ENABLE_TESTING ON)
|
||||
|
||||
set(HIP_ARCH "gfx942" CACHE STRING "Target HIP architecture.")
|
||||
|
||||
# Make sure all dirs are absolute
|
||||
set(ENZYME_DIR "/usr/workspace/andrej1/dfem-tuo-magic/local/cmake/Enzyme" CACHE PATH "Path to the Enzyme library.")
|
||||
set(HYPRE_DIR "/usr/workspace/andrej1/dfem-tuo-magic/local" CACHE PATH "Path to the hypre library.")
|
||||
set(METIS_DIR "/usr/workspace/andrej1/dfem-tuo-magic/local" CACHE PATH "Path to the METIS library.")
|
||||
|
||||
set(CMAKE_SKIP_PREPROCESSED_SOURCE_RULES ON) # Skip *.i rules
|
||||
set(CMAKE_SKIP_ASSEMBLY_SOURCE_RULES ON) # Skip *.s rules
|
||||
+21
-53
@@ -12,29 +12,10 @@
|
||||
|
||||
// This is serac's tuple implementation
|
||||
|
||||
#include <ostream>
|
||||
#include "../../config/config.hpp"
|
||||
#include <utility>
|
||||
#include <mfem.hpp>
|
||||
|
||||
// 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
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -232,8 +213,8 @@ struct tuple_size
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size<tuple<Types...>> :
|
||||
std::integral_constant<std::size_t, sizeof...(Types)>
|
||||
struct tuple_size<mfem::tuple<Types...>> :
|
||||
std::integral_constant<std::size_t, sizeof...(Types)>
|
||||
{
|
||||
};
|
||||
|
||||
@@ -245,7 +226,7 @@ struct tuple_size<tuple<Types...>> :
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
static_assert(i < sizeof...(T), "");
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
@@ -282,7 +263,6 @@ MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
MFEM_UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,7 +273,7 @@ MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
static_assert(i < sizeof...(T), "");
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
@@ -330,7 +310,6 @@ MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
MFEM_UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,7 +325,7 @@ MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
static_assert(i < sizeof...(T), "");
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
@@ -584,7 +563,7 @@ MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const double a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a / get<i>(x)...};
|
||||
@@ -601,7 +580,7 @@ MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
|
||||
*/
|
||||
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...>)
|
||||
const double a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / a...};
|
||||
}
|
||||
@@ -613,7 +592,7 @@ MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
|
||||
* @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)
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const double a, const tuple<T...>& x)
|
||||
{
|
||||
return div_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
@@ -626,7 +605,7 @@ MFEM_HOST_DEVICE constexpr auto operator/(const real_t a, const tuple<T...>& x)
|
||||
* @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)
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const double a)
|
||||
{
|
||||
return div_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
@@ -676,7 +655,7 @@ MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const real_t a,
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const double a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a * get<i>(x)...};
|
||||
@@ -693,7 +672,7 @@ MFEM_HOST_DEVICE constexpr auto mult_helper(const real_t a,
|
||||
*/
|
||||
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...>)
|
||||
const double a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * a...};
|
||||
}
|
||||
@@ -705,7 +684,7 @@ MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
|
||||
* @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)
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const double a, const tuple<T...>& x)
|
||||
{
|
||||
return mult_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
@@ -718,7 +697,7 @@ MFEM_HOST_DEVICE constexpr auto operator*(const real_t a, const tuple<T...>& x)
|
||||
* @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)
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const double a)
|
||||
{
|
||||
return mult_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
@@ -732,11 +711,11 @@ MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const real_t a)
|
||||
* @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,
|
||||
auto& print_helper(std::ostream& out, const mfem::tuple<T...>& A,
|
||||
std::integer_sequence<size_t, i...>)
|
||||
{
|
||||
out << "tuple{";
|
||||
(..., (out << (i == 0 ? "" : ", ") << get<i>(A)));
|
||||
(..., (out << (i == 0 ? "" : ", ") << mfem::get<i>(A)));
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
@@ -748,7 +727,7 @@ auto& print_helper(std::ostream& out, const tuple<T...>& A,
|
||||
* @brief print a tuple of values
|
||||
*/
|
||||
template <typename... T>
|
||||
auto& operator<<(std::ostream& out, const tuple<T...>& A)
|
||||
auto& operator<<(std::ostream& out, const mfem::tuple<T...>& A)
|
||||
{
|
||||
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
|
||||
}
|
||||
@@ -849,7 +828,7 @@ struct is_tuple : std::false_type
|
||||
|
||||
/// @overload
|
||||
template <typename... T>
|
||||
struct is_tuple<tuple<T...>> : std::true_type
|
||||
struct is_tuple<mfem::tuple<T...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
@@ -865,21 +844,10 @@ 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...>>
|
||||
struct is_tuple_of_tuples<mfem::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
|
||||
} // namespace mfem
|
||||
|
||||
+559
-597
File diff suppressed because it is too large
Load Diff
+52
-65
@@ -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
@@ -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
|
||||
|
||||
@@ -37,13 +37,6 @@ void DGMassApply(const int e,
|
||||
constexpr bool use_smem = (D1D > 0 && Q1D > 0);
|
||||
constexpr bool ACCUM = false;
|
||||
constexpr int NBZ = 1;
|
||||
|
||||
if (DIM == 1)
|
||||
{
|
||||
PAMassApply1D_Element<ACCUM>(e, NE, B, Bt, pa_data, x, y, d1d, q1d);
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_smem)
|
||||
{
|
||||
// cannot specialize functions below with D1D or Q1D equal to zero
|
||||
@@ -179,43 +172,6 @@ real_t DGMassDot(const int e,
|
||||
return s_dot[0];
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void DGMassBasis1D(const int e,
|
||||
const int NE,
|
||||
const real_t *b_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int d1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
|
||||
const auto b = Reshape(b_, D1D, D1D);
|
||||
const auto x = Reshape(x_, D1D, NE);
|
||||
auto y = Reshape(y_, D1D, NE);
|
||||
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
real_t Y[MD1];
|
||||
|
||||
MFEM_FOREACH_THREAD(i,x,D1D)
|
||||
{
|
||||
real_t val = 0.0;
|
||||
for (int j = 0; j < D1D; ++j)
|
||||
{
|
||||
val += b(i,j)*x(j,e);
|
||||
}
|
||||
Y[i] = val;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i,x,D1D)
|
||||
{
|
||||
y(i,e) = Y[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void DGMassBasis2D(const int e,
|
||||
@@ -313,11 +269,7 @@ void DGMassBasis(const int e,
|
||||
real_t *y_,
|
||||
const int d1d = 0)
|
||||
{
|
||||
if (DIM == 1)
|
||||
{
|
||||
DGMassBasis1D<T_D1D>(e, NE, b_, x_, y_, d1d);
|
||||
}
|
||||
else if (DIM == 2)
|
||||
if (DIM == 2)
|
||||
{
|
||||
DGMassBasis2D<T_D1D>(e, NE, b_, x_, y_, d1d);
|
||||
}
|
||||
|
||||
+24
-16
@@ -16,7 +16,9 @@ namespace mfem
|
||||
|
||||
void DofTransformation::TransformPrimal(real_t *v) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -46,7 +48,9 @@ void DofTransformation::TransformPrimal(real_t *v) const
|
||||
|
||||
void DofTransformation::InvTransformPrimal(real_t *v) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Height();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -76,7 +80,9 @@ void DofTransformation::InvTransformPrimal(real_t *v) const
|
||||
|
||||
void DofTransformation::TransformDual(real_t *v) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -106,7 +112,9 @@ void DofTransformation::TransformDual(real_t *v) const
|
||||
|
||||
void DofTransformation::InvTransformDual(real_t *v) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -134,33 +142,33 @@ void DofTransformation::InvTransformDual(real_t *v) const
|
||||
}
|
||||
}
|
||||
|
||||
void TransformPrimal(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// No action if both transformations are NULL
|
||||
if (!ran_dof_trans.IsIdentity())
|
||||
if (ran_dof_trans)
|
||||
{
|
||||
ran_dof_trans.TransformPrimalCols(elmat);
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
}
|
||||
if (!dom_dof_trans.IsIdentity())
|
||||
if (dom_dof_trans)
|
||||
{
|
||||
dom_dof_trans.TransformDualRows(elmat);
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDual(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// No action if both transformations are NULL
|
||||
if (!ran_dof_trans.IsIdentity())
|
||||
if (ran_dof_trans)
|
||||
{
|
||||
ran_dof_trans.TransformDualCols(elmat);
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
}
|
||||
if (!dom_dof_trans.IsIdentity())
|
||||
if (dom_dof_trans)
|
||||
{
|
||||
dom_dof_trans.TransformDualRows(elmat);
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-9
@@ -201,19 +201,19 @@ public:
|
||||
inline int NumRows() const { return dof_trans_->NumRows(); }
|
||||
inline int Width() const { return dof_trans_->Width(); }
|
||||
inline int NumCols() const { return dof_trans_->NumCols(); }
|
||||
inline bool IsIdentity() const { return !dof_trans_ || dof_trans_->IsIdentity(); }
|
||||
inline bool IsIdentity() const { return dof_trans_->IsIdentity(); }
|
||||
|
||||
/** Transform local DoFs to align with the global DoFs. For example, this
|
||||
transformation can be used to map the local vector computed by
|
||||
FiniteElement::Project() to the transformed vector stored within a
|
||||
GridFunction object. */
|
||||
void TransformPrimal(real_t *v) const;
|
||||
inline void TransformPrimal(Vector &v) const { TransformPrimal(v.GetData()); }
|
||||
inline void TransformPrimal(Vector &v) const
|
||||
{ TransformPrimal(v.GetData()); }
|
||||
|
||||
/// Transform groups of DoFs stored as dense matrices
|
||||
inline void TransformPrimalCols(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformPrimal(V.GetColumn(c));
|
||||
@@ -251,7 +251,6 @@ public:
|
||||
/// Transform rows of a dense matrix containing dual DoFs
|
||||
inline void TransformDualRows(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
Vector row;
|
||||
for (int r=0; r<V.Height(); r++)
|
||||
{
|
||||
@@ -264,7 +263,6 @@ public:
|
||||
/// Transform columns of a dense matrix containing dual DoFs
|
||||
inline void TransformDualCols(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformDual(V.GetColumn(c));
|
||||
@@ -276,16 +274,16 @@ public:
|
||||
computed by a DiscreteInterpolator before copying into a
|
||||
DiscreteLinearOperator.
|
||||
*/
|
||||
void TransformPrimal(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Transform a matrix of dual DoFs entries from different finite element spaces
|
||||
as computed by a BilinearFormIntegrator before summing into a
|
||||
MixedBilinearForm object.
|
||||
*/
|
||||
void TransformDual(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Abstract base class for high-order Nedelec spaces on elements with
|
||||
|
||||
@@ -20,16 +20,6 @@ namespace mfem
|
||||
|
||||
using namespace std;
|
||||
|
||||
DofToQuad DofToQuad::Abs() const
|
||||
{
|
||||
DofToQuad d2q(*this);
|
||||
d2q.B.Abs();
|
||||
d2q.Bt.Abs();
|
||||
d2q.G.Abs();
|
||||
d2q.Gt.Abs();
|
||||
return d2q;
|
||||
}
|
||||
|
||||
FiniteElement::FiniteElement(int D, Geometry::Type G,
|
||||
int Do, int O, int F)
|
||||
: Nodes(Do)
|
||||
|
||||
@@ -219,9 +219,6 @@ public:
|
||||
- #ndof x #nqpt, for H(div) vector elements, or
|
||||
- #ndof x #nqpt x cdim, for H(curl) vector elements. */
|
||||
Array<real_t> Gt;
|
||||
|
||||
/// Returns absolute value of the maps
|
||||
DofToQuad Abs() const;
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
|
||||
+24
-30
@@ -1891,38 +1891,31 @@ L2Pos_PyramidElement::L2Pos_PyramidElement(const int p)
|
||||
|
||||
Index idx;
|
||||
|
||||
if (p == 0)
|
||||
{
|
||||
dof_map[idx(0,0,0,0,0)] = 0;
|
||||
Nodes.IntPoint(0).Set3(0.375, 0.375, 0.25);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int o = 0, k = 0; k <= p; k++)
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
int i1 = p - j - k;
|
||||
int i2 = 0;
|
||||
int i3 = -1;
|
||||
int i4 = j + 1;
|
||||
const int i5 = k;
|
||||
// interior
|
||||
for (int o = 0, k = 0; k <= p; k++)
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
int i1 = p - j - k;
|
||||
int i2 = 0;
|
||||
int i3 = -1;
|
||||
int i4 = j + 1;
|
||||
const int i5 = k;
|
||||
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
for (int i = j + 1; i + k <= p; i++)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
}
|
||||
for (int i = j + 1; i + k <= p; i++)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static method
|
||||
@@ -2204,6 +2197,7 @@ void L2Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
{
|
||||
dshape(it.second, d) = m_dshape(it.first, d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,9 +49,6 @@
|
||||
#include "lor/lor.hpp"
|
||||
#include "dgmassinv.hpp"
|
||||
#include "hyperbolic.hpp"
|
||||
#include "bounds.hpp"
|
||||
|
||||
#include "dfem/doperator.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
|
||||
@@ -1,249 +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_FES_KERNELS_HPP
|
||||
#define MFEM_FES_KERNELS_HPP
|
||||
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
namespace internal
|
||||
{
|
||||
|
||||
///
|
||||
/// Implements matrix-vector multiply $y = A x$ for a sparse matrix composed of
|
||||
/// a sum of smaller dense blocks. There is additional permutation/sign
|
||||
/// information associated with each block. The base class only implements
|
||||
/// helper routines such as computing block widths, index into x, index into y,
|
||||
/// and column in A given sub-block information.
|
||||
/// @sa DerefineMatrixOpMultFunctor
|
||||
///
|
||||
/// @tparam Order vdim ordering for x and y. Note that for Diag = false this is
|
||||
/// ignored for x as x has a special interleaved order.
|
||||
/// @tparam Base used for the curious recurring template pattern (CRTP) so the
|
||||
/// base class can access child class fields without virtual functions
|
||||
/// @tparam Diag true if this corresponds to the diagonal block (coarse element
|
||||
/// and fine element are on our rank), false otherwise (coarse element is on our
|
||||
/// rank, fine element is on a different rank).
|
||||
///
|
||||
template <Ordering::Type Order, class Base, bool Diag = true>
|
||||
struct DerefineMatrixOpFunctorBase;
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byNODES, Base, true>
|
||||
{
|
||||
/// block column indices offsets
|
||||
const int *bcptr;
|
||||
/// column indices
|
||||
const int *cptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const
|
||||
{
|
||||
return bcptr[k + 1] - bcptr[k];
|
||||
}
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = cptr[bcptr[k] + j];
|
||||
if (col < 0)
|
||||
{
|
||||
col = -1 - col;
|
||||
sign = -sign;
|
||||
}
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int) const
|
||||
{
|
||||
return col + vdim * static_cast<const Base *>(this)->width;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return row + vdim * static_cast<const Base *>(this)->height;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byVDIM, Base, true>
|
||||
{
|
||||
/// block column indices offsets
|
||||
const int *bcptr;
|
||||
/// column indices
|
||||
const int *cptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const
|
||||
{
|
||||
return bcptr[k + 1] - bcptr[k];
|
||||
}
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = cptr[bcptr[k] + j];
|
||||
if (col < 0)
|
||||
{
|
||||
col = -1 - col;
|
||||
sign = -sign;
|
||||
}
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int) const
|
||||
{
|
||||
return vdim + col * static_cast<const Base *>(this)->vdims;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return vdim + row * static_cast<const Base *>(this)->vdims;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byNODES, Base, false>
|
||||
{
|
||||
/// receive segment offsets
|
||||
const int *segptr;
|
||||
/// receive segment index
|
||||
const int *rsptr;
|
||||
/// off-diagonal block column offsets
|
||||
const int *coptr;
|
||||
/// off-diagonal block widths
|
||||
const int *bwptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const { return bwptr[k]; }
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = coptr[k] + j;
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int k) const
|
||||
{
|
||||
int tmp = rsptr[k];
|
||||
int segwidth = segptr[tmp + 1] - segptr[tmp];
|
||||
return segptr[tmp] * static_cast<const Base *>(this)->vdims + col +
|
||||
vdim * segwidth;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return row + vdim * static_cast<const Base *>(this)->height;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
struct DerefineMatrixOpFunctorBase<Ordering::byVDIM, Base, false>
|
||||
{
|
||||
/// receive segment offsets
|
||||
const int *segptr;
|
||||
/// receive segment index
|
||||
const int *rsptr;
|
||||
/// off-diagonal block column offsets
|
||||
const int *coptr;
|
||||
/// off-diagonal block widths
|
||||
const int *bwptr;
|
||||
|
||||
int MFEM_HOST_DEVICE BlockWidth(int k) const { return bwptr[k]; }
|
||||
|
||||
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
|
||||
{
|
||||
col = coptr[k] + j;
|
||||
}
|
||||
|
||||
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int k) const
|
||||
{
|
||||
int tmp = rsptr[k];
|
||||
int segwidth = segptr[tmp + 1] - segptr[tmp];
|
||||
return segptr[tmp] * static_cast<const Base *>(this)->vdims + col +
|
||||
vdim * segwidth;
|
||||
}
|
||||
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
|
||||
{
|
||||
return vdim + row * static_cast<const Base *>(this)->vdims;
|
||||
}
|
||||
};
|
||||
|
||||
/// internally used to implement the derefinement operator Mult diagonal
|
||||
/// block
|
||||
template <Ordering::Type Order, bool Atomic, bool Diag = true>
|
||||
struct DerefineMatrixOpMultFunctor
|
||||
: public DerefineMatrixOpFunctorBase<
|
||||
Order, DerefineMatrixOpMultFunctor<Order, Atomic, Diag>, Diag>
|
||||
{
|
||||
const real_t *xptr;
|
||||
real_t *yptr;
|
||||
/// block storage
|
||||
const real_t *bsptr;
|
||||
/// block offsets
|
||||
const int *boptr;
|
||||
/// block row index offsets
|
||||
const int *brptr;
|
||||
/// row indices
|
||||
const int *rptr;
|
||||
|
||||
// number of blocks
|
||||
int nblocks;
|
||||
// number of components
|
||||
int vdims;
|
||||
/// overall operator height (for vdim = 1)
|
||||
int height;
|
||||
/// overall operator width (for vdim = 1)
|
||||
int width;
|
||||
void MFEM_HOST_DEVICE operator()(int kidx) const
|
||||
{
|
||||
int k = kidx % nblocks;
|
||||
int vdim = kidx / nblocks;
|
||||
|
||||
int block_height = brptr[k + 1] - brptr[k];
|
||||
int block_width = this->BlockWidth(k);
|
||||
MFEM_FOREACH_THREAD(i, x, block_height)
|
||||
{
|
||||
int row = rptr[brptr[k] + i];
|
||||
int rsign = 1;
|
||||
if (row < 0)
|
||||
{
|
||||
row = -1 - row;
|
||||
rsign = -1;
|
||||
}
|
||||
if (row < INT_MAX)
|
||||
{
|
||||
// row not marked as unused
|
||||
real_t sum = 0;
|
||||
for (int j = 0; j < block_width; ++j)
|
||||
{
|
||||
int col, sign = rsign;
|
||||
this->Col(j, k, col, sign);
|
||||
sum += sign * bsptr[boptr[k] + i + j * block_height] *
|
||||
xptr[this->IndexX(col, vdim, k)];
|
||||
}
|
||||
#if defined(__CUDA_ARCH__) or defined(__HIP_DEVICE_COMPILE__)
|
||||
if (Atomic)
|
||||
{
|
||||
atomicAdd(yptr + this->IndexY(row, vdim), sum);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
yptr[this->IndexY(row, vdim)] += sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// N is the max block row size (doesn't have to be a power of 2)
|
||||
void Run(int N) const { forall_2D(nblocks * vdims, N, 1, *this); }
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+23
-29
@@ -17,9 +17,6 @@
|
||||
#include "fem.hpp"
|
||||
#include "ceed/interface/util.hpp"
|
||||
|
||||
#include "derefmat_op.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
|
||||
@@ -27,9 +24,9 @@ using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
template <>
|
||||
void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
|
||||
Array<int> &dofs)
|
||||
|
||||
template <> void Ordering::
|
||||
DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim, Array<int> &dofs)
|
||||
{
|
||||
// static method
|
||||
int size = dofs.Size();
|
||||
@@ -43,9 +40,8 @@ void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
|
||||
Array<int> &dofs)
|
||||
template <> void Ordering::
|
||||
DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim, Array<int> &dofs)
|
||||
{
|
||||
// static method
|
||||
int size = dofs.Size();
|
||||
@@ -59,6 +55,7 @@ void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FiniteElementSpace::FiniteElementSpace()
|
||||
: mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
|
||||
ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0),
|
||||
@@ -334,6 +331,7 @@ void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -349,6 +347,7 @@ void FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -1937,7 +1936,6 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
|
||||
DenseMatrix eP;
|
||||
IsoparametricTransformation isotr;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int k = 0; k < mesh_ref->GetNE(); k++)
|
||||
{
|
||||
@@ -1958,10 +1956,10 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
|
||||
subY.SetSize(lP.Height());
|
||||
|
||||
fespace->GetElementDofs(k, dofs, doftrans);
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, dofs);
|
||||
old_elem_dof->GetRow(emb.parent, old_dofs);
|
||||
|
||||
if (doftrans.IsIdentity())
|
||||
if (!doftrans)
|
||||
{
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
@@ -1981,7 +1979,7 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans.SetVDim();
|
||||
doftrans->SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
@@ -1992,10 +1990,10 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
old_DoFTrans.InvTransformPrimal(subX);
|
||||
lP.Mult(subX, subY);
|
||||
doftrans.TransformPrimal(subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
}
|
||||
doftrans.SetVDim(rvdim, fespace->GetOrdering());
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2022,7 +2020,6 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
DenseMatrix eP;
|
||||
IsoparametricTransformation isotr;
|
||||
const FiniteElement *fe = nullptr;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int k = 0; k < mesh_ref->GetNE(); k++)
|
||||
{
|
||||
@@ -2043,10 +2040,10 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
const DenseMatrix &lP = (fespace->IsVariableOrder()) ? eP : localP[geom](
|
||||
emb.matrix);
|
||||
|
||||
fespace->GetElementDofs(k, f_dofs, doftrans);
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, f_dofs);
|
||||
old_elem_dof->GetRow(emb.parent, c_dofs);
|
||||
|
||||
if (doftrans.IsIdentity())
|
||||
if (!doftrans)
|
||||
{
|
||||
subY.SetSize(lP.Width());
|
||||
|
||||
@@ -2077,7 +2074,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans.SetVDim();
|
||||
doftrans->SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
f_dofs.Copy(f_vdofs);
|
||||
@@ -2086,7 +2083,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
doftrans.InvTransformDual(subX);
|
||||
doftrans->InvTransformDual(subX);
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
@@ -2098,7 +2095,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
old_DoFTrans.TransformDual(subYt);
|
||||
y.AddElementVector(c_vdofs, subYt);
|
||||
}
|
||||
doftrans.SetVDim(rvdim, fespace->GetOrdering());
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
@@ -3410,8 +3407,6 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(elem, dofs);
|
||||
@@ -3518,6 +3513,7 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation *FiniteElementSpace::GetElementDofs(int elem,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementDofs(elem, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -3527,8 +3523,6 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(bel, dofs);
|
||||
@@ -3623,6 +3617,7 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation *FiniteElementSpace::GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementDofs(bel, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -4247,11 +4242,7 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
case Mesh::DEREFINE:
|
||||
{
|
||||
BuildConformingInterpolation();
|
||||
#if 0
|
||||
Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
|
||||
#else
|
||||
Th.Reset(new DerefineMatrixOp(*this, old_ndofs, old_elem_dof, old_elem_fos));
|
||||
#endif
|
||||
if (IsVariableOrder())
|
||||
{
|
||||
if (cP && cR_hp)
|
||||
@@ -4285,6 +4276,9 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
void FiniteElementSpace::PRefineAndUpdate(const Array<pRefinement> & refs,
|
||||
bool want_transfer)
|
||||
{
|
||||
MFEM_VERIFY(PRefinementSupported(),
|
||||
"p-refinement is not supported in this space");
|
||||
|
||||
if (want_transfer)
|
||||
{
|
||||
fesPrev.reset(new FiniteElementSpace(mesh, fec, vdim, ordering));
|
||||
|
||||
+31
-44
@@ -113,7 +113,7 @@ class QuadratureSpace;
|
||||
class QuadratureInterpolator;
|
||||
class FaceQuadratureInterpolator;
|
||||
class PRefinementTransferOperator;
|
||||
struct DerefineMatrixOp;
|
||||
|
||||
|
||||
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
|
||||
mesh, mainly managing the set of degrees of freedom.
|
||||
@@ -246,7 +246,6 @@ class FiniteElementSpace
|
||||
friend class PRefinementTransferOperator;
|
||||
friend void Mesh::Swap(Mesh &, bool);
|
||||
friend class LORBase;
|
||||
friend struct DerefineMatrixOp;
|
||||
|
||||
protected:
|
||||
/// The mesh that FE space lives on (not owned).
|
||||
@@ -947,8 +946,8 @@ public:
|
||||
/// could be used to produce the appropriate offsets from these local dofs.
|
||||
///@{
|
||||
|
||||
/// @brief Returns indices of degrees of freedom of element 'elem'. The
|
||||
/// returned indices are offsets into an @ref ldof vector. See also
|
||||
/// @brief Returns indices of degrees of freedom of element 'elem'.
|
||||
/// The returned indices are offsets into an @ref ldof vector. See also
|
||||
/// GetElementVDofs().
|
||||
///
|
||||
/// @note In many cases the returned DofTransformation object will be NULL.
|
||||
@@ -958,18 +957,15 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetElementDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetElementDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
/// @brief The same as GetElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
virtual void GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -984,18 +980,15 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetBdrElementDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetBdrElementDofs(int bel, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
virtual void GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -1199,18 +1192,15 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetElementVDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetElementVDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
/// @brief The same as GetElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
void GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -1226,18 +1216,15 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetBdrElementVDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
void GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
|
||||
+174
-261
@@ -17,7 +17,6 @@
|
||||
#include "quadinterpolator.hpp"
|
||||
#include "transfer.hpp"
|
||||
#include "../mesh/nurbs.hpp"
|
||||
#include "../mesh/vtkhdf.hpp"
|
||||
#include "../general/text.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
@@ -288,6 +287,8 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
GridFunction &u = *this;
|
||||
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation *udoftrans;
|
||||
DofTransformation *fdoftrans;
|
||||
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
@@ -300,7 +301,6 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
flux = 0.0;
|
||||
count = 0;
|
||||
|
||||
DofTransformation udoftrans, fdoftrans;
|
||||
for (int i = 0; i < nfe; i++)
|
||||
{
|
||||
if (subdomain >= 0 && ufes->GetAttribute(i) != subdomain)
|
||||
@@ -308,17 +308,23 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
continue;
|
||||
}
|
||||
|
||||
ufes->GetElementVDofs(i, udofs, udoftrans);
|
||||
ffes->GetElementVDofs(i, fdofs, fdoftrans);
|
||||
udoftrans = ufes->GetElementVDofs(i, udofs);
|
||||
fdoftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
udoftrans.InvTransformPrimal(ul);
|
||||
if (udoftrans)
|
||||
{
|
||||
udoftrans->InvTransformPrimal(ul);
|
||||
}
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
*ffes->GetFE(i), fl, wcoef);
|
||||
|
||||
fdoftrans.TransformPrimal(fl);
|
||||
if (fdoftrans)
|
||||
{
|
||||
fdoftrans->TransformPrimal(fl);
|
||||
}
|
||||
flux.AddElementVector(fdofs, fl);
|
||||
|
||||
FiniteElementSpace::AdjustVDofs(fdofs);
|
||||
@@ -346,23 +352,12 @@ void GridFunction::ComputeFlux(BilinearFormIntegrator &blfi,
|
||||
|
||||
int GridFunction::VectorDim() const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return fes->GetVDim();
|
||||
}
|
||||
return fes->GetVDim()*std::max(fes->GetMesh()->SpaceDimension(),
|
||||
fe->GetRangeDim());
|
||||
return fes->GetVectorDim();
|
||||
}
|
||||
|
||||
int GridFunction::CurlDim() const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return 2 * fes->GetMesh()->SpaceDimension() - 3;
|
||||
}
|
||||
return fes->GetVDim()*fe->GetCurlDim();
|
||||
return fes->GetCurlDim();
|
||||
}
|
||||
|
||||
void GridFunction::GetTrueDofs(Vector &tv) const
|
||||
@@ -398,8 +393,7 @@ void GridFunction::GetNodalValues(int i, Array<real_t> &nval, int vdim) const
|
||||
{
|
||||
Array<int> vdofs;
|
||||
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
const IntegrationRule *ElemVert =
|
||||
Geometries.GetVertices(FElem->GetGeomType());
|
||||
@@ -409,7 +403,10 @@ void GridFunction::GetNodalValues(int i, Array<real_t> &nval, int vdim) const
|
||||
vdim--;
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
@@ -450,8 +447,7 @@ real_t GridFunction::GetValue(int i, const IntegrationPoint &ip, int vdim)
|
||||
const
|
||||
{
|
||||
Array<int> dofs;
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
Vector DofVal(dofs.Size()), LocVec;
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -466,7 +462,10 @@ const
|
||||
fe->CalcPhysShape(*Tr, DofVal);
|
||||
}
|
||||
GetSubVector(dofs, LocVec);
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
|
||||
return (DofVal * LocVec);
|
||||
}
|
||||
@@ -477,11 +476,13 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Array<int> vdofs;
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -515,19 +516,22 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
}
|
||||
|
||||
void GridFunction::GetValues(int i, const IntegrationRule &ir, Vector &vals,
|
||||
int vdim) const
|
||||
int vdim)
|
||||
const
|
||||
{
|
||||
Array<int> dofs;
|
||||
int n = ir.GetNPoints();
|
||||
vals.SetSize(n);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Vector DofVal(dof), loc_data(dof);
|
||||
GetSubVector(dofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
if (FElem->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
for (int k = 0; k < n; k++)
|
||||
@@ -860,12 +864,12 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
|
||||
Array<int> vdofs;
|
||||
const FiniteElement *fe = NULL;
|
||||
DofTransformation doftrans;
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs, doftrans);
|
||||
doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
fe = fes->GetFE(T.ElementNo);
|
||||
break;
|
||||
case ElementTransformation::EDGE:
|
||||
@@ -955,7 +959,10 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
int dof = fe->GetDof();
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -999,11 +1006,13 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
|
||||
int dof = FElem->GetDof();
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
|
||||
int nip = ir.GetNPoints();
|
||||
|
||||
@@ -1094,19 +1103,23 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
DofTransformation * doftrans;
|
||||
DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, orig_loc_values;
|
||||
int i, j, d, ne, dof, odof, vdim;
|
||||
|
||||
ne = fes->GetNE();
|
||||
vdim = fes->GetVDim();
|
||||
DofTransformation doftrans, orig_doftrans;
|
||||
for (i = 0; i < ne; i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
orig_fes->GetElementVDofs(i, orig_vdofs, orig_doftrans);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
orig_doftrans = orig_fes->GetElementVDofs(i, orig_vdofs);
|
||||
orig_func.GetSubVector(orig_vdofs, orig_loc_values);
|
||||
orig_doftrans.InvTransformPrimal(orig_loc_values);
|
||||
if (orig_doftrans)
|
||||
{
|
||||
orig_doftrans->InvTransformPrimal(orig_loc_values);
|
||||
}
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const FiniteElement *orig_fe = orig_fes->GetFE(i);
|
||||
dof = fe->GetDof();
|
||||
@@ -1123,7 +1136,10 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
loc_values(d*dof+j) = shape * (&orig_loc_values[d * odof]);
|
||||
}
|
||||
}
|
||||
doftrans.TransformPrimal(loc_values);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(loc_values);
|
||||
}
|
||||
SetSubVector(vdofs, loc_values);
|
||||
}
|
||||
}
|
||||
@@ -1133,6 +1149,8 @@ void GridFunction::GetBdrValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
// DofTransformation * doftrans;
|
||||
// DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, loc_values_t, orig_loc_values, orig_loc_values_t;
|
||||
int i, j, d, nbe, dof, odof, vdim;
|
||||
@@ -1172,8 +1190,7 @@ void GridFunction::GetVectorFieldValues(
|
||||
ElementTransformation *transf;
|
||||
|
||||
const int n = ir.GetNPoints();
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const int dof = fe->GetDof();
|
||||
const int sdim = fes->GetMesh()->SpaceDimension();
|
||||
@@ -1185,7 +1202,10 @@ void GridFunction::GetVectorFieldValues(
|
||||
DenseMatrix vshape(dof, vdim);
|
||||
Vector loc_data, val(vdim);
|
||||
GetSubVector(vdofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(k);
|
||||
@@ -1376,7 +1396,6 @@ void GridFunction::GetVectorGradientHat(
|
||||
|
||||
real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
@@ -1404,10 +1423,13 @@ real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
// Assuming RT-type space
|
||||
Array<int> dofs;
|
||||
fes->GetElementDofs(elNo, dofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
Vector loc_data, divshape(fe->GetDof());
|
||||
GetSubVector(dofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
fe->CalcDivShape(T.GetIntPoint(), divshape);
|
||||
return (loc_data * divshape) / T.Weight();
|
||||
}
|
||||
@@ -1460,7 +1482,6 @@ real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
|
||||
void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
@@ -1495,10 +1516,13 @@ void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
// Assuming ND-type space
|
||||
Array<int> dofs;
|
||||
fes->GetElementDofs(elNo, dofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(dofs, loc_data);
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
DenseMatrix curl_shape(fe->GetDof(), fe->GetCurlDim());
|
||||
curl.SetSize(curl_shape.Width());
|
||||
fe->CalcPhysCurlShape(T, curl_shape);
|
||||
@@ -1699,10 +1723,11 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
MassIntegrator Mi;
|
||||
DenseMatrix loc_mass;
|
||||
DofTransformation * te_doftrans;
|
||||
DofTransformation * tr_doftrans;
|
||||
Array<int> te_dofs, tr_dofs;
|
||||
Vector loc_avgs, loc_this;
|
||||
Vector int_psi(avgs.Size());
|
||||
DofTransformation tr_doftrans, te_doftrans;
|
||||
|
||||
avgs = 0.0;
|
||||
int_psi = 0.0;
|
||||
@@ -1710,13 +1735,19 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
Mi.AssembleElementMatrix2(*fes->GetFE(i), *avgs.FESpace()->GetFE(i),
|
||||
*fes->GetElementTransformation(i), loc_mass);
|
||||
fes->GetElementDofs(i, tr_dofs, tr_doftrans);
|
||||
avgs.FESpace()->GetElementDofs(i, te_dofs, te_doftrans);
|
||||
tr_doftrans = fes->GetElementDofs(i, tr_dofs);
|
||||
te_doftrans = avgs.FESpace()->GetElementDofs(i, te_dofs);
|
||||
GetSubVector(tr_dofs, loc_this);
|
||||
tr_doftrans.InvTransformPrimal(loc_this);
|
||||
if (tr_doftrans)
|
||||
{
|
||||
tr_doftrans->InvTransformPrimal(loc_this);
|
||||
}
|
||||
loc_avgs.SetSize(te_dofs.Size());
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
te_doftrans.TransformPrimal(loc_avgs);
|
||||
if (te_doftrans)
|
||||
{
|
||||
te_doftrans->TransformPrimal(loc_avgs);
|
||||
}
|
||||
avgs.AddElementVector(te_dofs, loc_avgs);
|
||||
loc_this = 1.0; // assume the local basis for 'this' sums to 1
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
@@ -1731,10 +1762,12 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
Array<int> dof_idx;
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(el, dof_idx, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(el, dof_idx);
|
||||
GetSubVector(dof_idx, dof_vals);
|
||||
doftrans.InvTransformPrimal(dof_vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(dof_vals);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
@@ -1759,7 +1792,6 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
Array<int> src_vdofs, dest_vdofs;
|
||||
Vector src_lvec, dest_lvec(vdim*P.Height());
|
||||
|
||||
DofTransformation src_doftrans, doftrans;
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
// Assuming the projection matrix P depends only on the element geometry
|
||||
@@ -1771,15 +1803,21 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
cached_geom = geom;
|
||||
}
|
||||
|
||||
src.fes->GetElementVDofs(i, src_vdofs, src_doftrans);
|
||||
DofTransformation * src_doftrans = src.fes->GetElementVDofs(i, src_vdofs);
|
||||
src.GetSubVector(src_vdofs, src_lvec);
|
||||
src_doftrans.InvTransformPrimal(src_lvec);
|
||||
if (src_doftrans)
|
||||
{
|
||||
src_doftrans->InvTransformPrimal(src_lvec);
|
||||
}
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
P.Mult(&src_lvec[vd*P.Width()], &dest_lvec[vd*P.Height()]);
|
||||
}
|
||||
fes->GetElementVDofs(i, dest_vdofs, doftrans);
|
||||
doftrans.TransformPrimal(dest_lvec);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, dest_vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(dest_lvec);
|
||||
}
|
||||
SetSubVector(dest_vdofs, dest_lvec);
|
||||
}
|
||||
}
|
||||
@@ -1788,13 +1826,15 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
const Vector &lo_, const Vector &hi_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
|
||||
GetSubVector(vdofs, vals);
|
||||
doftrans.InvTransformPrimal(vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
|
||||
MFEM_ASSERT(weights.Size() == size, "Different # of weights and dofs.");
|
||||
MFEM_ASSERT(lo_.Size() == size, "Different # of lower bounds and dofs.");
|
||||
@@ -1811,7 +1851,10 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
slbqp.SetPrintLevel(0); // print messages only if not converged
|
||||
slbqp.Mult(vals, new_vals);
|
||||
|
||||
doftrans.TransformPrimal(new_vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
SetSubVector(vdofs, new_vals);
|
||||
}
|
||||
|
||||
@@ -1819,12 +1862,14 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
real_t min_, real_t max_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
GetSubVector(vdofs, vals);
|
||||
doftrans.InvTransformPrimal(vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
|
||||
real_t max_val = vals.Max();
|
||||
real_t min_val = vals.Min();
|
||||
@@ -1832,7 +1877,10 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
if (max_val <= min_)
|
||||
{
|
||||
new_vals = min_;
|
||||
doftrans.TransformPrimal(new_vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
SetSubVector(vdofs, new_vals);
|
||||
return;
|
||||
}
|
||||
@@ -1864,6 +1912,7 @@ void GridFunction::RestrictConforming()
|
||||
|
||||
void GridFunction::GetNodalValues(Vector &nval, int vdim) const
|
||||
{
|
||||
int i, j;
|
||||
Array<int> vertices;
|
||||
Array<real_t> values;
|
||||
Array<int> overlap(fes->GetNV());
|
||||
@@ -1871,17 +1920,17 @@ void GridFunction::GetNodalValues(Vector &nval, int vdim) const
|
||||
nval = 0.0;
|
||||
overlap = 0;
|
||||
nval.HostReadWrite();
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVertices(i, vertices);
|
||||
GetNodalValues(i, values, vdim);
|
||||
for (int j = 0; j < vertices.Size(); j++)
|
||||
for (j = 0; j < vertices.Size(); j++)
|
||||
{
|
||||
nval(vertices[j]) += values[j];
|
||||
overlap[vertices[j]]++;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < overlap.Size(); i++)
|
||||
for (i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
nval(i) /= overlap[i];
|
||||
}
|
||||
@@ -2161,7 +2210,6 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector lvec;
|
||||
DofTransformation dof_tr;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
@@ -2176,10 +2224,10 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
}
|
||||
fe = fes->GetBE(i);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
fes->GetBdrElementDofs(i, dofs, dof_tr);
|
||||
DofTransformation *dof_tr = fes->GetBdrElementDofs(i, dofs);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
dof_tr.TransformPrimal(lvec);
|
||||
if (dof_tr) { dof_tr->TransformPrimal(lvec); }
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
|
||||
@@ -2284,8 +2332,6 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
DenseMatrix loc_mass;
|
||||
Array<int> vdofs, vertices;
|
||||
Vector vals, loc_mass_vals;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
mesh->GetElementVertices(i, vertices);
|
||||
@@ -2297,8 +2343,11 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
loc_mass);
|
||||
vals.SetSize(fe->GetDof());
|
||||
fe->ProjectDelta(j, vals);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
doftrans.TransformPrimal(vals);
|
||||
const DofTransformation* const doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
SetSubVector(vdofs, vals);
|
||||
loc_mass_vals.SetSize(vals.Size());
|
||||
loc_mass.Mult(vals, loc_mass_vals);
|
||||
@@ -2311,7 +2360,7 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation doftrans;
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
@@ -2322,10 +2371,13 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2392,19 +2444,23 @@ void GridFunction::ProjectCoefficient(
|
||||
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2471,7 +2527,8 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
DofTransformation doftrans;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
@@ -2480,10 +2537,13 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
continue;
|
||||
}
|
||||
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2494,6 +2554,7 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
|
||||
real_t val;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *transf;
|
||||
// DofTransformation * doftrans;
|
||||
Array<int> vdofs;
|
||||
|
||||
vdim = fes->GetVDim();
|
||||
@@ -2676,7 +2737,6 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
@@ -2696,8 +2756,11 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs, doftrans);
|
||||
doftrans.TransformPrimal(lvec);
|
||||
const DofTransformation* const doftrans = fes->GetBdrElementDofs(i, dofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(lvec);
|
||||
}
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#endif
|
||||
@@ -3753,32 +3816,6 @@ void GridFunction::SaveVTK(std::ostream &os, const std::string &field_name,
|
||||
os.flush();
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_HDF5
|
||||
|
||||
void GridFunction::SaveVTKHDF(const std::string &fname, const std::string &name,
|
||||
bool high_order, int ref)
|
||||
{
|
||||
if (ref == -1) { ref = high_order ? fes->GetMaxElementOrder() : 1; }
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (ParFiniteElementSpace* pfes = dynamic_cast<ParFiniteElementSpace*>(fes))
|
||||
{
|
||||
#ifdef MFEM_PARALLEL_HDF5
|
||||
VTKHDF vtkhdf(fname, pfes->GetComm());
|
||||
vtkhdf.SaveMesh(*fes->GetMesh(), high_order, ref);
|
||||
vtkhdf.SaveGridFunction(*this, name);
|
||||
return;
|
||||
#else
|
||||
MFEM_ABORT("Requires HDF5 library with parallel support enabled");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
VTKHDF vtkhdf(fname);
|
||||
vtkhdf.SaveMesh(*fes->GetMesh(), high_order, ref);
|
||||
vtkhdf.SaveGridFunction(*this, name);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void GridFunction::SaveSTLTri(std::ostream &os, real_t p1[], real_t p2[],
|
||||
real_t p3[])
|
||||
{
|
||||
@@ -3995,7 +4032,6 @@ real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation utrans, ftrans;
|
||||
|
||||
int dim = ufes->GetMesh()->Dimension();
|
||||
int nfe = ufes->GetNE();
|
||||
@@ -4027,13 +4063,19 @@ real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
{
|
||||
if (with_subdomains && ufes->GetAttribute(i) != s) { continue; }
|
||||
|
||||
ufes->GetElementVDofs(i, udofs, utrans);
|
||||
ffes->GetElementVDofs(i, fdofs, ftrans);
|
||||
const DofTransformation* const utrans = ufes->GetElementVDofs(i, udofs);
|
||||
const DofTransformation* const ftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
flux.GetSubVector(fdofs, fla);
|
||||
utrans.InvTransformPrimal(ul);
|
||||
ftrans.InvTransformPrimal(fla);
|
||||
if (utrans)
|
||||
{
|
||||
utrans->InvTransformPrimal(ul);
|
||||
}
|
||||
if (ftrans)
|
||||
{
|
||||
ftrans->InvTransformPrimal(fla);
|
||||
}
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
@@ -4248,7 +4290,6 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
MFEM_VERIFY(tichonov_coeff >= 0.0, "tichonov_coeff cannot be negative");
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation utrans;
|
||||
|
||||
Mesh *mesh = ufes->GetMesh();
|
||||
int dim = mesh->Dimension();
|
||||
@@ -4330,11 +4371,14 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
flux_order));
|
||||
int num_integration_pts = ir->GetNPoints();
|
||||
|
||||
ufes->GetElementVDofs(ielem, udofs, utrans);
|
||||
const DofTransformation* const utrans = ufes->GetElementVDofs(ielem, udofs);
|
||||
u.GetSubVector(udofs, ul);
|
||||
utrans.InvTransformPrimal(ul);
|
||||
if (utrans)
|
||||
{
|
||||
utrans->InvTransformPrimal(ul);
|
||||
}
|
||||
Transf = ufes->GetElementTransformation(ielem);
|
||||
const auto *dummy = ufes->GetFE(ielem);
|
||||
FiniteElement *dummy = nullptr;
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
|
||||
*dummy, fl, with_coeff, ir);
|
||||
|
||||
@@ -4563,135 +4607,4 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
return sol2d;
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
int fes_dim = fes->GetVDim();
|
||||
int rdim = fe->GetDim();
|
||||
|
||||
const TensorBasisElement *tbe =
|
||||
dynamic_cast<const TensorBasisElement *>(fe);
|
||||
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
|
||||
const Array<int> &dof_map = tbe->GetDofMap();
|
||||
|
||||
Vector loc_data;
|
||||
Array<int> dof_idx;
|
||||
fes->GetElementDofs(elem, dof_idx);
|
||||
int ndofs = dof_idx.Size();
|
||||
|
||||
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
|
||||
lower.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
|
||||
upper.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
|
||||
|
||||
for (int d = 0; d < fes_dim; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
Array<int> dof_idx_c = dof_idx;
|
||||
Vector lowerT(lower, d_off*n_c_pts, n_c_pts);
|
||||
Vector upperT(upper, d_off*n_c_pts, n_c_pts);
|
||||
fes->DofsToVDofs(vdim > 0 ? vdim-1 : d, dof_idx_c);
|
||||
GetSubVector(dof_idx_c, loc_data);
|
||||
Vector nodal_data;
|
||||
if (dof_map.Size() == 0)
|
||||
{
|
||||
nodal_data.SetDataAndSize(loc_data.GetData(), ndofs);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodal_data.SetSize(ndofs);
|
||||
for (int j = 0; j < ndofs; j++)
|
||||
{
|
||||
nodal_data(j) = loc_data(dof_map[j]);
|
||||
}
|
||||
}
|
||||
plb.GetNDBounds(rdim, nodal_data, lowerT, upperT);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim)
|
||||
{
|
||||
Vector lowerC, upperC;
|
||||
GetElementBoundsAtControlPoints(elem, plb, lowerC, upperC, vdim);
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
int rdim = fe->GetDim();
|
||||
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize((vdim > 0 ? 1 :fes_dim));
|
||||
upper.SetSize((vdim > 0 ? 1 :fes_dim));
|
||||
for (int d = 0; d < fes_dim; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
Vector lowerT(lowerC, d_off*n_c_pts, n_c_pts);
|
||||
Vector upperT(upperC, d_off*n_c_pts, n_c_pts);
|
||||
lower(d_off) = lowerT.Min();
|
||||
upper(d_off) = upperT.Max();
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim)
|
||||
{
|
||||
int nel = fes->GetNE();
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize(nel*(vdim > 0 ? 1 :fes_dim));
|
||||
upper.SetSize(nel*(vdim > 0 ? 1 :fes_dim));
|
||||
for (int e = 0; e < nel; e++)
|
||||
{
|
||||
Vector lt, ut;
|
||||
GetElementBounds(e, plb, lt, ut, vdim);
|
||||
for (int d = 0; d < fes_dim ; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
lower(e + d_off*nel) = lt(d_off);
|
||||
upper(e + d_off*nel) = ut(d_off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PLBound GridFunction::GetElementBounds(Vector &lower,
|
||||
Vector &upper,
|
||||
const int ref_factor,
|
||||
const int vdim)
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
GetElementBounds(plb, lower, upper, vdim);
|
||||
return plb;
|
||||
}
|
||||
|
||||
PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor, const int vdim)
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
Vector lel, uel;
|
||||
GetElementBounds(plb, lel, uel, vdim);
|
||||
|
||||
int nel = fes->GetNE();
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize(vdim > 0 ? 1 : fes_dim);
|
||||
upper.SetSize(vdim > 0 ? 1 : fes_dim);
|
||||
for (int d = 0; d < fes_dim; d++)
|
||||
{
|
||||
if (vdim > 0 && d != vdim-1) { continue; }
|
||||
const int d_off = vdim > 0 ? 0 : d;
|
||||
Vector lelt(lel, d_off*nel, nel);
|
||||
Vector uelt(uel, d_off*nel, nel);
|
||||
lower(d_off) = lelt.Min();
|
||||
upper(d_off) = uelt.Max();
|
||||
}
|
||||
return plb;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user