Compare commits
312
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c968516f36 | ||
|
|
9a9d1ea967 | ||
|
|
46ee2ab5dc | ||
|
|
fb672667cc | ||
|
|
1d35fafd21 | ||
|
|
a0ed1bfbca | ||
|
|
0c08279225 | ||
|
|
bc84ce3b47 | ||
|
|
36abe386e0 | ||
|
|
a4868f2a98 | ||
|
|
21321b3abc | ||
|
|
bb4f39c3d7 | ||
|
|
b605a29988 | ||
|
|
7967e13f1d | ||
|
|
b615f22b66 | ||
|
|
e0fe515f21 | ||
|
|
269ee766db | ||
|
|
da8a221097 | ||
|
|
20d6e63df0 | ||
|
|
2288cdcb7f | ||
|
|
233337c9d1 | ||
|
|
4d9cd853b7 | ||
|
|
27352658c3 | ||
|
|
b8a303a07a | ||
|
|
cee9bf3bb2 | ||
|
|
a901754de5 | ||
|
|
03da41c0f5 | ||
|
|
c6ec74db41 | ||
|
|
8854247f86 | ||
|
|
755e4501e1 | ||
|
|
1ec73c3bf6 | ||
|
|
3a15fe3d96 | ||
|
|
8b000dd222 | ||
|
|
a4d6889332 | ||
|
|
e0fbc5e3aa | ||
|
|
03da9d7789 | ||
|
|
9c8874a38c | ||
|
|
bb2460cbd0 | ||
|
|
0648e50e70 | ||
|
|
45e8125fd6 | ||
|
|
25056defeb | ||
|
|
20cd965ed8 | ||
|
|
1fda9c2391 | ||
|
|
2cec0353b1 | ||
|
|
9d22775395 | ||
|
|
94625fad8f | ||
|
|
c9a9c71ff5 | ||
|
|
764d9919b5 | ||
|
|
8efbd4e46f | ||
|
|
b94ac358e4 | ||
|
|
d4d4b79522 | ||
|
|
120f4cb043 | ||
|
|
4f1597c1bc | ||
|
|
56beedbdcb | ||
|
|
520a9c5125 | ||
|
|
ed521022cd | ||
|
|
7b35a47626 | ||
|
|
def35c8a15 | ||
|
|
4e35b3d8f1 | ||
|
|
29681677a1 | ||
|
|
9d72af995f | ||
|
|
b099252dcf | ||
|
|
d696fc2cea | ||
|
|
8dcd0d6349 | ||
|
|
2951d5f98e | ||
|
|
4b27589abf | ||
|
|
8338aa85e6 | ||
|
|
e20bb381ca | ||
|
|
89e23a93f5 | ||
|
|
4816fa0849 | ||
|
|
6fc7a8ca5a | ||
|
|
22c8b607dc | ||
|
|
91deab3c75 | ||
|
|
87cee25894 | ||
|
|
1bcddddfdb | ||
|
|
da68672955 | ||
|
|
a5dd4b862b | ||
|
|
a3fcb89049 | ||
|
|
bd6f3d51c8 | ||
|
|
8d95f71305 | ||
|
|
aee0cb1dc6 | ||
|
|
7dded1fdcf | ||
|
|
cc0dcaad09 | ||
|
|
960379a43d | ||
|
|
3782ece6b3 | ||
|
|
823c7a952d | ||
|
|
2bc553633e | ||
|
|
8be00d1115 | ||
|
|
bdd2bfbb79 | ||
|
|
1ea4cd14da | ||
|
|
63b72c4153 | ||
|
|
054593bd4d | ||
|
|
298a4bc32a | ||
|
|
65866edd70 | ||
|
|
85b96208f0 | ||
|
|
b825a46061 | ||
|
|
d6cbd4f99a | ||
|
|
c691658232 | ||
|
|
157ffad537 | ||
|
|
cfdb7d2a03 | ||
|
|
a95a2dc251 | ||
|
|
a7737e65ab | ||
|
|
dda669bd70 | ||
|
|
802345aa91 | ||
|
|
1e84d8a9c5 | ||
|
|
ce30630f5f | ||
|
|
0055de1734 | ||
|
|
954757f7de | ||
|
|
7ad1da790c | ||
|
|
4290365459 | ||
|
|
04d6fa900d | ||
|
|
9fec57261b | ||
|
|
9e8a710c92 | ||
|
|
c5d2f364ac | ||
|
|
fca154fbd4 | ||
|
|
804bdb498a | ||
|
|
1546398ee8 | ||
|
|
9237b4cc2e | ||
|
|
a02927005d | ||
|
|
6256741216 | ||
|
|
a5fcfa02e8 | ||
|
|
2fc9b97cb4 | ||
|
|
a5ca81806b | ||
|
|
d4513550f9 | ||
|
|
d1befb2ea6 | ||
|
|
e11a093e72 | ||
|
|
943234617b | ||
|
|
36389366ac | ||
|
|
dbd2b5556b | ||
|
|
6570ca9c7a | ||
|
|
71943e120c | ||
|
|
ef65351cc9 | ||
|
|
5a7807055b | ||
|
|
08b46a12dc | ||
|
|
78124a649d | ||
|
|
0e9d10c53d | ||
|
|
911eb07565 | ||
|
|
2efec6390b | ||
|
|
756bc524ab | ||
|
|
bade193d79 | ||
|
|
4363cd2dc2 | ||
|
|
cbbe609ff8 | ||
|
|
23caac9573 | ||
|
|
3eb0e321f4 | ||
|
|
58c276b1d9 | ||
|
|
b81c67a061 | ||
|
|
fbd6aa17e0 | ||
|
|
199192c0f6 | ||
|
|
27989c68bd | ||
|
|
8ba6b88ee0 | ||
|
|
df03268c01 | ||
|
|
7e7322cb86 | ||
|
|
d8b9c7881b | ||
|
|
58f0e28453 | ||
|
|
9d48f23dca | ||
|
|
1947748db6 | ||
|
|
7aca441524 | ||
|
|
689522beb5 | ||
|
|
c0254e3835 | ||
|
|
85e1855d1b | ||
|
|
51eb3c0026 | ||
|
|
c1c348f24f | ||
|
|
043c6ee860 | ||
|
|
790b4b9bce | ||
|
|
69aea4ce75 | ||
|
|
d7d173a215 | ||
|
|
d7ac021db2 | ||
|
|
2b1d4eb30c | ||
|
|
c134457322 | ||
|
|
33bd60f7c7 | ||
|
|
8f3e61a4d3 | ||
|
|
351806ce77 | ||
|
|
d865ac444e | ||
|
|
b14a9b4663 | ||
|
|
cdd560e6a2 | ||
|
|
4c9ddf93e4 | ||
|
|
8410c205a3 | ||
|
|
f714dcfd57 | ||
|
|
53fb6e3977 | ||
|
|
678a9db016 | ||
|
|
c09246351d | ||
|
|
86a8d39e54 | ||
|
|
54854b0908 | ||
|
|
8fa48c2425 | ||
|
|
3e9b8605f9 | ||
|
|
d84aa5a355 | ||
|
|
c11c5cf654 | ||
|
|
1c8d25c6ed | ||
|
|
be6d5e2b01 | ||
|
|
8abdc6500e | ||
|
|
21df84f320 | ||
|
|
9f5860fda2 | ||
|
|
c5a03405cf | ||
|
|
33d0d7dbfe | ||
|
|
c085ec6544 | ||
|
|
1f860fbfaf | ||
|
|
c43420c375 | ||
|
|
ecbdd73c54 | ||
|
|
d30e13c543 | ||
|
|
ba2b8aea52 | ||
|
|
f1353bd6e9 | ||
|
|
3cb0bee255 | ||
|
|
bda0b9aba5 | ||
|
|
e85af79c16 | ||
|
|
06c4da64af | ||
|
|
b841c9df71 | ||
|
|
31a6329964 | ||
|
|
9f83167010 | ||
|
|
2b4085e2dd | ||
|
|
a69ea6d698 | ||
|
|
a617205ee1 | ||
|
|
620f906765 | ||
|
|
2620effa65 | ||
|
|
3da9bdc39a | ||
|
|
2ab00899fd | ||
|
|
7009449ef5 | ||
|
|
34482860b0 | ||
|
|
8f4aafdebc | ||
|
|
26eea4f2fd | ||
|
|
313f6856e8 | ||
|
|
bf97e92be2 | ||
|
|
d7a04ca4ac | ||
|
|
28b1ac0c9d | ||
|
|
0eb2d21602 | ||
|
|
efcf608a5d | ||
|
|
f1d56c4068 | ||
|
|
94698f27d7 | ||
|
|
30dac8986c | ||
|
|
0691354c84 | ||
|
|
5c0b2a6b62 | ||
|
|
c25b84fbd8 | ||
|
|
d6c1edc9a3 | ||
|
|
74498373c9 | ||
|
|
1cc3d81866 | ||
|
|
233316269a | ||
|
|
e34c6b6013 | ||
|
|
ea593def25 | ||
|
|
f316ec7d5e | ||
|
|
800be7971a | ||
|
|
ee06c0eb44 | ||
|
|
f0e8e6e19a | ||
|
|
ff11a6b572 | ||
|
|
a8bbdf4fd4 | ||
|
|
2421b48f56 | ||
|
|
2cd2d11215 | ||
|
|
e11d19e3a9 | ||
|
|
b0c30784b6 | ||
|
|
a7aa6c5a7c | ||
|
|
f6c2f10dee | ||
|
|
5dcf6f7ead | ||
|
|
e8ed1a4c02 | ||
|
|
3cfca882af | ||
|
|
70cb8fcc04 | ||
|
|
0366ad2468 | ||
|
|
a355f28eae | ||
|
|
a75b1ca9c0 | ||
|
|
8ff6d69f74 | ||
|
|
35aeecb5c0 | ||
|
|
44f2a63f16 | ||
|
|
eb9022540e | ||
|
|
cf127c8b14 | ||
|
|
df36d0f352 | ||
|
|
765ebcecaa | ||
|
|
b3a08b91d6 | ||
|
|
726b5f99ff | ||
|
|
a943683063 | ||
|
|
b6fb45f384 | ||
|
|
d119fa7636 | ||
|
|
4a7c643f99 | ||
|
|
2dfd2ccfc5 | ||
|
|
ae9a8b2897 | ||
|
|
2d3aba5d87 | ||
|
|
f4c66c56d6 | ||
|
|
3c3face72c | ||
|
|
762551da72 | ||
|
|
293a374a74 | ||
|
|
39be93547e | ||
|
|
7559d37c58 | ||
|
|
90eed63144 | ||
|
|
e6bc4e5a0e | ||
|
|
03ec8d78e2 | ||
|
|
3fbeff1db7 | ||
|
|
3d7ac596da | ||
|
|
57e1693fc7 | ||
|
|
67ca28a501 | ||
|
|
6d334a925a | ||
|
|
8183e1729d | ||
|
|
ef1e0caed1 | ||
|
|
adbe1bfe3a | ||
|
|
5590b87f5e | ||
|
|
f28cd12995 | ||
|
|
e0aba0647d | ||
|
|
cd4593bf8e | ||
|
|
0cc5280e34 | ||
|
|
f12f0efb31 | ||
|
|
e3c3150958 | ||
|
|
911fbfbe82 | ||
|
|
0b3b21dbb2 | ||
|
|
823fd86a87 | ||
|
|
2d3c1bc79a | ||
|
|
1d556b93e7 | ||
|
|
00fcb1b37f | ||
|
|
9445358bc9 | ||
|
|
e4becc6e02 | ||
|
|
9b1c3b718d | ||
|
|
e9826f9c69 | ||
|
|
783136234a | ||
|
|
dd85ae3384 | ||
|
|
294a71c705 | ||
|
|
0b632bf3d4 | ||
|
|
fabce12b76 | ||
|
|
8fbfdb19fe |
@@ -0,0 +1,154 @@
|
||||
# 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
|
||||
@@ -0,0 +1,91 @@
|
||||
# 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
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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
|
||||
@@ -0,0 +1,71 @@
|
||||
# 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
|
||||
@@ -0,0 +1,64 @@
|
||||
# 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,18 +7,17 @@
|
||||
|
||||
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.1`, the `v2.1` 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.5`, the `v2.5` 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.
|
||||
|
||||
@@ -30,19 +29,39 @@ 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.
|
||||
|
||||
@@ -1,69 +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"
|
||||
|
||||
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++17
|
||||
-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
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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
|
||||
@@ -0,0 +1,38 @@
|
||||
# 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
|
||||
@@ -0,0 +1,36 @@
|
||||
# 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}}
|
||||
@@ -0,0 +1,197 @@
|
||||
# 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}}"
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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
|
||||
+4
-5
@@ -19,9 +19,6 @@ CMakeFiles/
|
||||
# Clangd server cache
|
||||
*.cache*
|
||||
|
||||
# VSCode configuration
|
||||
/.vscode/
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
|
||||
@@ -303,6 +300,7 @@ 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
|
||||
@@ -321,6 +319,7 @@ 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
|
||||
@@ -416,8 +415,8 @@ miniapps/diag-smoothers/mg-abs-l1-jacobi
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
tests/unit/punit_tests
|
||||
tests/unit/cunit_tests
|
||||
tests/unit/pcunit_tests
|
||||
tests/unit/gpu_unit_tests
|
||||
tests/unit/pgpu_unit_tests
|
||||
tests/unit/sedov_tests_*
|
||||
tests/unit/psedov_tests_*
|
||||
tests/unit/tmop_pa_tests_*
|
||||
|
||||
@@ -29,9 +29,14 @@ Discretization improvements
|
||||
|
||||
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
|
||||
@@ -39,6 +44,8 @@ GPU computing
|
||||
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.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
@@ -48,13 +55,26 @@ New and updated examples and miniapps
|
||||
operators as smoothers.
|
||||
These miniapps can be found in `miniapps/diag-smoothers`.
|
||||
|
||||
API changes:
|
||||
API changes
|
||||
-----------
|
||||
- mfem::internal::tensor and mfem::internal::dual have been moved to
|
||||
mfem::future::tensor and mfem::future::dual.
|
||||
- API addition: in class `Operator`, added virtual functions: `AbsMult`, and
|
||||
`AbsMultTranspose`; in class `Vector`, added `Abs` and `Pow`.
|
||||
|
||||
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
|
||||
====================================
|
||||
|
||||
+35
-76
@@ -62,14 +62,9 @@ static real_t epsilon_ = 1.0;
|
||||
static real_t sigma_ = 20.0;
|
||||
static real_t omega_ = 10.0;
|
||||
|
||||
real_t u0_real_exact(const Vector &);
|
||||
real_t u0_imag_exact(const Vector &);
|
||||
|
||||
void u1_real_exact(const Vector &, Vector &);
|
||||
void u1_imag_exact(const Vector &, Vector &);
|
||||
|
||||
void u2_real_exact(const Vector &, Vector &);
|
||||
void u2_imag_exact(const Vector &, Vector &);
|
||||
complex<real_t> u0_exact(const Vector &x);
|
||||
void u1_exact(const Vector &, ComplexVector &);
|
||||
void u2_exact(const Vector &, ComplexVector &);
|
||||
|
||||
bool check_for_inline_mesh(const char * mesh_file);
|
||||
|
||||
@@ -215,54 +210,48 @@ int main(int argc, char *argv[])
|
||||
ComplexGridFunction * u_exact = NULL;
|
||||
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
|
||||
|
||||
FunctionCoefficient u0_r(u0_real_exact);
|
||||
FunctionCoefficient u0_i(u0_imag_exact);
|
||||
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
|
||||
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
|
||||
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
|
||||
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
|
||||
ComplexFunctionCoefficient u0(u0_exact);
|
||||
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
|
||||
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
|
||||
|
||||
ConstantCoefficient zeroCoef(0.0);
|
||||
ConstantCoefficient oneCoef(1.0);
|
||||
ComplexConstantCoefficient oneCoef(1.0);
|
||||
|
||||
Vector zeroVec(dim); zeroVec = 0.0;
|
||||
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
|
||||
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
||||
VectorConstantCoefficient oneVecCoef(oneVec);
|
||||
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0_r, u0_i);
|
||||
u.ProjectBdrCoefficient(u0, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1_r, u1_i);
|
||||
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2_r, u2_i);
|
||||
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
@@ -300,27 +289,24 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient lossCoef(omega_ * sigma_);
|
||||
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
|
||||
|
||||
ComplexConstantCoefficient complexMassCoef(-omega_ * omega_ * epsilon_,
|
||||
omega_ * sigma_);
|
||||
|
||||
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new MassIntegrator(massCoef),
|
||||
new MassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DiffusionIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 1:
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 2:
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
@@ -436,29 +422,24 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (exact_sol)
|
||||
{
|
||||
real_t err_r = -1.0;
|
||||
real_t err_i = -1.0;
|
||||
real_t err_u = -1.0;
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
err_r = u.real().ComputeL2Error(u0_r);
|
||||
err_i = u.imag().ComputeL2Error(u0_i);
|
||||
err_u = u.ComputeL2Error(u0);
|
||||
break;
|
||||
case 1:
|
||||
err_r = u.real().ComputeL2Error(u1_r);
|
||||
err_i = u.imag().ComputeL2Error(u1_i);
|
||||
err_u = u.ComputeL2Error(u1);
|
||||
break;
|
||||
case 2:
|
||||
err_r = u.real().ComputeL2Error(u2_r);
|
||||
err_i = u.imag().ComputeL2Error(u2_i);
|
||||
err_u = u.ComputeL2Error(u2);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
|
||||
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
|
||||
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
@@ -564,36 +545,14 @@ complex<real_t> u0_exact(const Vector &x)
|
||||
return std::exp(-i * kappa * x[dim - 1]);
|
||||
}
|
||||
|
||||
real_t u0_real_exact(const Vector &x)
|
||||
{
|
||||
return u0_exact(x).real();
|
||||
}
|
||||
|
||||
real_t u0_imag_exact(const Vector &x)
|
||||
{
|
||||
return u0_exact(x).imag();
|
||||
}
|
||||
|
||||
void u1_real_exact(const Vector &x, Vector &v)
|
||||
void u1_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
|
||||
}
|
||||
|
||||
void u1_imag_exact(const Vector &x, Vector &v)
|
||||
void u2_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
|
||||
}
|
||||
|
||||
void u2_real_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
|
||||
}
|
||||
|
||||
void u2_imag_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
|
||||
}
|
||||
|
||||
+50
-33
@@ -62,6 +62,10 @@ static real_t epsilon_ = 1.0;
|
||||
static real_t sigma_ = 20.0;
|
||||
static real_t omega_ = 10.0;
|
||||
|
||||
complex<real_t> u0_exact(const Vector &x);
|
||||
void u1_exact(const Vector &, ComplexVector &);
|
||||
void u2_exact(const Vector &, ComplexVector &);
|
||||
|
||||
real_t u0_real_exact(const Vector &);
|
||||
real_t u0_imag_exact(const Vector &);
|
||||
|
||||
@@ -244,13 +248,22 @@ int main(int argc, char *argv[])
|
||||
ParComplexGridFunction * u_exact = NULL;
|
||||
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
|
||||
|
||||
ComplexFunctionCoefficient u0(u0_exact);
|
||||
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
|
||||
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
|
||||
|
||||
ComplexConstantCoefficient oneCoef(1.0);
|
||||
|
||||
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
|
||||
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
FunctionCoefficient u0_r(u0_real_exact);
|
||||
FunctionCoefficient u0_i(u0_imag_exact);
|
||||
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
|
||||
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
|
||||
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
|
||||
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
|
||||
|
||||
/*
|
||||
ConstantCoefficient zeroCoef(0.0);
|
||||
ConstantCoefficient oneCoef(1.0);
|
||||
|
||||
@@ -258,40 +271,40 @@ int main(int argc, char *argv[])
|
||||
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
|
||||
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
||||
VectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
*/
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0_r, u0_i);
|
||||
u.ProjectBdrCoefficient(u0, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u0);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1_r, u1_i);
|
||||
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u1);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (exact_sol)
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2_r, u2_i);
|
||||
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
|
||||
u_exact->ProjectCoefficient(u2);
|
||||
}
|
||||
else
|
||||
{
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
|
||||
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
|
||||
}
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
@@ -331,27 +344,24 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient lossCoef(omega_ * sigma_);
|
||||
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
|
||||
|
||||
ComplexConstantCoefficient complexMassCoef(-omega_ * omega_ * epsilon_,
|
||||
omega_ * sigma_);
|
||||
|
||||
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new MassIntegrator(massCoef),
|
||||
new MassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DiffusionIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 1:
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
case 2:
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
|
||||
NULL);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
|
||||
new VectorFEMassIntegrator(lossCoef));
|
||||
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
|
||||
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
@@ -475,22 +485,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (exact_sol)
|
||||
{
|
||||
real_t err_r = -1.0;
|
||||
real_t err_i = -1.0;
|
||||
real_t err_u = -1.0;
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
err_r = u.real().ComputeL2Error(u0_r);
|
||||
err_i = u.imag().ComputeL2Error(u0_i);
|
||||
err_u = u.ComputeL2Error(u0);
|
||||
break;
|
||||
case 1:
|
||||
err_r = u.real().ComputeL2Error(u1_r);
|
||||
err_i = u.imag().ComputeL2Error(u1_i);
|
||||
err_u = u.ComputeL2Error(u1);
|
||||
break;
|
||||
case 2:
|
||||
err_r = u.real().ComputeL2Error(u2_r);
|
||||
err_i = u.imag().ComputeL2Error(u2_i);
|
||||
err_u = u.ComputeL2Error(u2);
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
@@ -498,8 +504,7 @@ int main(int argc, char *argv[])
|
||||
if ( myid == 0 )
|
||||
{
|
||||
cout << endl;
|
||||
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
|
||||
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
|
||||
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
@@ -627,6 +632,12 @@ real_t u0_imag_exact(const Vector &x)
|
||||
return u0_exact(x).imag();
|
||||
}
|
||||
|
||||
void u1_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
|
||||
}
|
||||
|
||||
void u1_real_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
@@ -639,6 +650,12 @@ void u1_imag_exact(const Vector &x, Vector &v)
|
||||
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
|
||||
}
|
||||
|
||||
void u2_exact(const Vector &x, ComplexVector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
|
||||
}
|
||||
|
||||
void u2_real_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
@@ -59,6 +59,7 @@ set(SRCS
|
||||
integ/nonlininteg_vecconvection_pa.cpp
|
||||
integ/nonlininteg_vecconvection_mf.cpp
|
||||
coefficient.cpp
|
||||
complex_coefficient.cpp
|
||||
complex_fem.cpp
|
||||
convergence.cpp
|
||||
datacollection.cpp
|
||||
@@ -176,6 +177,7 @@ set(HDRS
|
||||
integ/bilininteg_hcurlhdiv_kernels.hpp
|
||||
integ/bilininteg_mass_kernels.hpp
|
||||
coefficient.hpp
|
||||
complex_coefficient.hpp
|
||||
complex_fem.hpp
|
||||
convergence.hpp
|
||||
datacollection.hpp
|
||||
@@ -247,6 +249,7 @@ set(HDRS
|
||||
nonlinearform_ext.hpp
|
||||
nonlininteg.hpp
|
||||
qfunction.hpp
|
||||
qinterp/det.hpp
|
||||
qinterp/eval.hpp
|
||||
qinterp/eval_hdiv.hpp
|
||||
qinterp/grad.hpp
|
||||
|
||||
@@ -515,6 +515,7 @@ 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)
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
// 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 "complex_fem.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
real_t
|
||||
RealPartCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_t val = complex_coef_.Eval(T, ip);
|
||||
return val.real();
|
||||
}
|
||||
|
||||
real_t
|
||||
ImagPartCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_t val = complex_coef_.Eval(T, ip);
|
||||
return val.imag();
|
||||
}
|
||||
|
||||
RealPartVectorCoefficient::RealPartVectorCoefficient(ComplexVectorCoefficient &
|
||||
complex_vcoef)
|
||||
: VectorCoefficient(complex_vcoef.GetVDim()),
|
||||
complex_vcoef_(complex_vcoef),
|
||||
val_(vdim)
|
||||
{}
|
||||
|
||||
void
|
||||
RealPartVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_vcoef_.Eval(val_, T, ip);
|
||||
V = val_.real();
|
||||
}
|
||||
|
||||
ImagPartVectorCoefficient::ImagPartVectorCoefficient(ComplexVectorCoefficient &
|
||||
complex_vcoef)
|
||||
: VectorCoefficient(complex_vcoef.GetVDim()),
|
||||
complex_vcoef_(complex_vcoef),
|
||||
val_(vdim)
|
||||
{}
|
||||
|
||||
void
|
||||
ImagPartVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_vcoef_.Eval(val_, T, ip);
|
||||
V = val_.imag();
|
||||
}
|
||||
|
||||
RealPartMatrixCoefficient::RealPartMatrixCoefficient(ComplexMatrixCoefficient &
|
||||
complex_mcoef)
|
||||
: MatrixCoefficient(complex_mcoef.GetHeight(), complex_mcoef.GetWidth()),
|
||||
complex_mcoef_(complex_mcoef),
|
||||
val_(height, width)
|
||||
{}
|
||||
|
||||
void
|
||||
RealPartMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_mcoef_.Eval(val_, T, ip);
|
||||
M = val_.real();
|
||||
}
|
||||
|
||||
ImagPartMatrixCoefficient::ImagPartMatrixCoefficient(ComplexMatrixCoefficient &
|
||||
complex_mcoef)
|
||||
: MatrixCoefficient(complex_mcoef.GetHeight(), complex_mcoef.GetWidth()),
|
||||
complex_mcoef_(complex_mcoef),
|
||||
val_(height, width)
|
||||
{}
|
||||
|
||||
void
|
||||
ImagPartMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
complex_mcoef_.Eval(val_, T, ip);
|
||||
M = val_.imag();
|
||||
}
|
||||
|
||||
ComplexCoefficient::ComplexCoefficient()
|
||||
: time(0.),
|
||||
re_part_coef_(*this), im_part_coef_(*this),
|
||||
real_coef_(re_part_coef_), imag_coef_(im_part_coef_)
|
||||
{ }
|
||||
|
||||
ComplexCoefficient::ComplexCoefficient(Coefficient &c_r,
|
||||
Coefficient &c_i)
|
||||
: time(c_r.GetTime()),
|
||||
re_part_coef_(*this), im_part_coef_(*this),
|
||||
real_coef_(c_r), imag_coef_(c_i)
|
||||
{
|
||||
c_i.SetTime(time);
|
||||
}
|
||||
|
||||
complex_t
|
||||
ComplexCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
// Avoid circular dependency
|
||||
MFEM_VERIFY(std::addressof(real_coef_) != std::addressof(re_part_coef_) &&
|
||||
std::addressof(imag_coef_) != std::addressof(im_part_coef_),
|
||||
"Classes dervied from ComplexCoefficient must either "
|
||||
"implement an Eval method or supply Coefficients "
|
||||
"for both the real and imaginary parts of the field.");
|
||||
|
||||
return complex_t(real_coef_.Eval(T, ip), imag_coef_.Eval(T, ip));
|
||||
}
|
||||
|
||||
ComplexVectorCoefficient::ComplexVectorCoefficient(VectorCoefficient &v_r,
|
||||
VectorCoefficient &v_i)
|
||||
: vdim(v_r.GetVDim()), time(v_r.GetTime()),
|
||||
re_part_vcoef_(*this), im_part_vcoef_(*this),
|
||||
real_vcoef_(v_r), imag_vcoef_(v_i)
|
||||
{
|
||||
MFEM_ASSERT(v_r.GetVDim() == v_i.GetVDim(), "ComplexVectorCoefficient"
|
||||
" - incompatible vector dimensions of real and imaginary parts.");
|
||||
|
||||
v_i.SetTime(time);
|
||||
}
|
||||
|
||||
void ComplexVectorCoefficient::Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
// Avoid circular dependency
|
||||
MFEM_VERIFY(std::addressof(real_vcoef_) != std::addressof(re_part_vcoef_) &&
|
||||
std::addressof(imag_vcoef_) != std::addressof(im_part_vcoef_),
|
||||
"Classes dervied from ComplexVectorCoefficient must either "
|
||||
"implement an Eval method or supply VectorCoefficients "
|
||||
"for both the real and imaginary parts of the field.");
|
||||
|
||||
V_r_.SetSize(vdim);
|
||||
V_i_.SetSize(vdim);
|
||||
|
||||
real_vcoef_.Eval(V_r_, T, ip);
|
||||
imag_vcoef_.Eval(V_i_, T, ip);
|
||||
|
||||
V.Set(V_r_, V_i_);
|
||||
}
|
||||
|
||||
ComplexConstantCoefficient::ComplexConstantCoefficient(
|
||||
const complex_t z)
|
||||
: val(z), real_coef(z.real()), imag_coef(z.imag())
|
||||
{
|
||||
real_coef_ = real_coef;
|
||||
imag_coef_ = imag_coef;
|
||||
}
|
||||
|
||||
ComplexConstantCoefficient::ComplexConstantCoefficient(
|
||||
real_t z_r, real_t z_i)
|
||||
: real_coef(z_r), imag_coef(z_i)
|
||||
{
|
||||
val = complex_t(z_r, z_i);
|
||||
|
||||
real_coef_ = real_coef;
|
||||
imag_coef_ = imag_coef;
|
||||
}
|
||||
|
||||
complex_t ComplexFunctionCoefficient::Eval(ElementTransformation & T,
|
||||
const IntegrationPoint & ip)
|
||||
{
|
||||
real_t x[3];
|
||||
Vector transip(x, 3);
|
||||
|
||||
T.Transform(ip, transip);
|
||||
|
||||
if (Function)
|
||||
{
|
||||
return Function(transip);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TDFunction(transip, GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexVectorFunctionCoefficient::Eval(ComplexVector &V,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
real_t x[3];
|
||||
Vector transip(x, 3);
|
||||
|
||||
T.Transform(ip, transip);
|
||||
|
||||
V.SetSize(vdim);
|
||||
if (Function)
|
||||
{
|
||||
Function(transip, V);
|
||||
}
|
||||
else
|
||||
{
|
||||
TDFunction(transip, GetTime(), V);
|
||||
}
|
||||
if (Q)
|
||||
{
|
||||
V *= Q->Eval(T, ip, GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace mfem
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
// 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_COMPLEX_COEFFICIENT
|
||||
#define MFEM_COMPLEX_COEFFICIENT
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/linalg.hpp"
|
||||
#include "coefficient.hpp"
|
||||
#include "intrules.hpp"
|
||||
#include "eltrans.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ComplexCoefficient;
|
||||
class ComplexVectorCoefficient;
|
||||
class ComplexMatrixCoefficient;
|
||||
|
||||
/// Standard Coefficient which returns the real part of a ComplexCoefficient
|
||||
class RealPartCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
ComplexCoefficient &complex_coef_;
|
||||
|
||||
public:
|
||||
RealPartCoefficient(ComplexCoefficient & complex_coef)
|
||||
: complex_coef_(complex_coef) {}
|
||||
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Standard Coefficient which returns the imaginary part of a
|
||||
/// ComplexCoefficient
|
||||
class ImagPartCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
ComplexCoefficient &complex_coef_;
|
||||
|
||||
public:
|
||||
ImagPartCoefficient(ComplexCoefficient & complex_coef)
|
||||
: complex_coef_(complex_coef) {}
|
||||
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef ImagPartCoefficient ImaginaryPartCoefficient;
|
||||
|
||||
class RealPartVectorCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexVectorCoefficient &complex_vcoef_;
|
||||
mutable ComplexVector val_;
|
||||
|
||||
public:
|
||||
RealPartVectorCoefficient(ComplexVectorCoefficient & complex_vcoef);
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class ImagPartVectorCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexVectorCoefficient &complex_vcoef_;
|
||||
mutable ComplexVector val_;
|
||||
|
||||
public:
|
||||
ImagPartVectorCoefficient(ComplexVectorCoefficient & complex_vcoef);
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef ImagPartVectorCoefficient ImaginaryPartVectorCoefficient;
|
||||
|
||||
class RealPartMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexMatrixCoefficient &complex_mcoef_;
|
||||
mutable ComplexTypeDenseMatrix val_;
|
||||
|
||||
public:
|
||||
RealPartMatrixCoefficient(ComplexMatrixCoefficient & complex_mcoef);
|
||||
|
||||
void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class ImagPartMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexMatrixCoefficient &complex_mcoef_;
|
||||
mutable ComplexTypeDenseMatrix val_;
|
||||
|
||||
public:
|
||||
ImagPartMatrixCoefficient(ComplexMatrixCoefficient & complex_mcoef);
|
||||
|
||||
void Eval(DenseMatrix &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef ImagPartMatrixCoefficient ImaginaryPartMatrixCoefficient;
|
||||
|
||||
/** @brief Base class ComplexCoefficients that optionally depend on space and
|
||||
time. These are used by the SesquilinearForm, ComplexLinearForm, and
|
||||
ComplexGridFunction classes to represent the physical coefficients in
|
||||
the PDEs that are being discretized. This class can also be used in a more
|
||||
general way to represent functions that don't necessarily belong to a FE
|
||||
space, e.g., to project onto ComplexGridFunctions to use as initial
|
||||
conditions, exact solutions, etc. See, e.g., ex22 for these uses. */
|
||||
class ComplexCoefficient
|
||||
{
|
||||
protected:
|
||||
real_t time;
|
||||
|
||||
private:
|
||||
RealPartCoefficient re_part_coef_;
|
||||
ImagPartCoefficient im_part_coef_;
|
||||
|
||||
protected:
|
||||
Coefficient &real_coef_;
|
||||
Coefficient &imag_coef_;
|
||||
|
||||
public:
|
||||
|
||||
ComplexCoefficient();
|
||||
ComplexCoefficient(Coefficient &c_r, Coefficient &c_i);
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(real_t t)
|
||||
{ time = t; real_coef_.SetTime(t); imag_coef_.SetTime(t); }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/** @brief Evaluate the coefficient in the element described by @a T at the
|
||||
point @a ip. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/** @brief Evaluate the coefficient in the element described by @a T at the
|
||||
point @a ip at time @a t. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip, real_t t)
|
||||
{
|
||||
SetTime(t);
|
||||
return Eval(T, ip);
|
||||
}
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the real part of
|
||||
the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its real part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual Coefficient & real() { return real_coef_; }
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the imaginary
|
||||
part of the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its imaginary part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual Coefficient & imag() { return imag_coef_; }
|
||||
|
||||
virtual ~ComplexCoefficient() { }
|
||||
};
|
||||
|
||||
/** @brief Base class ComplexVectorCoefficients that optionally depend
|
||||
on space and time. These are used by the SesquilinearForm,
|
||||
ComplexLinearForm, and ComplexGridFunction classes to represent
|
||||
the physical vector-valued coefficients in the PDEs that are being
|
||||
discretized. This class can also be used in a more general way to
|
||||
represent functions that don't necessarily belong to a FE space,
|
||||
e.g., to project onto ComplexGridFunctions to use as initial
|
||||
conditions, exact solutions, etc. See, e.g., ex22 for these
|
||||
uses. */
|
||||
class ComplexVectorCoefficient
|
||||
{
|
||||
protected:
|
||||
int vdim;
|
||||
real_t time;
|
||||
|
||||
private:
|
||||
RealPartVectorCoefficient re_part_vcoef_;
|
||||
ImagPartVectorCoefficient im_part_vcoef_;
|
||||
|
||||
protected:
|
||||
VectorCoefficient &real_vcoef_;
|
||||
VectorCoefficient &imag_vcoef_;
|
||||
|
||||
mutable Vector V_r_;
|
||||
mutable Vector V_i_;
|
||||
|
||||
public:
|
||||
ComplexVectorCoefficient(int vd)
|
||||
: vdim(vd), time(0.),
|
||||
re_part_vcoef_(*this), im_part_vcoef_(*this),
|
||||
real_vcoef_(re_part_vcoef_), imag_vcoef_(im_part_vcoef_)
|
||||
{ }
|
||||
|
||||
ComplexVectorCoefficient(VectorCoefficient &v_r, VectorCoefficient &v_i);
|
||||
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(real_t t)
|
||||
{ time = t; real_vcoef_.SetTime(t); imag_vcoef_.SetTime(t); }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/// Returns dimension of the vector.
|
||||
int GetVDim() { return vdim; }
|
||||
|
||||
/** @brief Evaluate the vector coefficient in the element described by @a T
|
||||
at the point @a ip, storing the result in @a V. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/** @brief Evaluate the vector coefficient in the element described by @a T
|
||||
at the point @a ip at time @a t, storing the result in @a V. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip, real_t t)
|
||||
{
|
||||
SetTime(t);
|
||||
Eval(V, T, ip);
|
||||
}
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the real part of
|
||||
the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its real part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual VectorCoefficient & real() { return real_vcoef_; }
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the imaginary
|
||||
part of the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its imaginary part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual VectorCoefficient & imag() { return imag_vcoef_; }
|
||||
|
||||
virtual ~ComplexVectorCoefficient() { }
|
||||
};
|
||||
|
||||
/** @brief Base class ComplexMatrixCoefficients that optionally depend
|
||||
on space and time. These are used by the SesquilinearForm,
|
||||
ComplexLinearForm, and ComplexGridFunction classes to represent
|
||||
the physical matrix-valued coefficients in the PDEs that are being
|
||||
discretized. This class can also be used in a more general way to
|
||||
represent functions that don't necessarily belong to a FE space.
|
||||
See, e.g., ex22 for these uses. */
|
||||
class ComplexMatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
int height, width;
|
||||
real_t time;
|
||||
|
||||
private:
|
||||
RealPartMatrixCoefficient re_part_mcoef_;
|
||||
ImagPartMatrixCoefficient im_part_mcoef_;
|
||||
|
||||
protected:
|
||||
MatrixCoefficient &real_mcoef_;
|
||||
MatrixCoefficient &imag_mcoef_;
|
||||
|
||||
mutable DenseMatrix M_r_;
|
||||
mutable DenseMatrix M_i_;
|
||||
|
||||
public:
|
||||
/// Construct a dim x dim matrix coefficient.
|
||||
explicit ComplexMatrixCoefficient(int dim)
|
||||
: height(dim), width(dim), time(0.),
|
||||
re_part_mcoef_(*this), im_part_mcoef_(*this),
|
||||
real_mcoef_(re_part_mcoef_), imag_mcoef_(im_part_mcoef_)
|
||||
{ }
|
||||
|
||||
/// Construct a h x w matrix coefficient.
|
||||
ComplexMatrixCoefficient(int h, int w) :
|
||||
height(h), width(w), time(0.),
|
||||
re_part_mcoef_(*this), im_part_mcoef_(*this),
|
||||
real_mcoef_(re_part_mcoef_), imag_mcoef_(im_part_mcoef_)
|
||||
{ }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
virtual void SetTime(real_t t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/// Get the height of the matrix.
|
||||
int GetHeight() const { return height; }
|
||||
|
||||
/// Get the width of the matrix.
|
||||
int GetWidth() const { return width; }
|
||||
|
||||
/// For backward compatibility get the width of the matrix.
|
||||
int GetVDim() const { return width; }
|
||||
|
||||
/** @brief Evaluate the matrix coefficient in the element described by @a T
|
||||
at the point @a ip, storing the result in @a K. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual void Eval(ComplexTypeDenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) = 0;
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the real part of
|
||||
the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its real part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual MatrixCoefficient & real() { return real_mcoef_; }
|
||||
|
||||
/** @brief Access a standard Coefficient object reproducing the imaginary
|
||||
part of the complex-valued field */
|
||||
/** @note By default this method returns an internal object which
|
||||
computes the complex value using the above Eval method and
|
||||
returns its imaginary part. Custom implementations may choose to
|
||||
override this method with a more efficient real-valued
|
||||
coefficient. */
|
||||
virtual MatrixCoefficient & imag() { return imag_mcoef_; }
|
||||
|
||||
virtual ~ComplexMatrixCoefficient() { }
|
||||
};
|
||||
|
||||
/// A complex-valued coefficient that is constant across space and time
|
||||
class ComplexConstantCoefficient : public ComplexCoefficient
|
||||
{
|
||||
private:
|
||||
complex_t val;
|
||||
|
||||
ConstantCoefficient real_coef;
|
||||
ConstantCoefficient imag_coef;
|
||||
|
||||
public:
|
||||
ComplexConstantCoefficient(const complex_t z);
|
||||
|
||||
ComplexConstantCoefficient(real_t z_r, real_t z_i = 0.);
|
||||
|
||||
complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) { return val; }
|
||||
};
|
||||
|
||||
/// Complex-valued vector coefficient that is constant in space and time.
|
||||
class ComplexVectorConstantCoefficient : public ComplexVectorCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexVector vec;
|
||||
|
||||
public:
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexVectorConstantCoefficient(const ComplexVector &v)
|
||||
: ComplexVectorCoefficient(v.Size()), vec(v) { }
|
||||
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexVectorConstantCoefficient(const Vector &v)
|
||||
: ComplexVectorCoefficient(v.Size()), vec(v) { }
|
||||
|
||||
using ComplexVectorCoefficient::Eval;
|
||||
|
||||
/// Evaluate the vector coefficient at @a ip.
|
||||
void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override { V = vec; }
|
||||
|
||||
/// Return a reference to the constant vector in this class.
|
||||
const ComplexVector& GetVec() const { return vec; }
|
||||
};
|
||||
|
||||
/// Complex-valued vector coefficient that is constant in space and time.
|
||||
class ComplexMatrixConstantCoefficient : public ComplexMatrixCoefficient
|
||||
{
|
||||
private:
|
||||
ComplexTypeDenseMatrix mat;
|
||||
|
||||
public:
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexMatrixConstantCoefficient(const ComplexTypeDenseMatrix &m)
|
||||
: ComplexMatrixCoefficient(m.Height(), m.Width()), mat(m) { }
|
||||
|
||||
/// Construct the coefficient with constant vector @a v.
|
||||
ComplexMatrixConstantCoefficient(const DenseMatrix &m)
|
||||
: ComplexMatrixCoefficient(m.Height(), m.Width()), mat(m) { }
|
||||
|
||||
using ComplexMatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
void Eval(ComplexTypeDenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override { M = mat; }
|
||||
|
||||
/// Return a reference to the constant matrix in this class.
|
||||
const ComplexTypeDenseMatrix& GetMat() const { return mat; }
|
||||
};
|
||||
|
||||
/// A general complex-valued function coefficient
|
||||
class ComplexFunctionCoefficient : public ComplexCoefficient
|
||||
{
|
||||
protected:
|
||||
std::function<complex_t(const Vector &)> Function;
|
||||
std::function<complex_t(const Vector &, real_t)> TDFunction;
|
||||
|
||||
public:
|
||||
/// Define a time-independent coefficient from a std function
|
||||
/** \param F time-independent std::function */
|
||||
ComplexFunctionCoefficient(std::function<complex_t
|
||||
(const Vector &)> F)
|
||||
: Function(std::move(F))
|
||||
{ }
|
||||
|
||||
/// Define a time-dependent coefficient from a std function
|
||||
/** \param TDF time-dependent function */
|
||||
ComplexFunctionCoefficient(std::function<complex_t
|
||||
(const Vector &, real_t)> TDF)
|
||||
: TDFunction(std::move(TDF))
|
||||
{ }
|
||||
|
||||
/// (DEPRECATED) Define a time-independent coefficient from a C-function
|
||||
/** @deprecated Use the method where the C-function, @a f, uses a const
|
||||
Vector argument instead of Vector. */
|
||||
MFEM_DEPRECATED ComplexFunctionCoefficient(complex_t
|
||||
(*f)(Vector &))
|
||||
{
|
||||
// Cast first to (void*) to suppress a warning from newer version of
|
||||
// Clang when using -Wextra.
|
||||
Function = reinterpret_cast<complex_t(*)
|
||||
(const Vector&)>((void*)f);
|
||||
TDFunction = NULL;
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
|
||||
/** @deprecated Use the method where the C-function, @a tdf, uses a const
|
||||
Vector argument instead of Vector. */
|
||||
MFEM_DEPRECATED ComplexFunctionCoefficient(complex_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<complex_t(*)(const Vector&,
|
||||
real_t)>((void*)tdf);
|
||||
}
|
||||
|
||||
/// Evaluate the coefficient at @a ip.
|
||||
complex_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// A general vector function coefficient
|
||||
class ComplexVectorFunctionCoefficient : public ComplexVectorCoefficient
|
||||
{
|
||||
private:
|
||||
std::function<void(const Vector &, ComplexVector &)> Function;
|
||||
std::function<void(const Vector &, real_t, ComplexVector &)> TDFunction;
|
||||
ComplexCoefficient *Q;
|
||||
|
||||
public:
|
||||
/// Define a time-independent complex-valued vector coefficient
|
||||
/// from a std function
|
||||
/** \param dim - the size of the vector
|
||||
\param F - time-independent function
|
||||
\param q - optional scalar Coefficient to scale the vector coefficient */
|
||||
ComplexVectorFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &,
|
||||
ComplexVector &)> F,
|
||||
ComplexCoefficient *q = nullptr)
|
||||
: ComplexVectorCoefficient(dim), Function(std::move(F)), Q(q)
|
||||
{ }
|
||||
|
||||
/// Define a time-dependent complex-valued vector coefficient from
|
||||
/// a std function
|
||||
/** \param dim - the size of the vector
|
||||
\param TDF - time-dependent function
|
||||
\param q - optional scalar ComplexCoefficient to scale the vector coefficient */
|
||||
ComplexVectorFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &, real_t,
|
||||
ComplexVector &)> TDF,
|
||||
ComplexCoefficient *q = nullptr)
|
||||
: ComplexVectorCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
|
||||
{ }
|
||||
|
||||
using ComplexVectorCoefficient::Eval;
|
||||
/// Evaluate the vector coefficient at @a ip.
|
||||
void Eval(ComplexVector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~ComplexVectorFunctionCoefficient() { }
|
||||
};
|
||||
|
||||
} // end namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -96,6 +96,23 @@ ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff)
|
||||
{
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectCoefficient(real_coeff);
|
||||
*gfi = 0.0;
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(ComplexCoefficient &coeff)
|
||||
{
|
||||
this->ProjectCoefficient(coeff.real(), coeff.imag());
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
VectorCoefficient &imag_vcoeff)
|
||||
@@ -108,6 +125,23 @@ ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff)
|
||||
{
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectCoefficient(real_vcoeff);
|
||||
*gfi = 0.0;
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectCoefficient(ComplexVectorCoefficient &vcoeff)
|
||||
{
|
||||
this->ProjectCoefficient(vcoeff.real(), vcoeff.imag());
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Coefficient &imag_coeff,
|
||||
@@ -121,6 +155,26 @@ ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
ConstantCoefficient zero_coeff(0.0);
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectBdrCoefficient(real_coeff, attr);
|
||||
gfi->ProjectBdrCoefficient(zero_coeff, attr);
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficient(ComplexCoefficient &coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
this->ProjectBdrCoefficient(coeff.real(), coeff.imag(), attr);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
|
||||
VectorCoefficient &imag_vcoeff,
|
||||
@@ -134,6 +188,28 @@ ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
|
||||
gfi->ProjectBdrCoefficientNormal(zero_vcoeff, attr);
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientNormal(
|
||||
ComplexVectorCoefficient &vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
this->ProjectBdrCoefficientNormal(vcoeff.real(), vcoeff.imag(), attr);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
@@ -149,6 +225,80 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
gfr->SyncMemory(*this);
|
||||
gfi->SyncMemory(*this);
|
||||
gfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
|
||||
gfi->ProjectBdrCoefficientTangent(zero_vcoeff, attr);
|
||||
gfr->SyncAliasMemory(*this);
|
||||
gfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ComplexGridFunction::ProjectBdrCoefficientTangent(
|
||||
ComplexVectorCoefficient &vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
this->ProjectBdrCoefficientTangent(vcoeff.real(), vcoeff.imag(), attr);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(Coefficient &re_exsol,
|
||||
Coefficient &im_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(im_exsol, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(Coefficient &re_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
ConstantCoefficient zero_coef(0.0);
|
||||
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(zero_coef, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(VectorCoefficient &re_exsol,
|
||||
VectorCoefficient &im_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(im_exsol, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ComplexGridFunction::ComputeL2Error(VectorCoefficient &re_exsol,
|
||||
const IntegrationRule *irs[],
|
||||
const Array<int> *elems) const
|
||||
{
|
||||
Vector zero_vec(re_exsol.GetVDim()); zero_vec = 0.0;
|
||||
VectorConstantCoefficient zero_coef(zero_vec);
|
||||
|
||||
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
|
||||
real_t err_i = gfi->ComputeL2Error(zero_coef, irs, elems);
|
||||
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
|
||||
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *fes,
|
||||
ComplexOperator::Convention convention)
|
||||
@@ -731,6 +881,17 @@ ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff)
|
||||
{
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectCoefficient(real_coeff);
|
||||
*pgfi = 0.0;
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
VectorCoefficient &imag_vcoeff)
|
||||
@@ -743,6 +904,17 @@ ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff)
|
||||
{
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectCoefficient(real_vcoeff);
|
||||
*pgfi = 0.0;
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Coefficient &imag_coeff,
|
||||
@@ -756,6 +928,19 @@ ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
ConstantCoefficient zero_coeff(0.0);
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectBdrCoefficient(real_coeff, attr);
|
||||
pgfi->ProjectBdrCoefficient(zero_coeff, attr);
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
@@ -771,6 +956,21 @@ ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
|
||||
pgfi->ProjectBdrCoefficientNormal(zero_vcoeff, attr);
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
@@ -786,6 +986,21 @@ ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
|
||||
&real_vcoeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
|
||||
VectorConstantCoefficient zero_vcoeff(zero_vec);
|
||||
pgfr->SyncMemory(*this);
|
||||
pgfi->SyncMemory(*this);
|
||||
pgfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
|
||||
pgfi->ProjectBdrCoefficientTangent(zero_vcoeff, attr);
|
||||
pgfr->SyncAliasMemory(*this);
|
||||
pgfi->SyncAliasMemory(*this);
|
||||
}
|
||||
|
||||
void
|
||||
ParComplexGridFunction::Distribute(const Vector *tv)
|
||||
{
|
||||
@@ -825,6 +1040,31 @@ ParComplexGridFunction::ParallelProject(Vector &tv) const
|
||||
tvi.SyncAliasMemory(tv);
|
||||
}
|
||||
|
||||
real_t
|
||||
ParComplexGridFunction::ComputeL2Error(Coefficient &exsolr,
|
||||
const IntegrationRule *irs[],
|
||||
Array<int> *elems) const
|
||||
{
|
||||
ConstantCoefficient zeroCoef(0.0);
|
||||
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = pgfi->ComputeL2Error(zeroCoef, irs, elems);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
real_t
|
||||
ParComplexGridFunction::ComputeL2Error(VectorCoefficient &exsolr,
|
||||
const IntegrationRule *irs[],
|
||||
Array<int> *elems) const
|
||||
{
|
||||
Vector zeroVec(exsolr.GetVDim()); zeroVec = 0.0;
|
||||
VectorConstantCoefficient zeroCoef(zeroVec);
|
||||
|
||||
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
|
||||
real_t err_i = pgfi->ComputeL2Error(zeroCoef, irs, elems);
|
||||
return sqrt(err_r * err_r + err_i * err_i);
|
||||
}
|
||||
|
||||
|
||||
ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
|
||||
ComplexOperator::Convention
|
||||
|
||||
+1307
-21
File diff suppressed because it is too large
Load Diff
+12
-10
@@ -764,9 +764,9 @@ ParaViewDataCollectionBase::ParaViewDataCollectionBase(
|
||||
{
|
||||
cycle = 0;
|
||||
#ifdef MFEM_USE_ZLIB
|
||||
compression = true; // if we have zlib, enable compression
|
||||
#else
|
||||
compression = false; // otherwise, disable compression
|
||||
// If we have zlib, enable compression. Otherwise, compression is disabled in
|
||||
// the DataCollection base class constructor.
|
||||
compression = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -784,13 +784,8 @@ 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_;
|
||||
compression = compression_level_ != 0;
|
||||
}
|
||||
|
||||
void ParaViewDataCollectionBase::SetCompression(bool compression_)
|
||||
{
|
||||
compression = compression_;
|
||||
}
|
||||
|
||||
int ParaViewDataCollectionBase::GetCompressionLevel() const
|
||||
@@ -1174,7 +1169,14 @@ const char *ParaViewDataCollection::GetDataTypeString() const
|
||||
ParaViewHDFDataCollection::ParaViewHDFDataCollection(
|
||||
const std::string &collection_name, Mesh *mesh)
|
||||
: ParaViewDataCollectionBase(collection_name, mesh)
|
||||
{ }
|
||||
{
|
||||
compression = true;
|
||||
}
|
||||
|
||||
void ParaViewHDFDataCollection::SetCompression(bool compression_)
|
||||
{
|
||||
compression = compression_;
|
||||
}
|
||||
|
||||
void ParaViewHDFDataCollection::EnsureVTKHDF()
|
||||
{
|
||||
|
||||
@@ -537,13 +537,6 @@ public:
|
||||
/// Any nonzero compression level will enable compression.
|
||||
void SetCompressionLevel(int compression_level_);
|
||||
|
||||
/// @brief 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;
|
||||
|
||||
/// @brief Sets whether or not to output the data as high-order elements
|
||||
/// (false by default).
|
||||
///
|
||||
@@ -633,6 +626,12 @@ public:
|
||||
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;
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ public:
|
||||
{
|
||||
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
|
||||
prolongation(solutions, solutions_t, solutions_l);
|
||||
residual_l = 0.0;
|
||||
for (auto &action : action_callbacks)
|
||||
{
|
||||
action(solutions_l, parameters_l, residual_l);
|
||||
|
||||
+5
-4
@@ -987,7 +987,7 @@ get_restriction_transpose(
|
||||
{
|
||||
auto RT = [=](const Vector &v_e, Vector &v_l)
|
||||
{
|
||||
v_l = v_e;
|
||||
v_l += v_e;
|
||||
};
|
||||
return std::make_tuple(RT, 1);
|
||||
}
|
||||
@@ -996,7 +996,7 @@ get_restriction_transpose(
|
||||
const Operator *R = get_restriction<entity_t>(f, o);
|
||||
std::function<void(const Vector&, Vector&)> RT = [=](const Vector &x, Vector &y)
|
||||
{
|
||||
R->MultTranspose(x, y);
|
||||
R->AddMultTranspose(x, y);
|
||||
};
|
||||
return std::make_tuple(RT, R->Height());
|
||||
}
|
||||
@@ -1708,6 +1708,7 @@ std::array<DofToQuadMap, N> load_dtq_mem(
|
||||
const auto B = Reshape(&dtq[i].B[0], nqp_b, dim_b, ndof_b);
|
||||
auto mem_Bi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_b, dim_b,
|
||||
ndof_b);
|
||||
|
||||
MFEM_FOREACH_THREAD(q, x, nqp_b)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d, y, ndof_b)
|
||||
@@ -2158,7 +2159,7 @@ template <
|
||||
std::size_t... Is>
|
||||
std::array<DofToQuadMap, N> create_dtq_maps_impl(
|
||||
field_operator_ts &fops,
|
||||
std::vector<const DofToQuad*> dtqs,
|
||||
std::vector<const DofToQuad*> &dtqs,
|
||||
const std::array<int, N> &field_map,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
@@ -2243,7 +2244,7 @@ template <
|
||||
std::size_t num_fields>
|
||||
std::array<DofToQuadMap, num_fields> create_dtq_maps(
|
||||
field_operator_ts &fops,
|
||||
std::vector<const DofToQuad*> dtqmaps,
|
||||
std::vector<const DofToQuad*> &dtqmaps,
|
||||
const std::array<int, num_fields> &to_field_map)
|
||||
{
|
||||
return create_dtq_maps_impl<entity_t>(
|
||||
|
||||
+1
-1
@@ -4334,7 +4334,7 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
u.GetSubVector(udofs, ul);
|
||||
utrans.InvTransformPrimal(ul);
|
||||
Transf = ufes->GetElementTransformation(ielem);
|
||||
FiniteElement *dummy = nullptr;
|
||||
const auto *dummy = ufes->GetFE(ielem);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
|
||||
*dummy, fl, with_coeff, ir);
|
||||
|
||||
|
||||
@@ -1009,6 +1009,7 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
|
||||
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
@@ -1038,11 +1039,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
real_t (*QDD0)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+0);
|
||||
real_t (*QDD1)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+1);
|
||||
real_t (*QDD2)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+2);
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
}
|
||||
@@ -1050,9 +1051,9 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
B[qx][dy] = b(qx,dy);
|
||||
G[qx][dy] = g(qx,dy);
|
||||
@@ -1060,11 +1061,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1080,11 +1081,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1101,11 +1102,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1136,9 +1137,9 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
MFEM_SYNC_THREAD;
|
||||
if (MFEM_THREAD_ID(z) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
|
||||
{
|
||||
Bt[dy][qx] = b(qx,dy);
|
||||
Gt[dy][qx] = g(qx,dy);
|
||||
@@ -1146,11 +1147,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -1167,11 +1168,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(Q1D)
|
||||
@@ -1188,11 +1189,11 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0, v = 0.0, w = 0.0;
|
||||
MFEM_UNROLL(MQ1)
|
||||
|
||||
@@ -62,7 +62,7 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
|
||||
const int NE = ne;
|
||||
const int Q1D = quad1D;
|
||||
const int NQ = pow(Q1D, dim);
|
||||
const int NQ = static_cast<int>(std::pow(Q1D, dim));
|
||||
const bool const_c = coeff.Size() == 1;
|
||||
const bool by_val = map_type == FiniteElement::VALUE;
|
||||
const auto W = Reshape(ir->GetWeights().Read(), NQ);
|
||||
|
||||
+1
-1
@@ -673,7 +673,7 @@ public:
|
||||
int myid;
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
|
||||
int seed = (seed_ > 0) ? seed_ + myid : (int)time(0) + myid;
|
||||
int seed = (seed_ > 0) ? seed_ + myid : time(nullptr) + myid;
|
||||
SetSeed(seed);
|
||||
}
|
||||
#else
|
||||
|
||||
+2
-2
@@ -5259,7 +5259,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
gc.GetNeighborLTDofTable(nbr_ltdof);
|
||||
const int nb_connections = nbr_ltdof.Size_of_connections();
|
||||
shr_ltdof.SetSize(nb_connections);
|
||||
shr_ltdof.CopyFrom(nbr_ltdof.GetJ());
|
||||
if (nb_connections > 0) { shr_ltdof.CopyFrom(nbr_ltdof.GetJ()); }
|
||||
shr_buf.SetSize(nb_connections);
|
||||
shr_buf.UseDevice(true);
|
||||
shr_buf_offsets = nbr_ltdof.GetIMemory();
|
||||
@@ -5288,7 +5288,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
gc.GetNeighborLDofTable(nbr_ldof);
|
||||
const int nb_connections = nbr_ldof.Size_of_connections();
|
||||
ext_ldof.SetSize(nb_connections);
|
||||
ext_ldof.CopyFrom(nbr_ldof.GetJ());
|
||||
if (nb_connections > 0) { ext_ldof.CopyFrom(nbr_ldof.GetJ()); }
|
||||
ext_ldof.GetMemory().UseDevice(true);
|
||||
ext_buf.SetSize(nb_connections);
|
||||
ext_buf.UseDevice(true);
|
||||
|
||||
+3
-280
@@ -9,278 +9,16 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../quadinterpolator.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../fem/kernels.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
#include "det.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
namespace quadrature_interpolator
|
||||
{
|
||||
|
||||
static void Det1D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d,
|
||||
const int q1d,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b);
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
const auto G = Reshape(g, q1d, d1d);
|
||||
const auto X = Reshape(x, d1d, NE);
|
||||
|
||||
auto Y = Reshape(y, q1d, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
for (int q = 0; q < q1d; q++)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int d = 0; d < d1d; d++)
|
||||
{
|
||||
u += G(q, d) * X(d, e);
|
||||
}
|
||||
Y(q, e) = u;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void Det2D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
static constexpr int SDIM = 2;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XY[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
MFEM_SHARED real_t QQ[2*SDIM][NBZ][MQ1*MQ1];
|
||||
|
||||
kernels::internal::LoadX<MD1,NBZ>(e,D1D,X,XY);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1,NBZ>(D1D,Q1D,BG,XY,DQ);
|
||||
kernels::internal::GradY<MD1,MQ1,NBZ>(D1D,Q1D,BG,DQ,QQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[4];
|
||||
kernels::internal::PullGrad<MQ1,NBZ>(Q1D,qx,qy,QQ,J);
|
||||
Y(qx,qy,e) = kernels::Det<2>(J);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void Det2DSurface(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
|
||||
static constexpr int SDIM = 3;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XYZ[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
// Load XYZ components
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
XYZ[d][tidz][dx + dy*D1D] = X(dx,dy,d,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
ConstDeviceMatrix B_mat(BG[0], D1D, Q1D);
|
||||
ConstDeviceMatrix G_mat(BG[1], D1D, Q1D);
|
||||
|
||||
// x contraction
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const real_t xval = XYZ[d][tidz][dx + dy*D1D];
|
||||
u += xval * G_mat(dx,qx);
|
||||
v += xval * B_mat(dx,qx);
|
||||
}
|
||||
DQ[d][tidz][dy + qx*D1D] = u;
|
||||
DQ[3 + d][tidz][dy + qx*D1D] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// y contraction and determinant computation
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
J_[d] += DQ[d][tidz][dy + qx*D1D] * B_mat(dy,qy);
|
||||
J_[3 + d] += DQ[3 + d][tidz][dy + qx*D1D] * G_mat(dy,qy);
|
||||
}
|
||||
}
|
||||
DeviceTensor<2> J(J_, 3, 2);
|
||||
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
|
||||
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
|
||||
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
|
||||
Y(qx,qy,e) = std::sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, bool SMEM = true>
|
||||
static void Det3D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr) // used only with SMEM = false
|
||||
{
|
||||
constexpr int DIM = 3;
|
||||
static constexpr int GRID = SMEM ? 0 : 128;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, D1D, DIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, Q1D, NE);
|
||||
|
||||
real_t *GM = nullptr;
|
||||
if (!SMEM)
|
||||
{
|
||||
const DeviceDofQuadLimits &limits = DeviceDofQuadLimits::Get();
|
||||
const int max_q1d = T_Q1D ? T_Q1D : limits.MAX_Q1D;
|
||||
const int max_d1d = T_D1D ? T_D1D : limits.MAX_D1D;
|
||||
const int max_qd = std::max(max_q1d, max_d1d);
|
||||
const int mem_size = max_qd * max_qd * max_qd * 9;
|
||||
d_buff->SetSize(2*mem_size*GRID);
|
||||
GM = d_buff->Write();
|
||||
}
|
||||
|
||||
mfem::forall_3D_grid(NE, Q1D, Q1D, Q1D, GRID, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
static constexpr int MQ1 = T_Q1D ? T_Q1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_Q1D);
|
||||
static constexpr int MD1 = T_D1D ? T_D1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_D1D);
|
||||
static constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
|
||||
static constexpr int MSZ = MDQ * MDQ * MDQ * 9;
|
||||
|
||||
const int bid = MFEM_BLOCK_ID(x);
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t SM0[SMEM?MSZ:1];
|
||||
MFEM_SHARED real_t SM1[SMEM?MSZ:1];
|
||||
real_t *lm0 = SMEM ? SM0 : GM + MSZ*bid;
|
||||
real_t *lm1 = SMEM ? SM1 : GM + MSZ*(GRID+bid);
|
||||
real_t (*DDD)[MD1*MD1*MD1] = (real_t (*)[MD1*MD1*MD1]) (lm0);
|
||||
real_t (*DDQ)[MD1*MD1*MQ1] = (real_t (*)[MD1*MD1*MQ1]) (lm1);
|
||||
real_t (*DQQ)[MD1*MQ1*MQ1] = (real_t (*)[MD1*MQ1*MQ1]) (lm0);
|
||||
real_t (*QQQ)[MQ1*MQ1*MQ1] = (real_t (*)[MQ1*MQ1*MQ1]) (lm1);
|
||||
|
||||
kernels::internal::LoadX<MD1>(e,D1D,X,DDD);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1>(D1D,Q1D,BG,DDD,DDQ);
|
||||
kernels::internal::GradY<MD1,MQ1>(D1D,Q1D,BG,DDQ,DQQ);
|
||||
kernels::internal::GradZ<MD1,MQ1>(D1D,Q1D,BG,DQQ,QQQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[9];
|
||||
kernels::internal::PullGrad<MQ1>(Q1D, qx,qy,qz, QQQ, J);
|
||||
Y(qx,qy,qz,e) = kernels::Det<3>(J);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void InitDetKernels()
|
||||
{
|
||||
using k = QuadratureInterpolator::DetKernels;
|
||||
@@ -302,27 +40,12 @@ void InitDetKernels()
|
||||
}
|
||||
|
||||
} // namespace quadrature_interpolator
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
namespace
|
||||
{
|
||||
using DetKernel = QuadratureInterpolator::DetKernelType;
|
||||
}
|
||||
|
||||
template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
DetKernel QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
DetKernel QuadratureInterpolator::DetKernels::Fallback(
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Fallback(
|
||||
int DIM, int SDIM, int D1D, int Q1D)
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
// 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_QUADINTERP_DET_HPP
|
||||
#define MFEM_QUADINTERP_DET_HPP
|
||||
|
||||
#include "../quadinterpolator.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../fem/kernels.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
namespace quadrature_interpolator
|
||||
{
|
||||
|
||||
inline void Det1D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d,
|
||||
const int q1d,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b);
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
const auto G = Reshape(g, q1d, d1d);
|
||||
const auto X = Reshape(x, d1d, NE);
|
||||
|
||||
auto Y = Reshape(y, q1d, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
for (int q = 0; q < q1d; q++)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int d = 0; d < d1d; d++)
|
||||
{
|
||||
u += G(q, d) * X(d, e);
|
||||
}
|
||||
Y(q, e) = u;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void Det2D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
static constexpr int SDIM = 2;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XY[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
MFEM_SHARED real_t QQ[2*SDIM][NBZ][MQ1*MQ1];
|
||||
|
||||
kernels::internal::LoadX<MD1,NBZ>(e,D1D,X,XY);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1,NBZ>(D1D,Q1D,BG,XY,DQ);
|
||||
kernels::internal::GradY<MD1,MQ1,NBZ>(D1D,Q1D,BG,DQ,QQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[4];
|
||||
kernels::internal::PullGrad<MQ1,NBZ>(Q1D,qx,qy,QQ,J);
|
||||
Y(qx,qy,e) = kernels::Det<2>(J);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void Det2DSurface(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
|
||||
static constexpr int SDIM = 3;
|
||||
static constexpr int NBZ = 1;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t XYZ[SDIM][NBZ][MD1*MD1];
|
||||
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
|
||||
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
// Load XYZ components
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
XYZ[d][tidz][dx + dy*D1D] = X(dx,dy,d,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
ConstDeviceMatrix B_mat(BG[0], D1D, Q1D);
|
||||
ConstDeviceMatrix G_mat(BG[1], D1D, Q1D);
|
||||
|
||||
// x contraction
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const real_t xval = XYZ[d][tidz][dx + dy*D1D];
|
||||
u += xval * G_mat(dx,qx);
|
||||
v += xval * B_mat(dx,qx);
|
||||
}
|
||||
DQ[d][tidz][dy + qx*D1D] = u;
|
||||
DQ[3 + d][tidz][dy + qx*D1D] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// y contraction and determinant computation
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
J_[d] += DQ[d][tidz][dy + qx*D1D] * B_mat(dy,qy);
|
||||
J_[3 + d] += DQ[3 + d][tidz][dy + qx*D1D] * G_mat(dy,qy);
|
||||
}
|
||||
}
|
||||
DeviceTensor<2> J(J_, 3, 2);
|
||||
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
|
||||
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
|
||||
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
|
||||
Y(qx,qy,e) = std::sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, bool SMEM = true>
|
||||
inline void Det3D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr) // used only with SMEM = false
|
||||
{
|
||||
constexpr int DIM = 3;
|
||||
static constexpr int GRID = SMEM ? 0 : 128;
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto B = Reshape(b, Q1D, D1D);
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, D1D, D1D, DIM, NE);
|
||||
auto Y = Reshape(y, Q1D, Q1D, Q1D, NE);
|
||||
|
||||
real_t *GM = nullptr;
|
||||
if (!SMEM)
|
||||
{
|
||||
const DeviceDofQuadLimits &limits = DeviceDofQuadLimits::Get();
|
||||
const int max_q1d = T_Q1D ? T_Q1D : limits.MAX_Q1D;
|
||||
const int max_d1d = T_D1D ? T_D1D : limits.MAX_D1D;
|
||||
const int max_qd = std::max(max_q1d, max_d1d);
|
||||
const int mem_size = max_qd * max_qd * max_qd * 9;
|
||||
d_buff->SetSize(2*mem_size*GRID);
|
||||
GM = d_buff->Write();
|
||||
}
|
||||
|
||||
mfem::forall_3D_grid(NE, Q1D, Q1D, Q1D, GRID, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
static constexpr int MQ1 = T_Q1D ? T_Q1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_Q1D);
|
||||
static constexpr int MD1 = T_D1D ? T_D1D :
|
||||
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_D1D);
|
||||
static constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
|
||||
static constexpr int MSZ = MDQ * MDQ * MDQ * 9;
|
||||
|
||||
const int bid = MFEM_BLOCK_ID(x);
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
MFEM_SHARED real_t SM0[SMEM?MSZ:1];
|
||||
MFEM_SHARED real_t SM1[SMEM?MSZ:1];
|
||||
real_t *lm0 = SMEM ? SM0 : GM + MSZ*bid;
|
||||
real_t *lm1 = SMEM ? SM1 : GM + MSZ*(GRID+bid);
|
||||
real_t (*DDD)[MD1*MD1*MD1] = (real_t (*)[MD1*MD1*MD1]) (lm0);
|
||||
real_t (*DDQ)[MD1*MD1*MQ1] = (real_t (*)[MD1*MD1*MQ1]) (lm1);
|
||||
real_t (*DQQ)[MD1*MQ1*MQ1] = (real_t (*)[MD1*MQ1*MQ1]) (lm0);
|
||||
real_t (*QQQ)[MQ1*MQ1*MQ1] = (real_t (*)[MQ1*MQ1*MQ1]) (lm1);
|
||||
|
||||
kernels::internal::LoadX<MD1>(e,D1D,X,DDD);
|
||||
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
|
||||
|
||||
kernels::internal::GradX<MD1,MQ1>(D1D,Q1D,BG,DDD,DDQ);
|
||||
kernels::internal::GradY<MD1,MQ1>(D1D,Q1D,BG,DDQ,DQQ);
|
||||
kernels::internal::GradZ<MD1,MQ1>(D1D,Q1D,BG,DQQ,QQQ);
|
||||
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t J[9];
|
||||
kernels::internal::PullGrad<MQ1>(Q1D, qx,qy,qz, QQQ, J);
|
||||
Y(qx,qy,qz,e) = kernels::Det<3>(J);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace quadrature_interpolator
|
||||
} // namespace internal
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_QUADINTERP_DET_HPP
|
||||
+9
-27
@@ -5122,33 +5122,32 @@ real_t TMOP_Integrator::GetSurfaceFittingWeight()
|
||||
|
||||
void TMOP_Integrator::EnableNormalization(const GridFunction &x)
|
||||
{
|
||||
ComputeNormalizationEnergies(x, metric_normal, lim_normal, surf_fit_normal);
|
||||
ComputeNormalizationEnergies(x, metric_normal, lim_normal);
|
||||
metric_normal = 1.0 / metric_normal;
|
||||
lim_normal = 1.0 / lim_normal;
|
||||
//if (surf_fit_gf) { surf_fit_normal = 1.0 / surf_fit_normal; }
|
||||
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
|
||||
{
|
||||
real_t loc[3];
|
||||
ComputeNormalizationEnergies(x, loc[0], loc[1], loc[2]);
|
||||
real_t rdc[3];
|
||||
MPI_Allreduce(loc, rdc, 3, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
real_t loc[2];
|
||||
ComputeNormalizationEnergies(x, loc[0], loc[1]);
|
||||
real_t rdc[2];
|
||||
MPI_Allreduce(loc, rdc, 2, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
x.ParFESpace()->GetComm());
|
||||
metric_normal = 1.0 / rdc[0];
|
||||
lim_normal = 1.0 / rdc[1];
|
||||
// if (surf_fit_gf) { surf_fit_normal = 1.0 / rdc[2]; }
|
||||
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
real_t &metric_energy,
|
||||
real_t &lim_energy,
|
||||
real_t &surf_fit_gf_energy)
|
||||
real_t &lim_energy)
|
||||
{
|
||||
metric_energy = 0.0;
|
||||
lim_energy = 0.0;
|
||||
if (PA.enabled)
|
||||
{
|
||||
MFEM_VERIFY(PA.E.Size() > 0, "Must be called after AssemblePA!");
|
||||
@@ -5191,9 +5190,6 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
Jpr.SetSize(dim);
|
||||
Jpt.SetSize(dim);
|
||||
|
||||
metric_energy = 0.0;
|
||||
lim_energy = 0.0;
|
||||
surf_fit_gf_energy = 0.0;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -5225,21 +5221,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
|
||||
lim_energy += weight;
|
||||
}
|
||||
|
||||
// Normalization of the surface fitting term.
|
||||
if (surf_fit_gf)
|
||||
{
|
||||
Array<int> dofs;
|
||||
Vector sigma_e;
|
||||
surf_fit_gf->FESpace()->GetElementDofs(i, dofs);
|
||||
surf_fit_gf->GetSubVector(dofs, sigma_e);
|
||||
for (int s = 0; s < dofs.Size(); s++)
|
||||
{
|
||||
if ((*surf_fit_marker)[dofs[s]] == true)
|
||||
{
|
||||
surf_fit_gf_energy += sigma_e(s) * sigma_e(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Normalization of the surface fitting term.
|
||||
}
|
||||
|
||||
// Cases when integration is not over the target element, or when the
|
||||
|
||||
+1
-2
@@ -2038,8 +2038,7 @@ protected:
|
||||
} PA;
|
||||
|
||||
void ComputeNormalizationEnergies(const GridFunction &x,
|
||||
real_t &metric_energy, real_t &lim_energy,
|
||||
real_t &surf_fit_gf_energy);
|
||||
real_t &metric_energy, real_t &lim_energy);
|
||||
|
||||
void AssembleElementVectorExact(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
|
||||
@@ -39,6 +39,7 @@ list(APPEND HDRS
|
||||
arrays_by_name.hpp
|
||||
backends.hpp
|
||||
binaryio.hpp
|
||||
complex_type.hpp
|
||||
cuda.hpp
|
||||
device.hpp
|
||||
error.hpp
|
||||
|
||||
+5
-1
@@ -326,7 +326,11 @@ public:
|
||||
the Size to match this Capacity after this.*/
|
||||
template <typename U>
|
||||
inline void CopyFrom(const U *src)
|
||||
{ std::memcpy(begin(), src, MemoryUsage()); }
|
||||
{
|
||||
if (!begin() || size == 0) { return; }
|
||||
MFEM_ASSERT(begin() && src, "Error in Array::CopyFrom");
|
||||
std::memcpy(begin(), src, MemoryUsage());
|
||||
}
|
||||
|
||||
/// STL-like begin. Returns pointer to the first element of the array.
|
||||
inline T* begin() { return data; }
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#define MFEM_THREAD_ID(k) 0
|
||||
#define MFEM_THREAD_SIZE(k) 1
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=0; i<N; i++)
|
||||
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) MFEM_FOREACH_THREAD(i,k,N)
|
||||
#endif
|
||||
|
||||
// 'double' and 'float' atomicAdd implementation for previous versions of CUDA
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// 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_COMPLEX_TYPE
|
||||
#define MFEM_COMPLEX_TYPE
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
#include <complex>
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
#include <cuComplex.h>
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_HIP)
|
||||
#include <hip/hip_complex.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// @brief Complex number type for device.
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
|
||||
#define zAbs std::abs
|
||||
#define zExp std::exp
|
||||
#define zNorm std::norm
|
||||
using complex_t = std::complex<real_t>;
|
||||
|
||||
#else // CUDA or HIP
|
||||
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
using DoubleComplex_t = cuDoubleComplex;
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_HIP)
|
||||
using DoubleComplex_t = hipDoubleComplex;
|
||||
#endif
|
||||
|
||||
struct Complex : public DoubleComplex_t
|
||||
{
|
||||
MFEM_HOST_DEVICE Complex() = default;
|
||||
MFEM_HOST_DEVICE Complex(real_t r) { x = r, y = 0.0; }
|
||||
MFEM_HOST_DEVICE Complex(real_t r, real_t i) { x = r, y = i; }
|
||||
MFEM_HOST_DEVICE real_t real() const { return x; }
|
||||
MFEM_HOST_DEVICE void real(real_t r) { x = r; }
|
||||
MFEM_HOST_DEVICE real_t imag() const { return y; }
|
||||
MFEM_HOST_DEVICE void imag(real_t i) { y = i; }
|
||||
|
||||
template <typename U>
|
||||
MFEM_HOST_DEVICE inline Complex &operator*=(const U &z)
|
||||
{
|
||||
return *this = *this * z, *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
MFEM_HOST_DEVICE inline Complex &operator/=(const U &z)
|
||||
{
|
||||
return *this = *this / z, *this;
|
||||
}
|
||||
};
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator*(const Complex &x, const real_t &y)
|
||||
{
|
||||
return Complex(x.real() * y, x.imag() * y);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator+(const Complex &a, const Complex &b)
|
||||
{
|
||||
return Complex(a.real() + b.real(), a.imag() + b.imag());
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator*(const real_t d, const Complex &z)
|
||||
{
|
||||
return Complex(z.real() * d, z.imag() * d);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator*(const Complex &a, const Complex &b)
|
||||
{
|
||||
return Complex(a.real() * b.real() - a.imag() * b.imag(),
|
||||
a.real() * b.imag() + a.imag() * b.real());
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex operator/(const Complex &z, const real_t &d)
|
||||
{
|
||||
return Complex(z.real() / d, z.imag() / d);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline real_t zAbs(const Complex &z)
|
||||
{
|
||||
return std::hypot(z.real(), z.imag());
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline Complex zExp(const Complex &q)
|
||||
{
|
||||
Complex z;
|
||||
real_t s, c, e = std::exp(q.real());
|
||||
sincos(q.imag(), &s, &c);
|
||||
z.real(c * e), z.imag(s * e);
|
||||
return z;
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline real_t zNorm(const Complex &z)
|
||||
{
|
||||
return z.real() * z.real() + z.imag() * z.imag();
|
||||
}
|
||||
|
||||
using complex_t = Complex;
|
||||
#endif // MFEM_USE_CUDA || MFEM_USE_HIP
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_COMPLEX_TYPE
|
||||
@@ -47,6 +47,7 @@
|
||||
#define MFEM_THREAD_ID(k) threadIdx.k
|
||||
#define MFEM_THREAD_SIZE(k) blockDim.k
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=threadIdx.k; i<N; i+=blockDim.k)
|
||||
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) if(const int i=threadIdx.k; i<N)
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../fem/ceed/interface/util.hpp"
|
||||
#endif
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "communication.hpp"
|
||||
#include "../linalg/hypre.hpp"
|
||||
#endif
|
||||
|
||||
@@ -145,6 +146,11 @@ Device::Device()
|
||||
Configure(device);
|
||||
device_env = true;
|
||||
}
|
||||
|
||||
if (GetEnv("MFEM_GPU_AWARE_MPI"))
|
||||
{
|
||||
SetGPUAwareMPI(true);
|
||||
}
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
@@ -196,6 +202,29 @@ void Device::Configure(const std::string &device, const int device_id)
|
||||
{
|
||||
bmap[internal::backend_name[i]] = internal::backend_list[i];
|
||||
}
|
||||
// auto-detect GPU configurations
|
||||
// assumes only one of HIP or CUDA are available
|
||||
#ifdef MFEM_USE_HIP
|
||||
bmap["gpu"] = Backend::HIP;
|
||||
#ifdef MFEM_USE_RAJA
|
||||
bmap["raja-gpu"] = Backend::RAJA_HIP;
|
||||
#endif
|
||||
#ifdef MFEM_USE_CEED
|
||||
bmap["ceed-gpu"] = Backend::CEED_HIP;
|
||||
#endif
|
||||
// no OCCA+HIP?
|
||||
#elif defined(MFEM_USE_CUDA)
|
||||
bmap["gpu"] = Backend::CUDA;
|
||||
#ifdef MFEM_USE_RAJA
|
||||
bmap["raja-gpu"] = Backend::RAJA_CUDA;
|
||||
#endif
|
||||
#ifdef MFEM_USE_CEED
|
||||
bmap["ceed-gpu"] = Backend::CEED_CUDA;
|
||||
#endif
|
||||
#ifdef MFEM_USE_OCCA
|
||||
bmap["occa-gpu"] = Backend::OCCA_CUDA;
|
||||
#endif
|
||||
#endif
|
||||
std::string device_option;
|
||||
std::string::size_type beg = 0, end;
|
||||
while (1)
|
||||
@@ -313,6 +342,13 @@ void Device::Print(std::ostream &os)
|
||||
{
|
||||
os << ',' << MemoryTypeName[static_cast<int>(device_mem_type)];
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Allows(Backend::DEVICE_MASK) &&
|
||||
Mpi::IsInitialized() && !Mpi::IsFinalized())
|
||||
{
|
||||
os << "\nUse GPU-aware MPI: " << (GetGPUAwareMPI() ? "yes" : "no");
|
||||
}
|
||||
#endif
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +198,10 @@ public:
|
||||
'ceed-hip', 'hip', 'debug',
|
||||
'occa-omp', 'raja-omp', 'omp',
|
||||
'ceed-cpu', 'occa-cpu', 'raja-cpu', 'cpu'.
|
||||
- The following backend aliases are also available: 'ceed-gpu',
|
||||
'occa-gpu', 'raja-gpu', and 'gpu' where they alias their respective
|
||||
'*-cuda' or '*-hip' backends depending on the MFEM build-time
|
||||
configuration.
|
||||
- Multiple backends can be configured at the same time.
|
||||
- Only one 'occa-*' backend can be configured at a time.
|
||||
- The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
|
||||
|
||||
+3
-1
@@ -47,7 +47,9 @@
|
||||
#define MFEM_THREAD_ID(k) hipThreadIdx_ ##k
|
||||
#define MFEM_THREAD_SIZE(k) hipBlockDim_ ##k
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) \
|
||||
for(int i=hipThreadIdx_ ##k; i<N; i+=hipBlockDim_ ##k)
|
||||
for(int i=hipThreadIdx_ ##k; i<N; i+=hipBlockDim_ ##k)
|
||||
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) \
|
||||
if(const int i=hipThreadIdx_ ##k; i<N)
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
|
||||
@@ -21,6 +21,7 @@ list(APPEND SRCS
|
||||
blockvector.cpp
|
||||
complex_densemat.cpp
|
||||
complex_operator.cpp
|
||||
complex_vector.cpp
|
||||
constraints.cpp
|
||||
densemat.cpp
|
||||
symmat.cpp
|
||||
@@ -47,6 +48,7 @@ list(APPEND HDRS
|
||||
blockvector.hpp
|
||||
complex_densemat.hpp
|
||||
complex_operator.hpp
|
||||
complex_vector.hpp
|
||||
constraints.hpp
|
||||
densemat.hpp
|
||||
dinvariants.hpp
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../general/forall.hpp"
|
||||
#include "complex_densemat.hpp"
|
||||
#include "lapack.hpp"
|
||||
#include <complex>
|
||||
@@ -16,6 +17,8 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
DenseMatrix & ComplexDenseMatrix::real()
|
||||
{
|
||||
MFEM_ASSERT(Op_Real_, "ComplexDenseMatrix has no real part!");
|
||||
@@ -1017,4 +1020,303 @@ void ComplexCholeskyFactors::GetInverseMatrix(int m, real_t * X_r,
|
||||
delete [] X;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix()
|
||||
: height(0), width(0)
|
||||
{}
|
||||
|
||||
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(const ComplexTypeDenseMatrix &m)
|
||||
: height(m.Height()), width(m.Width())
|
||||
{
|
||||
const int hw = height * width;
|
||||
if (hw > 0)
|
||||
{
|
||||
MFEM_ASSERT(m.data, "invalid source matrix");
|
||||
data.New(hw);
|
||||
std::memcpy(data, m.data, sizeof(complex_t)*hw);
|
||||
}
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(const DenseMatrix &m)
|
||||
: height(m.Height()), width(m.Width())
|
||||
{
|
||||
const int hw = height * width;
|
||||
if (hw > 0)
|
||||
{
|
||||
MFEM_ASSERT(m.data, "invalid source matrix");
|
||||
data.New(hw);
|
||||
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = m.data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(int s)
|
||||
: height(s), width(s)
|
||||
{
|
||||
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
|
||||
if (s > 0)
|
||||
{
|
||||
data.New(s*s);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(int m, int n)
|
||||
: height(m), width(n)
|
||||
{
|
||||
MFEM_ASSERT(m >= 0 && n >= 0,
|
||||
"invalid DenseMatrix size: " << m << " x " << n);
|
||||
const int capacity = m*n;
|
||||
if (capacity > 0)
|
||||
{
|
||||
data.New(capacity);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexTypeDenseMatrix::SetSize(int h, int w)
|
||||
{
|
||||
MFEM_ASSERT(h >= 0 && w >= 0,
|
||||
"invalid ComplexTypeDenseMatrix size: " << h << " x " << w);
|
||||
if (Height() == h && Width() == w)
|
||||
{
|
||||
return;
|
||||
}
|
||||
height = h;
|
||||
width = w;
|
||||
const int hw = h*w;
|
||||
if (hw > data.Capacity())
|
||||
{
|
||||
data.Delete();
|
||||
data.New(hw);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
complex_t &ComplexTypeDenseMatrix::Elem(int i, int j)
|
||||
{
|
||||
return (*this)(i,j);
|
||||
}
|
||||
|
||||
/// Returns constant reference to a_{ij}.
|
||||
const complex_t &ComplexTypeDenseMatrix::Elem(int i, int j) const
|
||||
{
|
||||
return (*this)(i,j);
|
||||
}
|
||||
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(real_t c)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(complex_t c)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Copy the matrix entries from the given array
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(const real_t *d)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = d[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=
|
||||
(const complex_t *d)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = d[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Sets the matrix size and elements equal to those of m
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(const DenseMatrix &m)
|
||||
{
|
||||
SetSize(m.height, m.width);
|
||||
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=
|
||||
(const ComplexTypeDenseMatrix &m)
|
||||
{
|
||||
SetSize(m.height, m.width);
|
||||
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=(const real_t *m)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] += m[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=
|
||||
(const complex_t *m)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] += m[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=(const DenseMatrix &m)
|
||||
{
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] += m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=
|
||||
(const ComplexTypeDenseMatrix &m)
|
||||
{
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] += m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator-=(const DenseMatrix &m)
|
||||
{
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] -= m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator-=
|
||||
(const ComplexTypeDenseMatrix &m)
|
||||
{
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] -= m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator*=(real_t c)
|
||||
{
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] *= c;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator*=(complex_t c)
|
||||
{
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] *= c;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::Set(const DenseMatrix &Mr,
|
||||
const DenseMatrix &Mi)
|
||||
{
|
||||
MFEM_ASSERT(height == Mr.Height() && height == Mi.Height() &&
|
||||
width == Mr.Width() && width == Mi.Width(),
|
||||
"incompatible Matrices!");
|
||||
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = complex_t(Mr.data[i], Mi.data[i]);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ComplexTypeDenseMatrix::Swap(ComplexTypeDenseMatrix &other)
|
||||
{
|
||||
mfem::Swap(width, other.width);
|
||||
mfem::Swap(height, other.height);
|
||||
mfem::Swap(data, other.data);
|
||||
}
|
||||
|
||||
ComplexTypeDenseMatrix::~ComplexTypeDenseMatrix()
|
||||
{
|
||||
data.Delete();
|
||||
}
|
||||
|
||||
const DenseMatrix &ComplexTypeDenseMatrix::real() const
|
||||
{
|
||||
re_part.SetSize(height, width);
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
re_part.data[i] = data[i].real();
|
||||
}
|
||||
|
||||
return re_part;
|
||||
}
|
||||
|
||||
const DenseMatrix &ComplexTypeDenseMatrix::imag() const
|
||||
{
|
||||
im_part.SetSize(height, width);
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
im_part.data[i] = data[i].imag();
|
||||
}
|
||||
|
||||
return im_part;
|
||||
}
|
||||
|
||||
} // mfem namespace
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_COMPLEX_DENSEMAT
|
||||
|
||||
#include "complex_operator.hpp"
|
||||
#include "../general/complex_type.hpp"
|
||||
#include <complex>
|
||||
|
||||
namespace mfem
|
||||
@@ -241,6 +242,220 @@ public:
|
||||
|
||||
};
|
||||
|
||||
class ComplexTypeDenseMatrix
|
||||
{
|
||||
protected:
|
||||
int height; ///< Dimension of the output / number of rows in the matrix.
|
||||
int width; ///< Dimension of the input / number of columns in the matrix.
|
||||
|
||||
private:
|
||||
Memory<complex_t > data;
|
||||
|
||||
mutable DenseMatrix re_part;
|
||||
mutable DenseMatrix im_part;
|
||||
|
||||
public:
|
||||
/** Default constructor for DenseMatrix.
|
||||
Sets data = NULL and height = width = 0. */
|
||||
ComplexTypeDenseMatrix();
|
||||
|
||||
/// Copy constructor
|
||||
ComplexTypeDenseMatrix(const ComplexTypeDenseMatrix &);
|
||||
ComplexTypeDenseMatrix(const DenseMatrix &);
|
||||
|
||||
/// Creates square matrix of size s.
|
||||
explicit ComplexTypeDenseMatrix(int s);
|
||||
|
||||
/// Creates rectangular matrix of size m x n.
|
||||
ComplexTypeDenseMatrix(int m, int n);
|
||||
|
||||
/// Construct a ComplexTypeDenseMatrix using an existing data array.
|
||||
/** The ComplexTypeDenseMatrix does not assume ownership of the data array,
|
||||
i.e. it will not delete the array. */
|
||||
ComplexTypeDenseMatrix(complex_t *d, int h, int w)
|
||||
: height(h), width(w) { UseExternalData(d, h, w); }
|
||||
|
||||
/// Create a dense matrix using a braced initializer list
|
||||
/// The inner lists correspond to rows of the matrix
|
||||
template <int M, int N, typename T = real_t>
|
||||
explicit ComplexTypeDenseMatrix(const T (&values)[M][N]) :
|
||||
ComplexTypeDenseMatrix(
|
||||
M, N)
|
||||
{
|
||||
// DenseMatrix is column-major so copies have to be element-wise
|
||||
for (int i = 0; i < M; i++)
|
||||
{
|
||||
for (int j = 0; j < N; j++)
|
||||
{
|
||||
(*this)(i,j) = values[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the data array and the size of the DenseMatrix.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the data array @a d. This method should not be used with
|
||||
DenseMatrix that owns its current data array. */
|
||||
void UseExternalData(complex_t *d, int h, int w)
|
||||
{
|
||||
data.Wrap(d, h*w, false);
|
||||
height = h; width = w;
|
||||
}
|
||||
|
||||
/// Change the data array and the size of the DenseMatrix.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the new array @a d. This method will delete the current data
|
||||
array, if owned. */
|
||||
void Reset(complex_t *d, int h, int w)
|
||||
{ if (OwnsData()) { data.Delete(); } UseExternalData(d, h, w); }
|
||||
|
||||
/** Clear the data array and the dimensions of the DenseMatrix. This method
|
||||
should not be used with DenseMatrix that owns its current data array. */
|
||||
void ClearExternalData() { data.Reset(); height = width = 0; }
|
||||
|
||||
/// Delete the matrix data array (if owned) and reset the matrix state.
|
||||
void Clear()
|
||||
{ if (OwnsData()) { data.Delete(); } ClearExternalData(); }
|
||||
|
||||
/// Get the height (size of output) of the Operator. Synonym with NumRows().
|
||||
inline int Height() const { return height; }
|
||||
/** @brief Get the number of rows (size of output) of the Operator. Synonym
|
||||
with Height(). */
|
||||
inline int NumRows() const { return height; }
|
||||
|
||||
/// Get the width (size of input) of the Operator. Synonym with NumCols().
|
||||
inline int Width() const { return width; }
|
||||
/** @brief Get the number of columns (size of input) of the Operator. Synonym
|
||||
with Width(). */
|
||||
inline int NumCols() const { return width; }
|
||||
|
||||
/// For backward compatibility define Size to be synonym of Width()
|
||||
int Size() const { return Width(); }
|
||||
|
||||
// Total size = width*height
|
||||
int TotalSize() const { return width*height; }
|
||||
|
||||
/// Change the size of the DenseMatrix to s x s.
|
||||
void SetSize(int s) { SetSize(s, s); }
|
||||
|
||||
/// Change the size of the DenseMatrix to h x w.
|
||||
void SetSize(int h, int w);
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline complex_t *Data() const
|
||||
{
|
||||
return const_cast<complex_t*>
|
||||
((const complex_t*)data);
|
||||
}
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline complex_t *GetData() const { return Data(); }
|
||||
|
||||
Memory<complex_t > &GetMemory() { return data; }
|
||||
const Memory<complex_t > &GetMemory() const { return data; }
|
||||
|
||||
/// Return the DenseMatrix data (host pointer) ownership flag.
|
||||
inline bool OwnsData() const { return data.OwnsHostPtr(); }
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
inline complex_t &operator()(int i, int j);
|
||||
|
||||
/// Returns constant reference to a_{ij}.
|
||||
inline const complex_t &operator()(int i, int j) const;
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
complex_t &Elem(int i, int j);
|
||||
|
||||
/// Returns constant reference to a_{ij}.
|
||||
const complex_t &Elem(int i, int j) const;
|
||||
|
||||
/// Sets the matrix elements equal to constant c
|
||||
ComplexTypeDenseMatrix &operator=(real_t c);
|
||||
ComplexTypeDenseMatrix &operator=(complex_t c);
|
||||
|
||||
/// Copy the matrix entries from the given array
|
||||
ComplexTypeDenseMatrix &operator=(const real_t *d);
|
||||
ComplexTypeDenseMatrix &operator=(const complex_t *d);
|
||||
|
||||
/// Sets the matrix size and elements equal to those of m
|
||||
ComplexTypeDenseMatrix &operator=(const DenseMatrix &m);
|
||||
ComplexTypeDenseMatrix &operator=(const ComplexTypeDenseMatrix &m);
|
||||
|
||||
ComplexTypeDenseMatrix &operator+=(const real_t *m);
|
||||
ComplexTypeDenseMatrix &operator+=(const complex_t *m);
|
||||
ComplexTypeDenseMatrix &operator+=(const DenseMatrix &m);
|
||||
ComplexTypeDenseMatrix &operator+=(const ComplexTypeDenseMatrix &m);
|
||||
|
||||
ComplexTypeDenseMatrix &operator-=(const DenseMatrix &m);
|
||||
ComplexTypeDenseMatrix &operator-=(const ComplexTypeDenseMatrix &m);
|
||||
|
||||
ComplexTypeDenseMatrix &operator*=(real_t c);
|
||||
ComplexTypeDenseMatrix &operator*=(complex_t c);
|
||||
|
||||
/// (*this) = x + i * y
|
||||
ComplexTypeDenseMatrix &Set(const DenseMatrix &x, const DenseMatrix &y);
|
||||
|
||||
std::size_t MemoryUsage() const
|
||||
{ return data.Capacity() * sizeof(complex_t); }
|
||||
|
||||
/// Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
|
||||
const complex_t *Read(bool on_dev = true) const
|
||||
{ return mfem::Read(data, Height()*Width(), on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Read(GetMemory(), TotalSize(), false).
|
||||
const complex_t *HostRead() const
|
||||
{ return mfem::Read(data, Height()*Width(), false); }
|
||||
|
||||
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), on_dev).
|
||||
complex_t *Write(bool on_dev = true)
|
||||
{ return mfem::Write(data, Height()*Width(), on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), false).
|
||||
complex_t *HostWrite()
|
||||
{ return mfem::Write(data, Height()*Width(), false); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), on_dev).
|
||||
complex_t *ReadWrite(bool on_dev = true)
|
||||
{ return mfem::ReadWrite(data, Height()*Width(), on_dev); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), false).
|
||||
complex_t *HostReadWrite()
|
||||
{ return mfem::ReadWrite(data, Height()*Width(), false); }
|
||||
|
||||
void Swap(ComplexTypeDenseMatrix &other);
|
||||
|
||||
/// Return a reference to the real part of this matrix
|
||||
const DenseMatrix &real() const;
|
||||
|
||||
/// Return a reference to the imaginary part of this matrix
|
||||
const DenseMatrix &imag() const;
|
||||
|
||||
/// Destroys dense matrix.
|
||||
virtual ~ComplexTypeDenseMatrix();
|
||||
};
|
||||
|
||||
/// Specialization of the template function Swap<> for class ComplexTypeDenseMatrix
|
||||
template<> inline void Swap<ComplexTypeDenseMatrix>(ComplexTypeDenseMatrix &a,
|
||||
ComplexTypeDenseMatrix &b)
|
||||
{
|
||||
a.Swap(b);
|
||||
}
|
||||
|
||||
// Inline methods
|
||||
|
||||
inline complex_t &ComplexTypeDenseMatrix::operator()(int i, int j)
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
return data[i+j*height];
|
||||
}
|
||||
|
||||
inline const complex_t &ComplexTypeDenseMatrix::operator()
|
||||
(int i, int j) const
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
return data[i+j*height];
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_COMPLEX_DENSEMAT
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
// 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 "../general/forall.hpp"
|
||||
#include "../general/reducers.hpp"
|
||||
#include "complex_vector.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
ComplexVector::ComplexVector(const ComplexVector &v)
|
||||
{
|
||||
const int s = v.Size();
|
||||
size = s;
|
||||
if (s > 0)
|
||||
{
|
||||
MFEM_ASSERT(!v.data.Empty(), "invalid source vector");
|
||||
data.New(s, v.data.GetMemoryType());
|
||||
data.CopyFrom(v.data, s);
|
||||
}
|
||||
UseDevice(v.UseDevice());
|
||||
}
|
||||
|
||||
ComplexVector::ComplexVector(const Vector &v)
|
||||
{
|
||||
const int s = v.Size();
|
||||
size = s;
|
||||
if (s > 0)
|
||||
{
|
||||
MFEM_ASSERT(!v.data.Empty(), "invalid source vector");
|
||||
data.New(s, v.data.GetMemoryType());
|
||||
MFEM_FORALL(i, size, data[i] = v.data[i]; );
|
||||
}
|
||||
UseDevice(v.UseDevice());
|
||||
}
|
||||
|
||||
ComplexVector::ComplexVector(ComplexVector &&v)
|
||||
{
|
||||
*this = std::move(v);
|
||||
}
|
||||
|
||||
complex_t &ComplexVector::Elem(int i)
|
||||
{
|
||||
return operator()(i);
|
||||
}
|
||||
|
||||
const complex_t &ComplexVector::Elem(int i) const
|
||||
{
|
||||
return operator()(i);
|
||||
}
|
||||
|
||||
complex_t ComplexVector::operator*(const complex_t *v) const
|
||||
{
|
||||
HostRead();
|
||||
complex_t dot = 0.0;
|
||||
#ifdef MFEM_USE_LEGACY_OPENMP
|
||||
#pragma omp parallel for reduction(+:dot)
|
||||
#endif
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dot += data[i] * v[i];
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
complex_t ComplexVector::operator*(const real_t *v) const
|
||||
{
|
||||
HostRead();
|
||||
complex_t dot = 0.0;
|
||||
#ifdef MFEM_USE_LEGACY_OPENMP
|
||||
#pragma omp parallel for reduction(+:dot)
|
||||
#endif
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dot += data[i] * v[i];
|
||||
}
|
||||
return dot;
|
||||
}
|
||||
|
||||
complex_t ComplexVector::operator*(const ComplexVector &v) const
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
if (size == 0) { return 0.0; }
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const auto m_data = Read(use_dev), v_data = v.Read(use_dev);
|
||||
|
||||
// The standard way of computing the dot product is non-deterministic
|
||||
complex_t prod = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
prod += m_data[i] * v_data[i];
|
||||
}
|
||||
return prod;
|
||||
}
|
||||
|
||||
complex_t ComplexVector::operator*(const Vector &v) const
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
if (size == 0) { return 0.0; }
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const auto m_data = Read(use_dev);
|
||||
const auto v_data = v.Read(use_dev);
|
||||
|
||||
// The standard way of computing the dot product is non-deterministic
|
||||
complex_t prod = 0.0;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
prod += m_data[i] * v_data[i];
|
||||
}
|
||||
return prod;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(const complex_t *v)
|
||||
{
|
||||
HostRead();
|
||||
MFEM_FORALL(i, size, data[i] = v[i]; );
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(const real_t *v)
|
||||
{
|
||||
HostRead();
|
||||
MFEM_FORALL(i, size, data[i] = v[i]; );
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(const ComplexVector &v)
|
||||
{
|
||||
#if 0
|
||||
SetSize(v.Size(), v.data.GetMemoryType());
|
||||
data.CopyFrom(v.data, v.Size());
|
||||
UseDevice(v.UseDevice());
|
||||
#else
|
||||
SetSize(v.Size());
|
||||
const bool vuse = v.UseDevice();
|
||||
const bool use_dev = UseDevice() || vuse;
|
||||
v.UseDevice(use_dev);
|
||||
// keep 'data' where it is, unless 'use_dev' is true
|
||||
if (use_dev) { Write(); }
|
||||
data.CopyFrom(v.data, v.Size());
|
||||
v.UseDevice(vuse);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(const Vector &v)
|
||||
{
|
||||
SetSize(v.Size());
|
||||
const bool vuse = v.UseDevice();
|
||||
const bool use_dev = UseDevice() || vuse;
|
||||
v.UseDevice(use_dev);
|
||||
// keep 'data' where it is, unless 'use_dev' is true
|
||||
if (use_dev) { Write(); }
|
||||
MFEM_FORALL(i, size, data[i] = v[i]; );
|
||||
v.UseDevice(vuse);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(ComplexVector &&v)
|
||||
{
|
||||
v.Swap(*this);
|
||||
if (this != &v) { v.Destroy(); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(complex_t value)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = Write(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] = value; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator=(real_t value)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = Write(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] = value; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator*=(complex_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] *= c; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator*=(real_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] *= c; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator*=(const ComplexVector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] *= x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator*=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] *= x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator/=(complex_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
const complex_t m = conj(c) / norm(c);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] *= m; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator/=(real_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
const real_t m = 1.0/c;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] *= m; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator/=(const ComplexVector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] /= x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator/=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] /= x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator-=(complex_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] -= c; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator-=(real_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] -= c; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator-=(const ComplexVector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] -= x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator-=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] -= x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator+=(complex_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] += c; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator+=(real_t c)
|
||||
{
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] += c; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator+=(const ComplexVector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] += x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::operator+=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || v.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = v.Read(use_dev);
|
||||
auto y = ReadWrite(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] += x[i]; });
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexVector &ComplexVector::Set(const Vector &Vr, const Vector &Vi)
|
||||
{
|
||||
MFEM_ASSERT(size == Vr.size && size == Vi.size, "incompatible Vectors!");
|
||||
|
||||
const bool use_dev = UseDevice() || Vr.UseDevice() || Vi.UseDevice();
|
||||
const int N = size;
|
||||
const auto x = Vr.Read(use_dev);
|
||||
const auto y = Vi.Read(use_dev);
|
||||
auto z = Write(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ z[i] = complex_t(x[i], y[i]); });
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Vector &ComplexVector::real() const
|
||||
{
|
||||
re_part.SetSize(size);
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
const auto z = Read(use_dev);
|
||||
auto x = re_part.Write(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ x[i] = z[i].real(); });
|
||||
return re_part;
|
||||
}
|
||||
|
||||
const Vector &ComplexVector::imag() const
|
||||
{
|
||||
im_part.SetSize(size);
|
||||
const bool use_dev = UseDevice();
|
||||
const int N = size;
|
||||
const auto z = Read(use_dev);
|
||||
auto y = im_part.Write(use_dev);
|
||||
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{ y[i] = z[i].imag(); });
|
||||
return im_part;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
// 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_COMPLEX_VECTOR
|
||||
#define MFEM_COMPLEX_VECTOR
|
||||
|
||||
#include "vector.hpp"
|
||||
#include "../general/complex_type.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ComplexVector
|
||||
{
|
||||
private:
|
||||
|
||||
Memory<complex_t > data;
|
||||
int size;
|
||||
|
||||
mutable Vector re_part;
|
||||
mutable Vector im_part;
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor for ComplexVector. Sets size = 0
|
||||
ComplexVector() : size(0) { }
|
||||
|
||||
/// Copy constructor. Allocates a new data array and copies the data.
|
||||
ComplexVector(const ComplexVector &);
|
||||
|
||||
/// Copy constructor. Allocates a new data array and copies the
|
||||
/// data into real part of this vector.
|
||||
ComplexVector(const Vector &);
|
||||
|
||||
/// Move constructor. "Steals" data from its argument.
|
||||
ComplexVector(ComplexVector&& v);
|
||||
|
||||
/// @brief Creates vector of size s.
|
||||
/// @warning Entries are not initialized to zero!
|
||||
explicit ComplexVector(int s);
|
||||
|
||||
/// Creates a vector referencing an array of complex<doubles>,
|
||||
/// owned by someone else.
|
||||
/// The pointer @a data_ can be NULL. The data array can be replaced later
|
||||
/// with SetData().
|
||||
ComplexVector(complex_t *data_, int size_)
|
||||
{ data.Wrap(data_, size_, false); size = size_; }
|
||||
|
||||
/// @brief Create a ComplexVector referencing a sub-vector of the
|
||||
// ComplexVector @a base starting at the given offset, @a
|
||||
// base_offset, and size @a size_.
|
||||
ComplexVector(ComplexVector &base, int base_offset, int size_)
|
||||
: data(base.data, base_offset, size_), size(size_) { }
|
||||
|
||||
/// Create a ComplexVector of size @a size_ using MemoryType @a mt.
|
||||
ComplexVector(int size_, MemoryType mt)
|
||||
: data(size_, mt), size(size_) { }
|
||||
|
||||
/// @brief Create a ComplexVector of size @a size_ using host
|
||||
/// MemoryType @a h_mt and device MemoryType @a d_mt.
|
||||
ComplexVector(int size_, MemoryType h_mt, MemoryType d_mt)
|
||||
: data(size_, h_mt, d_mt), size(size_) { }
|
||||
|
||||
/// Create a vector from a statically sized C-style array of convertible type
|
||||
template <typename CT, int N>
|
||||
explicit ComplexVector(const CT (&values)[N]) : ComplexVector(N)
|
||||
{ std::copy(values, values + N, begin()); }
|
||||
|
||||
/// Create a vector using a braced initializer list
|
||||
template <typename CT, typename std::enable_if<
|
||||
std::is_convertible<CT,complex_t >::value,bool>::type = true>
|
||||
explicit ComplexVector(std::initializer_list<CT> values) : ComplexVector(
|
||||
values.size())
|
||||
{ std::copy(values.begin(), values.end(), begin()); }
|
||||
|
||||
/// Enable execution of Vector operations using the mfem::Device.
|
||||
/// The default is to use Backend::CPU (serial execution on each MPI rank),
|
||||
/// regardless of the mfem::Device configuration.
|
||||
///
|
||||
/// When appropriate, MFEM functions and class methods will enable the use
|
||||
/// of the mfem::Device for their Vector parameters.
|
||||
///
|
||||
/// Some derived classes, e.g. GridFunction, enable the use of the
|
||||
/// mfem::Device by default.
|
||||
virtual void UseDevice(bool use_dev) const { data.UseDevice(use_dev); }
|
||||
|
||||
/// Return the device flag of the Memory object used by the Vector
|
||||
virtual bool UseDevice() const { return data.UseDevice(); }
|
||||
|
||||
/// @brief Resize the vector to size @a s.
|
||||
/// If the new size is less than or equal to Capacity() then the internal
|
||||
/// data array remains the same. Otherwise, the old array is deleted, if
|
||||
/// owned, and a new array of size @a s is allocated without copying the
|
||||
/// previous content of the ComplexVector.
|
||||
/// @warning In the second case above (new size greater than current one),
|
||||
/// the vector will allocate new data array, even if it did not own the
|
||||
/// original data! Also, new entries are not initialized!
|
||||
void SetSize(int s);
|
||||
|
||||
/// Resize the vector to size @a s using MemoryType @a mt.
|
||||
void SetSize(int s, MemoryType mt);
|
||||
|
||||
/// Resize the vector to size @a s using the MemoryType of @a v.
|
||||
void SetSize(int s, const ComplexVector &v)
|
||||
{ SetSize(s, v.GetMemory().GetMemoryType()); }
|
||||
|
||||
/// Resize the vector to size @a s using the MemoryType of @a v.
|
||||
void SetSize(int s, const Vector &v)
|
||||
{ SetSize(s, v.GetMemory().GetMemoryType()); }
|
||||
|
||||
/// Set the Vector data.
|
||||
/// @warning This method should be called only when OwnsData() is false.
|
||||
void SetData(complex_t *d)
|
||||
{ data.Wrap(d, data.Capacity(), false); }
|
||||
|
||||
/// Set the Vector data and size.
|
||||
/// The Vector does not assume ownership of the new data. The new size is
|
||||
/// also used as the new Capacity().
|
||||
/// @warning This method should be called only when OwnsData() is false.
|
||||
/// @sa NewDataAndSize().
|
||||
void SetDataAndSize(complex_t *d, int s)
|
||||
{ data.Wrap(d, s, false); size = s; }
|
||||
|
||||
/// Set the Vector data and size, deleting the old data, if owned.
|
||||
/// The Vector does not assume ownership of the new data. The new size is
|
||||
/// also used as the new Capacity().
|
||||
/// @sa SetDataAndSize().
|
||||
void NewDataAndSize(complex_t *d, int s)
|
||||
{
|
||||
data.Delete();
|
||||
SetDataAndSize(d, s);
|
||||
}
|
||||
|
||||
/// Reset the Vector to use the given external Memory @a mem and size @a s.
|
||||
/// If @a own_mem is false, the Vector will not own any of the pointers of
|
||||
/// @a mem.
|
||||
///
|
||||
/// Note that when @a own_mem is true, the @a mem object can be destroyed
|
||||
/// immediately by the caller but `mem.Delete()` should NOT be called since
|
||||
/// the Vector object takes ownership of all pointers owned by @a mem.
|
||||
///
|
||||
/// @sa NewDataAndSize().
|
||||
inline void NewMemoryAndSize(const Memory<complex_t > &mem,
|
||||
int s, bool own_mem);
|
||||
|
||||
/// Reset the Vector to be a reference to a sub-vector of @a base.
|
||||
inline void MakeRef(ComplexVector &base, int offset, int size);
|
||||
|
||||
/// @brief Reset the Vector to be a reference to a sub-vector of @a base
|
||||
/// without changing its current size.
|
||||
inline void MakeRef(ComplexVector &base, int offset);
|
||||
|
||||
/// Set the Vector data (host pointer) ownership flag.
|
||||
void MakeDataOwner() const { data.SetHostPtrOwner(true); }
|
||||
|
||||
/// Destroy a vector
|
||||
void Destroy();
|
||||
|
||||
/// @brief Delete the device pointer, if owned. If @a copy_to_host is true
|
||||
/// and the data is valid only on device, move it to host before deleting.
|
||||
/// Invalidates the device memory.
|
||||
void DeleteDevice(bool copy_to_host = true)
|
||||
{ data.DeleteDevice(copy_to_host); }
|
||||
|
||||
/// Returns the size of the vector.
|
||||
inline int Size() const { return size; }
|
||||
|
||||
/// Return the size of the currently allocated data array.
|
||||
/// It is always true that Capacity() >= Size().
|
||||
inline int Capacity() const { return data.Capacity(); }
|
||||
|
||||
/// Return a pointer to the beginning of the ComplexVector data.
|
||||
/// @warning This method should be used with caution as it gives write access
|
||||
/// to the data of const-qualified ComplexVector%s.
|
||||
inline complex_t *GetData() const
|
||||
{ return const_cast<complex_t*>((const complex_t*)data); }
|
||||
|
||||
/// STL-like begin.
|
||||
inline complex_t *begin() { return data; }
|
||||
|
||||
/// STL-like end.
|
||||
inline complex_t *end() { return data + size; }
|
||||
|
||||
/// STL-like begin (const version).
|
||||
inline const complex_t *begin() const { return data; }
|
||||
|
||||
/// STL-like end (const version).
|
||||
inline const complex_t *end() const { return data + size; }
|
||||
|
||||
/// Return a reference to the Memory object used by the Vector.
|
||||
Memory<complex_t > &GetMemory() { return data; }
|
||||
|
||||
/// @brief Return a reference to the Memory object used by the
|
||||
/// ComplexVector, const version.
|
||||
const Memory<complex_t > &GetMemory() const { return data; }
|
||||
|
||||
/// Update the memory location of the vector to match @a v.
|
||||
void SyncMemory(const ComplexVector &v) const
|
||||
{ GetMemory().Sync(v.GetMemory()); }
|
||||
|
||||
/// Update the alias memory location of the vector to match @a v.
|
||||
void SyncAliasMemory(const ComplexVector &v) const
|
||||
{ GetMemory().SyncAlias(v.GetMemory(),Size()); }
|
||||
|
||||
/// Read the Vector data (host pointer) ownership flag.
|
||||
inline bool OwnsData() const { return data.OwnsHostPtr(); }
|
||||
|
||||
/// Changes the ownership of the data; after the call the Vector is empty
|
||||
inline void StealData(complex_t **p)
|
||||
{ *p = data; data.Reset(); size = 0; }
|
||||
|
||||
/// Changes the ownership of the data; after the call the Vector is empty
|
||||
inline complex_t *StealData()
|
||||
{ complex_t *p; StealData(&p); return p; }
|
||||
|
||||
/// Access Vector entries. Index i = 0 .. size-1.
|
||||
complex_t &Elem(int i);
|
||||
|
||||
/// Read only access to Vector entries. Index i = 0 .. size-1.
|
||||
const complex_t &Elem(int i) const;
|
||||
|
||||
/// Access Vector entries using () for 0-based indexing.
|
||||
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
|
||||
inline complex_t &operator()(int i);
|
||||
|
||||
/// Read only access to Vector entries using () for 0-based indexing.
|
||||
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
|
||||
inline const complex_t &operator()(int i) const;
|
||||
|
||||
/// Access Vector entries using [] for 0-based indexing.
|
||||
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
|
||||
inline complex_t &operator[](int i) { return (*this)(i); }
|
||||
|
||||
/// Read only access to Vector entries using [] for 0-based indexing.
|
||||
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
|
||||
inline const complex_t &operator[](int i) const
|
||||
{ return (*this)(i); }
|
||||
|
||||
/// Dot product with a `complex<double> *` array.
|
||||
/// @note No complex conjugate is performed
|
||||
complex_t operator*(const complex_t *v) const;
|
||||
complex_t operator*(const real_t *v) const;
|
||||
|
||||
/// Return the inner-product.
|
||||
/// @note No complex conjugate is performed
|
||||
complex_t operator*(const ComplexVector &v) const;
|
||||
complex_t operator*(const Vector &v) const;
|
||||
|
||||
/// Copy Size() entries from @a v.
|
||||
ComplexVector &operator=(const complex_t *v);
|
||||
ComplexVector &operator=(const real_t *v);
|
||||
|
||||
/// Copy assignment.
|
||||
/// @note Defining this method overwrites the implicitly defined copy
|
||||
/// assignment operator.
|
||||
ComplexVector &operator=(const ComplexVector &v);
|
||||
ComplexVector &operator=(const Vector &v);
|
||||
|
||||
/// Move assignment
|
||||
ComplexVector &operator=(ComplexVector&& v);
|
||||
|
||||
/// Redefine '=' for vector = constant.
|
||||
ComplexVector &operator=(complex_t value);
|
||||
ComplexVector &operator=(real_t value);
|
||||
|
||||
/// Scale vector by a constant
|
||||
ComplexVector &operator*=(complex_t c);
|
||||
ComplexVector &operator*=(real_t c);
|
||||
|
||||
/// Component-wise scaling: (*this)(i) *= v(i)
|
||||
ComplexVector &operator*=(const ComplexVector &v);
|
||||
ComplexVector &operator*=(const Vector &v);
|
||||
|
||||
/// Divide vector by a consant
|
||||
ComplexVector &operator/=(complex_t c);
|
||||
ComplexVector &operator/=(real_t c);
|
||||
|
||||
/// Component-wise division: (*this)(i) /= v(i)
|
||||
ComplexVector &operator/=(const ComplexVector &v);
|
||||
ComplexVector &operator/=(const Vector &v);
|
||||
|
||||
/// Subtract a constant from this vector
|
||||
ComplexVector &operator-=(complex_t c);
|
||||
ComplexVector &operator-=(real_t c);
|
||||
|
||||
/// Subtract a vector from this vector
|
||||
ComplexVector &operator-=(const ComplexVector &v);
|
||||
ComplexVector &operator-=(const Vector &v);
|
||||
|
||||
/// Add a constant to this vector
|
||||
ComplexVector &operator+=(complex_t c);
|
||||
ComplexVector &operator+=(real_t c);
|
||||
|
||||
/// Add a vector to this vector
|
||||
ComplexVector &operator+=(const ComplexVector &v);
|
||||
ComplexVector &operator+=(const Vector &v);
|
||||
|
||||
/// (*this) = x + i * y
|
||||
ComplexVector &Set(const Vector &x, const Vector &y);
|
||||
|
||||
/// Swap the contents of two Vectors
|
||||
inline void Swap(ComplexVector &other);
|
||||
|
||||
/// Return a reference to the real part of this vector
|
||||
const Vector &real() const;
|
||||
|
||||
/// Return a reference to the imaginary part of this vector
|
||||
const Vector &imag() const;
|
||||
|
||||
/// Destroys vector.
|
||||
virtual ~ComplexVector();
|
||||
|
||||
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
|
||||
virtual const complex_t *Read(bool on_dev = true) const
|
||||
{ return mfem::Read(data, size, on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
|
||||
virtual const complex_t *HostRead() const
|
||||
{ return mfem::Read(data, size, false); }
|
||||
|
||||
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
|
||||
virtual complex_t *Write(bool on_dev = true)
|
||||
{ return mfem::Write(data, size, on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
|
||||
virtual complex_t *HostWrite()
|
||||
{ return mfem::Write(data, size, false); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
|
||||
virtual complex_t *ReadWrite(bool on_dev = true)
|
||||
{ return mfem::ReadWrite(data, size, on_dev); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
|
||||
virtual complex_t *HostReadWrite()
|
||||
{ return mfem::ReadWrite(data, size, false); }
|
||||
};
|
||||
|
||||
inline ComplexVector::ComplexVector(int s)
|
||||
{
|
||||
MFEM_ASSERT(s>=0,"Unexpected negative size.");
|
||||
size = s;
|
||||
if (s > 0)
|
||||
{
|
||||
data.New(s);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ComplexVector::SetSize(int s)
|
||||
{
|
||||
if (s == size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s <= data.Capacity())
|
||||
{
|
||||
size = s;
|
||||
return;
|
||||
}
|
||||
// preserve a valid MemoryType and device flag
|
||||
const MemoryType mt = data.GetMemoryType();
|
||||
const bool use_dev = data.UseDevice();
|
||||
data.Delete();
|
||||
size = s;
|
||||
data.New(s, mt);
|
||||
data.UseDevice(use_dev);
|
||||
}
|
||||
|
||||
inline void ComplexVector::SetSize(int s, MemoryType mt)
|
||||
{
|
||||
if (mt == data.GetMemoryType())
|
||||
{
|
||||
if (s == size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s <= data.Capacity())
|
||||
{
|
||||
size = s;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const bool use_dev = data.UseDevice();
|
||||
data.Delete();
|
||||
if (s > 0)
|
||||
{
|
||||
data.New(s, mt);
|
||||
size = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
size = 0;
|
||||
}
|
||||
data.UseDevice(use_dev);
|
||||
}
|
||||
|
||||
inline void ComplexVector::NewMemoryAndSize(
|
||||
const Memory<complex_t > &mem,
|
||||
int s,
|
||||
bool own_mem)
|
||||
{
|
||||
data.Delete();
|
||||
size = s;
|
||||
if (own_mem)
|
||||
{
|
||||
data = mem;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.MakeAlias(mem, 0, s);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ComplexVector::MakeRef(ComplexVector &base, int offset, int s)
|
||||
{
|
||||
data.Delete();
|
||||
size = s;
|
||||
data.MakeAlias(base.GetMemory(), offset, s);
|
||||
}
|
||||
|
||||
inline void ComplexVector::MakeRef(ComplexVector &base, int offset)
|
||||
{
|
||||
data.Delete();
|
||||
data.MakeAlias(base.GetMemory(), offset, size);
|
||||
}
|
||||
|
||||
inline void ComplexVector::Destroy()
|
||||
{
|
||||
const bool use_dev = data.UseDevice();
|
||||
data.Delete();
|
||||
size = 0;
|
||||
data.Reset();
|
||||
data.UseDevice(use_dev);
|
||||
}
|
||||
|
||||
inline complex_t &ComplexVector::operator()(int i)
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < size,
|
||||
"index [" << i << "] is out of range [0," << size << ")");
|
||||
|
||||
return data[i];
|
||||
}
|
||||
|
||||
inline const complex_t &ComplexVector::operator()(int i) const
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < size,
|
||||
"index [" << i << "] is out of range [0," << size << ")");
|
||||
|
||||
return data[i];
|
||||
}
|
||||
|
||||
inline void ComplexVector::Swap(ComplexVector &other)
|
||||
{
|
||||
mfem::Swap(data, other.data);
|
||||
mfem::Swap(size, other.size);
|
||||
}
|
||||
|
||||
/// Specialization of the template function Swap<> for class ComplexVector
|
||||
template<> inline void Swap<ComplexVector>(ComplexVector &a, ComplexVector &b)
|
||||
{
|
||||
a.Swap(b);
|
||||
}
|
||||
|
||||
inline ComplexVector::~ComplexVector()
|
||||
{
|
||||
data.Delete();
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -4405,4 +4405,32 @@ void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X)
|
||||
BatchedLinAlg::LUSolve(Mlu, P, X);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
void BandedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
|
||||
Array<int> &ipiv)
|
||||
{
|
||||
int LDAB = (2*KL) + KU + 1;
|
||||
int N = AB.NumCols();
|
||||
int NRHS = B.NumCols();
|
||||
int info;
|
||||
ipiv.SetSize(N);
|
||||
MFEM_LAPACK_PREFIX(gbsv_)(&N, &KL, &KU, &NRHS, AB.GetData(), &LDAB,
|
||||
ipiv.GetData(), B.GetData(), &N, &info);
|
||||
MFEM_ASSERT(info == 0, "BandedSolve failed in LAPACK");
|
||||
}
|
||||
|
||||
void BandedFactorizedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
|
||||
bool transpose, Array<int> &ipiv)
|
||||
{
|
||||
int LDAB = (2*KL) + KU + 1;
|
||||
int N = AB.NumCols();
|
||||
int NRHS = B.NumCols();
|
||||
char trans = transpose ? 'T' : 'N';
|
||||
int info;
|
||||
MFEM_LAPACK_PREFIX(gbtrs_)(&trans, &N, &KL, &KU, &NRHS, AB.GetData(), &LDAB,
|
||||
ipiv.GetData(), B.GetData(), &N, &info);
|
||||
MFEM_ASSERT(info == 0, "BandedFactorizedSolve failed in LAPACK");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -24,6 +24,7 @@ class DenseMatrix : public Matrix
|
||||
{
|
||||
friend class DenseTensor;
|
||||
friend class DenseMatrixInverse;
|
||||
friend class ComplexTypeDenseMatrix;
|
||||
|
||||
private:
|
||||
Memory<real_t> data;
|
||||
@@ -1329,6 +1330,13 @@ void BatchLUFactor(DenseTensor &Mlu, Array<int> &P, const real_t TOL = 0.0);
|
||||
dimension m x n. */
|
||||
void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X);
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
void BandedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
|
||||
Array<int> &ipiv);
|
||||
void BandedFactorizedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
|
||||
bool transpose, Array<int> &ipiv);
|
||||
#endif
|
||||
|
||||
// Inline methods
|
||||
|
||||
inline real_t &DenseMatrix::operator()(int i, int j)
|
||||
|
||||
@@ -2574,6 +2574,18 @@ void HypreParMatrix::EliminateBC(const Array<int> &ess_dofs,
|
||||
#if defined(HYPRE_USING_GPU)
|
||||
if (HypreUsingGPU())
|
||||
{
|
||||
#if defined(HYPRE_WITH_GPU_AWARE_MPI) || defined(HYPRE_USING_GPU_AWARE_MPI)
|
||||
// hypre_GetGpuAwareMPI() was introduced in v2.31.0, however, its value
|
||||
// is not checked in hypre_ParCSRCommHandleCreate_v2() before v2.33.0,
|
||||
// instead only HYPRE_WITH_GPU_AWARE_MPI is checked.
|
||||
#if MFEM_HYPRE_VERSION >= 23300
|
||||
if (hypre_GetGpuAwareMPI())
|
||||
#endif
|
||||
{
|
||||
// ensure int_buf_data has been computed before sending it
|
||||
MFEM_STREAM_SYNC;
|
||||
}
|
||||
#endif
|
||||
// Try to use device-aware MPI for the communication if available
|
||||
comm_handle = hypre_ParCSRCommHandleCreate_v2(
|
||||
11, comm_pkg, HYPRE_MEMORY_DEVICE, int_buf_data,
|
||||
|
||||
@@ -42,6 +42,13 @@ extern "C" void
|
||||
MFEM_LAPACK_PREFIX(getri_)(int *N, real_t *A, int *LDA, int *IPIV, real_t *WORK,
|
||||
int *LWORK, int *INFO);
|
||||
extern "C" void
|
||||
MFEM_LAPACK_PREFIX(gbsv_)(int *, int *, int *, int *, real_t *, int *, int *,
|
||||
real_t *, int *, int *);
|
||||
extern "C" void
|
||||
MFEM_LAPACK_PREFIX(gbtrs_)(char *, int *, int *, int *, int *, real_t *, int *,
|
||||
int *, real_t *, int *, int *);
|
||||
|
||||
extern "C" void
|
||||
MFEM_LAPACK_PREFIX(syevr_)(char *JOBZ, char *RANGE, char *UPLO, int *N,
|
||||
real_t *A, int *LDA, real_t *VL, real_t *VU, int *IL,
|
||||
int *IU, real_t *ABSTOL, int *M, real_t *W,
|
||||
|
||||
@@ -80,6 +80,8 @@ inline real_t rand_real()
|
||||
/// Vector data type.
|
||||
class Vector
|
||||
{
|
||||
friend class ComplexVector;
|
||||
|
||||
protected:
|
||||
|
||||
Memory<real_t> data;
|
||||
|
||||
+35
-27
@@ -26,6 +26,12 @@
|
||||
}\
|
||||
}
|
||||
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
#define MFEM_NETCDF_REAL_T NC_DOUBLE
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
#define MFEM_NETCDF_REAL_T NC_FLOAT
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -135,18 +141,18 @@ public:
|
||||
/// @brief Writes the mesh to an ExodusII file.
|
||||
/// @param fpath The path to the file.
|
||||
/// @param flags NC_CLOBBER will overwrite existing file.
|
||||
void PrintExodusII(std::string fpath, int flags = NC_CLOBBER);
|
||||
void PrintExodusII(const std::string &fpath, int flags = NC_CLOBBER);
|
||||
|
||||
/// @brief Static method for writing a mesh to an ExodusII file.
|
||||
/// @param mesh The mesh to write to the file.
|
||||
/// @param fpath The path to the file.
|
||||
/// @param flags NetCDF file flags.
|
||||
static void PrintExodusII(Mesh & mesh, std::string fpath,
|
||||
static void PrintExodusII(Mesh & mesh, const std::string &fpath,
|
||||
int flags = NC_CLOBBER);
|
||||
|
||||
protected:
|
||||
/// @brief Closes any open file and creates a NetCDF file using selected flags.
|
||||
void OpenExodusII(std::string fpath, int flags);
|
||||
void OpenExodusII(const std::string &fpath, int flags);
|
||||
|
||||
/// @brief Closes any open file.
|
||||
void CloseExodusII();
|
||||
@@ -167,9 +173,9 @@ protected:
|
||||
std::unordered_set<int> GenerateUniqueNodeIDs();
|
||||
|
||||
/// @brief Populates vectors with x, y, z coordinates from mesh.
|
||||
void ExtractVertexCoordinates(std::vector<double> & coordx,
|
||||
std::vector<double> & coordy,
|
||||
std::vector<double> & coordz);
|
||||
void ExtractVertexCoordinates(std::vector<real_t> &coordx,
|
||||
std::vector<real_t> &coordy,
|
||||
std::vector<real_t> &coordz);
|
||||
|
||||
/// @brief Writes node connectivity for a particular block.
|
||||
/// @param block_id The block to write to the file.
|
||||
@@ -187,7 +193,7 @@ protected:
|
||||
/// @brief Writes the number of elements in the mesh.
|
||||
void WriteNumOfElements();
|
||||
|
||||
/// @brief Writes the floating-point word size (4 == float; 8 == double).
|
||||
/// @brief Writes the floating-point word size (sizeof(real_t)).
|
||||
void WriteFloatingPointWordSize();
|
||||
|
||||
/// @brief Writes the API version.
|
||||
@@ -291,7 +297,7 @@ private:
|
||||
std::map<int, std::vector<int>> exodusII_side_ids_for_boundary_id;
|
||||
};
|
||||
|
||||
void Mesh::PrintExodusII(const std::string fpath)
|
||||
void Mesh::PrintExodusII(const std::string &fpath)
|
||||
{
|
||||
ExodusIIWriter::PrintExodusII(*this, fpath);
|
||||
}
|
||||
@@ -362,7 +368,7 @@ void ExodusIIWriter::WriteExodusIIMeshInformation()
|
||||
WriteNodeSets();
|
||||
}
|
||||
|
||||
void ExodusIIWriter::PrintExodusII(std::string fpath, int flags)
|
||||
void ExodusIIWriter::PrintExodusII(const std::string &fpath, int flags)
|
||||
{
|
||||
OpenExodusII(fpath, flags);
|
||||
|
||||
@@ -374,7 +380,7 @@ void ExodusIIWriter::PrintExodusII(std::string fpath, int flags)
|
||||
mfem::out << "Mesh successfully written to Exodus II file" << std::endl;
|
||||
}
|
||||
|
||||
void ExodusIIWriter::PrintExodusII(Mesh & mesh, std::string fpath,
|
||||
void ExodusIIWriter::PrintExodusII(Mesh &mesh, const std::string &fpath,
|
||||
int flags)
|
||||
{
|
||||
ExodusIIWriter writer(mesh);
|
||||
@@ -382,7 +388,7 @@ void ExodusIIWriter::PrintExodusII(Mesh & mesh, std::string fpath,
|
||||
writer.PrintExodusII(fpath, flags);
|
||||
}
|
||||
|
||||
void ExodusIIWriter::OpenExodusII(std::string fpath, int flags)
|
||||
void ExodusIIWriter::OpenExodusII(const std::string &fpath, int flags)
|
||||
{
|
||||
CloseExodusII(); // Close any open files.
|
||||
|
||||
@@ -422,7 +428,7 @@ void ExodusIIWriter::WriteNumOfElements()
|
||||
|
||||
void ExodusIIWriter::WriteFloatingPointWordSize()
|
||||
{
|
||||
const int word_size = 8;
|
||||
const int word_size = sizeof(real_t);
|
||||
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_FLOATING_POINT_WORD_SIZE_LABEL,
|
||||
NC_INT, 1,
|
||||
&word_size);
|
||||
@@ -430,13 +436,15 @@ void ExodusIIWriter::WriteFloatingPointWordSize()
|
||||
|
||||
void ExodusIIWriter::WriteAPIVersion()
|
||||
{
|
||||
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_API_VERSION_LABEL, NC_FLOAT, 1,
|
||||
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_API_VERSION_LABEL, MFEM_NETCDF_REAL_T,
|
||||
1,
|
||||
&ExodusIILabels::EXODUS_API_VERSION);
|
||||
}
|
||||
|
||||
void ExodusIIWriter::WriteDatabaseVersion()
|
||||
{
|
||||
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_DATABASE_VERSION_LABEL, NC_FLOAT, 1,
|
||||
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_DATABASE_VERSION_LABEL,
|
||||
MFEM_NETCDF_REAL_T, 1,
|
||||
&ExodusIILabels::EXODUS_DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@@ -607,25 +615,25 @@ void ExodusIIWriter::WriteNodalCoordinates()
|
||||
DefineDimension("num_nodes", num_nodes, &num_nodes_id);
|
||||
|
||||
// 3. Extract the nodal coordinates.
|
||||
// NB: assume doubles (could be floats!); ndims = 1 (vector).
|
||||
// NB: writes in format real_t (double or float); ndims = 1 (vector).
|
||||
// https://docs.unidata.ucar.edu/netcdf-c/current/group__variables.html#gac7e8662c51f3bb07d1fc6d6c6d9052c8
|
||||
std::vector<double> coordx(num_nodes);
|
||||
std::vector<double> coordy(num_nodes);
|
||||
std::vector<double> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
|
||||
std::vector<real_t> coordx(num_nodes);
|
||||
std::vector<real_t> coordy(num_nodes);
|
||||
std::vector<real_t> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
|
||||
|
||||
ExtractVertexCoordinates(coordx, coordy, coordz);
|
||||
|
||||
// 4. Define and put the nodal coordinates.
|
||||
DefineAndPutVar(ExodusIILabels::EXODUS_COORDX_LABEL, NC_DOUBLE, 1,
|
||||
DefineAndPutVar(ExodusIILabels::EXODUS_COORDX_LABEL, MFEM_NETCDF_REAL_T, 1,
|
||||
&num_nodes_id,
|
||||
coordx.data());
|
||||
DefineAndPutVar(ExodusIILabels::EXODUS_COORDY_LABEL, NC_DOUBLE, 1,
|
||||
DefineAndPutVar(ExodusIILabels::EXODUS_COORDY_LABEL, MFEM_NETCDF_REAL_T, 1,
|
||||
&num_nodes_id,
|
||||
coordy.data());
|
||||
|
||||
if (mesh.Dimension() == 3)
|
||||
{
|
||||
DefineAndPutVar(ExodusIILabels::EXODUS_COORDZ_LABEL, NC_DOUBLE, 1,
|
||||
DefineAndPutVar(ExodusIILabels::EXODUS_COORDZ_LABEL, MFEM_NETCDF_REAL_T, 1,
|
||||
&num_nodes_id,
|
||||
coordz.data());
|
||||
}
|
||||
@@ -770,9 +778,9 @@ void ExodusIIWriter::WriteNodeConnectivityForBlock(const int block_id)
|
||||
}
|
||||
|
||||
|
||||
void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
|
||||
std::vector<double> & coordy,
|
||||
std::vector<double> & coordz)
|
||||
void ExodusIIWriter::ExtractVertexCoordinates(std::vector<real_t> & coordx,
|
||||
std::vector<real_t> & coordy,
|
||||
std::vector<real_t> & coordz)
|
||||
{
|
||||
if (mesh.GetNodes()) // Higher-order.
|
||||
{
|
||||
@@ -782,7 +790,7 @@ void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
|
||||
sorted_node_ids.assign(unordered_node_ids.begin(), unordered_node_ids.end());
|
||||
std::sort(sorted_node_ids.begin(), sorted_node_ids.end());
|
||||
|
||||
double coordinates[3];
|
||||
real_t coordinates[3];
|
||||
for (size_t i = 0; i < sorted_node_ids.size(); i++)
|
||||
{
|
||||
int node_id = sorted_node_ids[i];
|
||||
@@ -802,7 +810,7 @@ void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
|
||||
{
|
||||
for (int ivertex = 0; ivertex < mesh.GetNV(); ivertex++)
|
||||
{
|
||||
double * coordinates = mesh.GetVertex(ivertex);
|
||||
real_t *coordinates = mesh.GetVertex(ivertex);
|
||||
|
||||
coordx[ivertex] = coordinates[0];
|
||||
coordy[ivertex] = coordinates[1];
|
||||
@@ -1080,4 +1088,4 @@ void ExodusIIWriter::CheckNodalFESpaceIsSecondOrderH1() const
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2994,6 +2994,7 @@ void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
|
||||
const int num_edge_dofs = old_dofs.Size();
|
||||
|
||||
// Save the original nodes
|
||||
Nodes->HostReadWrite(); // for "(*Nodes)() = "
|
||||
const Vector onodes = *Nodes;
|
||||
|
||||
// vertex dofs do not need to be moved
|
||||
|
||||
+11
-8
@@ -588,9 +588,10 @@ protected:
|
||||
void Loader(std::istream &input, int generate_edges = 0,
|
||||
std::string parse_tag = "");
|
||||
|
||||
/** If NURBS mesh, write NURBS format. If NCMesh, write mfem v1.1 format.
|
||||
If section_delimiter is empty, write mfem v1.0 format. Otherwise, write
|
||||
mfem v1.2 format with the given section_delimiter at the end.
|
||||
/** @brief If NURBS mesh, write NURBS format. If NCMesh, write mfem v1.1
|
||||
format. If section_delimiter is empty, write mfem v1.0 format. Otherwise,
|
||||
write mfem v1.2 format with the given section_delimiter at the end.
|
||||
|
||||
If @a comments is non-empty, it will be printed after the first line of
|
||||
the file, and each line should begin with '#'. */
|
||||
void Printer(std::ostream &os = mfem::out,
|
||||
@@ -2482,10 +2483,12 @@ public:
|
||||
/// Print the mesh to the given stream using Netgen/Truegrid format.
|
||||
virtual void PrintXG(std::ostream &os = mfem::out) const;
|
||||
|
||||
/// Print the mesh to the given stream using the default MFEM mesh format.
|
||||
/// \see mfem::ofgzstream() for on-the-fly compression of ascii outputs. If
|
||||
/// @a comments is non-empty, it will be printed after the first line of the
|
||||
/// file, and each line should begin with '#'.
|
||||
/** @brief Print the mesh to the given stream using the default MFEM mesh
|
||||
format.
|
||||
|
||||
\see mfem::ofgzstream() for on-the-fly compression of ascii outputs. If
|
||||
@a comments is non-empty, it will be printed after the first line of the
|
||||
file, and each line should begin with '#'. */
|
||||
virtual void Print(std::ostream &os = mfem::out,
|
||||
const std::string &comments = "") const
|
||||
{ Printer(os, "", comments); }
|
||||
@@ -2537,7 +2540,7 @@ public:
|
||||
|
||||
#ifdef MFEM_USE_NETCDF
|
||||
/// @brief Export a mesh to an Exodus II file.
|
||||
void PrintExodusII(const std::string fpath);
|
||||
void PrintExodusII(const std::string &fpath);
|
||||
#endif
|
||||
|
||||
/** @brief Prints the mesh with boundary elements given by the boundary of
|
||||
|
||||
+3
-13
@@ -802,21 +802,11 @@ struct BufferReader : BufferReaderBase
|
||||
{
|
||||
// Each "data block" is preceded by a header that is either UInt32 or
|
||||
// UInt64. The rest of the data follows.
|
||||
uint64_t data_size;
|
||||
if (header_type == UINT32_HEADER)
|
||||
{
|
||||
uint32_t *data_size_32 = (uint32_t *)header_buf;
|
||||
data_size = *data_size_32;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t *data_size_64 = (uint64_t *)header_buf;
|
||||
data_size = *data_size_64;
|
||||
}
|
||||
MFEM_VERIFY(sizeof(F)*n == data_size, "AppendedData: wrong data size");
|
||||
MFEM_VERIFY(sizeof(F)*n == ReadHeaderEntry(header_buf),
|
||||
"AppendedData: wrong data size");
|
||||
}
|
||||
|
||||
if (std::is_same<T, F>::value)
|
||||
if (std::is_same_v<T, F>)
|
||||
{
|
||||
// Special case: no type conversions necessary, so can just memcpy
|
||||
memcpy(dest, buf, sizeof(T)*n);
|
||||
|
||||
+99
-27
@@ -9,8 +9,13 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "mesh_headers.hpp"
|
||||
#include "../fem/fem.hpp"
|
||||
#include "nurbs.hpp"
|
||||
|
||||
#include "point.hpp"
|
||||
#include "segment.hpp"
|
||||
#include "quadrilateral.hpp"
|
||||
#include "hexahedron.hpp"
|
||||
#include "../fem/gridfunc.hpp"
|
||||
#include "../general/text.hpp"
|
||||
|
||||
#include <fstream>
|
||||
@@ -33,6 +38,7 @@ KnotVector::KnotVector(istream &input)
|
||||
|
||||
knot.Load(input, NumOfControlPoints + Order + 1);
|
||||
GetElements();
|
||||
coarse = false;
|
||||
}
|
||||
|
||||
KnotVector::KnotVector(int order, int NCP)
|
||||
@@ -41,12 +47,13 @@ KnotVector::KnotVector(int order, int NCP)
|
||||
NumOfControlPoints = NCP;
|
||||
knot.SetSize(NumOfControlPoints + Order + 1);
|
||||
NumOfElements = 0;
|
||||
coarse = false;
|
||||
|
||||
knot = -1.;
|
||||
}
|
||||
|
||||
KnotVector::KnotVector(int order, const Vector& intervals,
|
||||
const Array<int>& continuity )
|
||||
const Array<int>& continuity)
|
||||
{
|
||||
// NOTE: This may need to be generalized to support periodicity
|
||||
// in the future.
|
||||
@@ -86,6 +93,7 @@ KnotVector::KnotVector(int order, const Vector& intervals,
|
||||
++NumOfElements;
|
||||
}
|
||||
}
|
||||
coarse = false;
|
||||
}
|
||||
|
||||
KnotVector &KnotVector::operator=(const KnotVector &kv)
|
||||
@@ -143,7 +151,7 @@ void KnotVector::UniformRefinement(Vector &newknots, int rf) const
|
||||
{
|
||||
for (int m = 1; m < rf; ++m)
|
||||
{
|
||||
newknots(j) = m * h * (knot(i) + knot(i+1));
|
||||
newknots(j) = ((1.0 - (m * h)) * knot(i)) + (m * h * knot(i+1));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +340,7 @@ void KnotVector::PrintFunctions(std::ostream &os, int samples) const
|
||||
}
|
||||
}
|
||||
|
||||
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
|
||||
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
|
||||
// Algorithm A2.2 p. 70
|
||||
void KnotVector::CalcShape(Vector &shape, int i, real_t xi) const
|
||||
{
|
||||
@@ -359,7 +367,7 @@ void KnotVector::CalcShape(Vector &shape, int i, real_t xi) const
|
||||
}
|
||||
}
|
||||
|
||||
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
|
||||
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
|
||||
// Algorithm A2.3 p. 72
|
||||
void KnotVector::CalcDShape(Vector &grad, int i, real_t xi) const
|
||||
{
|
||||
@@ -417,7 +425,7 @@ void KnotVector::CalcDShape(Vector &grad, int i, real_t xi) const
|
||||
}
|
||||
}
|
||||
|
||||
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
|
||||
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
|
||||
// Algorithm A2.3 p. 72
|
||||
void KnotVector::CalcDnShape(Vector &gradn, int n, int i, real_t xi) const
|
||||
{
|
||||
@@ -537,11 +545,11 @@ void KnotVector::FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const
|
||||
int i = j - d;
|
||||
if (isElement(i))
|
||||
{
|
||||
arg1 = 1e-16;
|
||||
arg1 = std::numeric_limits<real_t>::epsilon() / 2_r;
|
||||
CalcShape(shape, i, arg1);
|
||||
max1 = shape[d];
|
||||
|
||||
arg2 = 1-(1e-16);
|
||||
arg2 = 1_r - arg1;
|
||||
CalcShape(shape, i, arg2);
|
||||
max2 = shape[d];
|
||||
|
||||
@@ -579,9 +587,9 @@ void KnotVector::FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const
|
||||
}
|
||||
}
|
||||
|
||||
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
|
||||
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
|
||||
// Algorithm A9.1 p. 369
|
||||
void KnotVector::FindInterpolant(Array<Vector*> &x)
|
||||
void KnotVector::FindInterpolant(Array<Vector*> &x, bool reuse_inverse)
|
||||
{
|
||||
int order = GetOrder();
|
||||
int ncp = GetNCP();
|
||||
@@ -589,29 +597,93 @@ void KnotVector::FindInterpolant(Array<Vector*> &x)
|
||||
// Find interpolation points
|
||||
Vector xi_args, u_args;
|
||||
Array<int> i_args;
|
||||
FindMaxima(i_args,xi_args, u_args);
|
||||
FindMaxima(i_args, xi_args, u_args);
|
||||
|
||||
// Assemble collocation matrix
|
||||
Vector shape(order+1);
|
||||
DenseMatrix A(ncp,ncp);
|
||||
A = 0.0;
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
// If using LAPACK, we use banded matrix storage (order + 1 nonzeros per row).
|
||||
// Find banded structure of matrix.
|
||||
int KL = 0; // Number of subdiagonals
|
||||
int KU = 0; // Number of superdiagonals
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
CalcShape(shape, i_args[i], xi_args[i]);
|
||||
for (int p = 0; p < order+1; p++)
|
||||
{
|
||||
A(i,i_args[i] + p) = shape[p];
|
||||
const int col = i_args[i] + p;
|
||||
if (col < i)
|
||||
{
|
||||
KL = std::max(KL, i - col);
|
||||
}
|
||||
else if (i < col)
|
||||
{
|
||||
KU = std::max(KU, col - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Solve problems
|
||||
A.Invert();
|
||||
const int LDAB = (2*KL) + KU + 1;
|
||||
const int N = ncp;
|
||||
|
||||
fact_AB.SetSize(LDAB, N);
|
||||
#else
|
||||
// Without LAPACK, we store and invert a DenseMatrix (inefficient).
|
||||
if (!reuse_inverse)
|
||||
{
|
||||
A_coll_inv.SetSize(ncp, ncp);
|
||||
A_coll_inv = 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vector shape(order+1);
|
||||
|
||||
if (!reuse_inverse) // Set collocation matrix entries
|
||||
{
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
CalcShape(shape, i_args[i], xi_args[i]);
|
||||
for (int p = 0; p < order+1; p++)
|
||||
{
|
||||
const int j = i_args[i] + p;
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
fact_AB(KL+KU+i-j,j) = shape[p];
|
||||
#else
|
||||
A_coll_inv(i,j) = shape[p];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Solve the system
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
const int NRHS = x.Size();
|
||||
DenseMatrix B(N, NRHS);
|
||||
for (int j=0; j<NRHS; ++j)
|
||||
{
|
||||
for (int i=0; i<N; ++i) { B(i, j) = (*x[j])[i]; }
|
||||
}
|
||||
|
||||
if (reuse_inverse)
|
||||
{
|
||||
BandedFactorizedSolve(KL, KU, fact_AB, B, false, fact_ipiv);
|
||||
}
|
||||
else
|
||||
{
|
||||
BandedSolve(KL, KU, fact_AB, B, fact_ipiv);
|
||||
}
|
||||
|
||||
for (int j=0; j<NRHS; ++j)
|
||||
{
|
||||
for (int i=0; i<N; ++i) { (*x[j])[i] = B(i, j); }
|
||||
}
|
||||
#else
|
||||
if (!reuse_inverse) { A_coll_inv.Invert(); }
|
||||
Vector tmp;
|
||||
for (int i= 0; i < x.Size(); i++)
|
||||
for (int i = 0; i < x.Size(); i++)
|
||||
{
|
||||
tmp = *x[i];
|
||||
A.Mult(tmp,*x[i]);
|
||||
A_coll_inv.Mult(tmp, *x[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int KnotVector::findKnotSpan(real_t u) const
|
||||
@@ -1413,7 +1485,7 @@ void NURBSPatch::DegreeElevate(int t)
|
||||
}
|
||||
}
|
||||
|
||||
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
|
||||
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
|
||||
void NURBSPatch::DegreeElevate(int dir, int t)
|
||||
{
|
||||
if (dir >= kv.Size() || dir < 0)
|
||||
@@ -1431,8 +1503,8 @@ void NURBSPatch::DegreeElevate(int dir, int t)
|
||||
KnotVector &oldkv = *kv[dir];
|
||||
oldkv.GetElements();
|
||||
|
||||
NURBSPatch *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
|
||||
oldkv.GetNCP() + oldkv.GetNE()*t);
|
||||
auto *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
|
||||
oldkv.GetNCP() + oldkv.GetNE()*t);
|
||||
NURBSPatch &newp = *newpatch;
|
||||
KnotVector &newkv = *newp.GetKV(dir);
|
||||
|
||||
@@ -2377,7 +2449,7 @@ NURBSExtension::NURBSExtension(Mesh *mesh_array[], int num_pieces)
|
||||
}
|
||||
|
||||
NURBSExtension::NURBSExtension(const Mesh *patch_topology,
|
||||
const Array<const NURBSPatch*> patches_)
|
||||
const Array<const NURBSPatch*> &patches_)
|
||||
{
|
||||
// Basic topology checks
|
||||
MFEM_VERIFY(patches_.Size() > 0, "Must have at least one patch");
|
||||
@@ -4587,7 +4659,7 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
|
||||
|
||||
// Flip vector
|
||||
int size = pkvc[d]->Size();
|
||||
int ns = ceil(size/2.0);
|
||||
int ns = static_cast<int>(ceil(size/2.0));
|
||||
for (int j = 0; j < ns; j++)
|
||||
{
|
||||
real_t tmp = apb - pkvc[d]->Elem(j);
|
||||
@@ -4647,7 +4719,7 @@ void NURBSExtension::KnotRemove(Array<Vector *> &kv, real_t tol)
|
||||
|
||||
// Flip vector
|
||||
int size = pkvc[d]->Size();
|
||||
int ns = ceil(size/2.0);
|
||||
int ns = static_cast<int>(ceil(size/2.0));
|
||||
for (int j = 0; j < ns; j++)
|
||||
{
|
||||
real_t tmp = apb - pkvc[d]->Elem(j);
|
||||
|
||||
+20
-7
@@ -22,7 +22,6 @@
|
||||
#include "../general/communication.hpp"
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -55,7 +54,7 @@ protected:
|
||||
|
||||
public:
|
||||
/// Create an empty KnotVector.
|
||||
KnotVector() { }
|
||||
KnotVector() = default;
|
||||
|
||||
/** @brief Create a KnotVector by reading data from stream @a input. Two
|
||||
integers are read, for order and number of control points. */
|
||||
@@ -74,7 +73,7 @@ public:
|
||||
polynomial degree). Periodicity is not supported.
|
||||
*/
|
||||
KnotVector(int order, const Vector& intervals,
|
||||
const Array<int>& continuity );
|
||||
const Array<int>& continuity);
|
||||
|
||||
/// Copy constructor.
|
||||
KnotVector(const KnotVector &kv) { (*this) = kv; }
|
||||
@@ -144,8 +143,13 @@ public:
|
||||
/** @brief Global curve interpolation through the points @a x (overwritten).
|
||||
@a x is an array with the length of the spatial dimension containing
|
||||
vectors with spatial coordinates. The control points of the interpolated
|
||||
curve are returned in @a x in the same form. */
|
||||
void FindInterpolant(Array<Vector*> &x);
|
||||
curve are returned in @a x in the same form.
|
||||
|
||||
The inverse of the collocation matrix, used in the interpolation, is
|
||||
stored for repeated calls and used if @a reuse_inverse is true. Reuse is
|
||||
valid only if this KnotVector has not changed since the initial call with
|
||||
@a reuse_inverse false. */
|
||||
void FindInterpolant(Array<Vector*> &x, bool reuse_inverse = false);
|
||||
|
||||
/** Set @a diff, comprised of knots in @a kv not contained in this KnotVector.
|
||||
@a kv must be of the same order as this KnotVector. The current
|
||||
@@ -203,6 +207,14 @@ public:
|
||||
/** Flag to indicate whether the KnotVector has been coarsened, which means
|
||||
it is ready for non-nested refinement. */
|
||||
bool coarse;
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
// Data for reusing banded matrix factorization in FindInterpolant().
|
||||
DenseMatrix fact_AB; /// Banded matrix factorization
|
||||
Array<int> fact_ipiv; /// Row pivot indices
|
||||
#else
|
||||
DenseMatrix A_coll_inv; /// Collocation matrix inverse
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -286,7 +298,7 @@ public:
|
||||
includes the weight. The array of control point coordinates stores each
|
||||
point's coordinates contiguously, and points are ordered in a standard
|
||||
ijk grid ordering. */
|
||||
NURBSPatch(Array<const KnotVector *> &kv_, int dim_,
|
||||
NURBSPatch(Array<const KnotVector *> &kv_, int dim_,
|
||||
const real_t* control_points);
|
||||
|
||||
/// Constructor for a patch of dimension equal to the size of @a kv.
|
||||
@@ -701,7 +713,8 @@ public:
|
||||
|
||||
NURBSExtension(Mesh *mesh_array[], int num_pieces);
|
||||
|
||||
NURBSExtension(const Mesh *patch_topology, const Array<const NURBSPatch*> p);
|
||||
NURBSExtension(const Mesh *patch_topology,
|
||||
const Array<const NURBSPatch*> &patches_);
|
||||
|
||||
/// Copy assignment not supported.
|
||||
NURBSExtension& operator=(const NURBSExtension&) = delete;
|
||||
|
||||
+2
-1
@@ -3132,11 +3132,12 @@ void ParMesh::GetFaceNbrElementTransformation(
|
||||
pNodes->ParFESpace()->GetFaceNbrElementVDofs(FaceNo, vdofs);
|
||||
int n = vdofs.Size()/spaceDim;
|
||||
pointmat.SetSize(spaceDim, n);
|
||||
pNodes->FaceNbrData().HostRead();
|
||||
for (int k = 0; k < spaceDim; k++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
pointmat(k,j) = (pNodes->FaceNbrData())(vdofs[n*k+j]);
|
||||
pointmat(k,j) = AsConst(pNodes->FaceNbrData())(vdofs[n*k+j]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,15 +257,7 @@ template <typename SubMeshT>
|
||||
void AddBoundaryElements(SubMeshT &mesh,
|
||||
const std::unordered_map<int,int> &lface_to_boundary_attribute)
|
||||
{
|
||||
mesh.Dimension();
|
||||
const int num_codim_1 = [&mesh]()
|
||||
{
|
||||
auto Dim = mesh.Dimension();
|
||||
if (Dim == 1) { return mesh.GetNV(); }
|
||||
else if (Dim == 2) { return mesh.GetNEdges(); }
|
||||
else if (Dim == 3) { return mesh.GetNFaces(); }
|
||||
else { MFEM_ABORT("Invalid dimension."); return -1; }
|
||||
}();
|
||||
const int num_codim_1 = mesh.GetNumFaces();
|
||||
|
||||
if (mesh.Dimension() == 3)
|
||||
{
|
||||
|
||||
+44
-42
@@ -84,7 +84,7 @@ void VTKHDF::EnsureSteps()
|
||||
}
|
||||
|
||||
hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
|
||||
int ndims)
|
||||
Dims &dims)
|
||||
{
|
||||
const char *name_c = name.c_str();
|
||||
|
||||
@@ -94,20 +94,23 @@ hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
|
||||
if (status == 0)
|
||||
{
|
||||
// Dataset does not exist, create it.
|
||||
Dims dims(ndims);
|
||||
Dims maxdims(ndims, H5S_UNLIMITED);
|
||||
const hid_t fspace = H5Screate_simple(ndims, dims, maxdims);
|
||||
const int ndims = dims.ndims;
|
||||
// The dataset is allowed to grow in the first dimension, but is fixed
|
||||
// in size in all other dimesions; the maximum dataset size is same as
|
||||
// dims, but unlimited in first dimension.
|
||||
Dims max_dims = dims;
|
||||
max_dims[0] = H5S_UNLIMITED;
|
||||
const hid_t fspace = H5Screate_simple(ndims, dims, max_dims);
|
||||
|
||||
Dims chunk(ndims);
|
||||
size_t chunk_size_bytes = 1024 * 1024 / 2; // 0.5 MB
|
||||
const size_t t_bytes = H5Tget_size(type);
|
||||
for (int i = 1; i < ndims; ++i)
|
||||
{
|
||||
chunk[i] = 16;
|
||||
chunk_size_bytes /= 16;
|
||||
chunk[i] = dims[i];
|
||||
chunk_size_bytes /= dims[i];
|
||||
}
|
||||
chunk[0] = chunk_size_bytes / t_bytes;
|
||||
for (int i = 1; i < ndims; ++i) { chunk[i] = 16; }
|
||||
const hid_t dcpl = H5Pcreate(H5P_DATASET_CREATE);
|
||||
H5Pset_chunk(dcpl, ndims, chunk);
|
||||
if (compression_level >= 0)
|
||||
@@ -124,7 +127,19 @@ hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
|
||||
else if (status > 0)
|
||||
{
|
||||
// Dataset exists, open it.
|
||||
return H5Dopen2(f, name_c, H5P_DEFAULT);
|
||||
const hid_t d = H5Dopen2(f, name_c, H5P_DEFAULT);
|
||||
|
||||
// Resize the dataset, set dims to its new size.
|
||||
Dims old_dims(dims.ndims);
|
||||
const hid_t dspace = H5Dget_space(d);
|
||||
const int ndims_dset = H5Sget_simple_extent_ndims(dspace);
|
||||
MFEM_VERIFY(ndims_dset == dims.ndims, "");
|
||||
H5Sget_simple_extent_dims(dspace, old_dims, NULL);
|
||||
H5Sclose(dspace);
|
||||
dims[0] += old_dims[0];
|
||||
H5Dset_extent(d, dims);
|
||||
|
||||
return d;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -160,27 +175,13 @@ void VTKHDF::AppendParData(hid_t f, const std::string &name, hsize_t locsize,
|
||||
hsize_t offset, Dims globsize, T *data)
|
||||
{
|
||||
const int ndims = globsize.ndims;
|
||||
const hid_t d = EnsureDataset(f, name, GetTypeID<T>(), ndims);
|
||||
|
||||
// Resize the dataset, set dims to its new size.
|
||||
hsize_t old_size;
|
||||
Dims dims(ndims);
|
||||
{
|
||||
const hid_t dspace = H5Dget_space(d);
|
||||
const int ndims_dset = H5Sget_simple_extent_ndims(dspace);
|
||||
MFEM_VERIFY(ndims_dset == ndims, "");
|
||||
H5Sget_simple_extent_dims(dspace, dims, NULL);
|
||||
H5Sclose(dspace);
|
||||
old_size = dims[0];
|
||||
dims[0] += globsize[0];
|
||||
for (int i = 1; i < ndims; ++i) { dims[i] = globsize[i]; }
|
||||
H5Dset_extent(d, dims);
|
||||
}
|
||||
Dims dims = globsize;
|
||||
const hid_t d = EnsureDataset(f, name, GetTypeID<T>(), dims);
|
||||
|
||||
// Write the new entry.
|
||||
const hid_t dspace = H5Dget_space(d);
|
||||
Dims start(ndims);
|
||||
start[0] = old_size + offset;
|
||||
start[0] = dims[0] - globsize[0] + offset;
|
||||
Dims count(ndims);
|
||||
count[0] = locsize;
|
||||
for (int i = 1; i < ndims; ++i) { count[i] = globsize[i]; }
|
||||
@@ -334,14 +335,14 @@ void VTKHDF::Truncate(const real_t t)
|
||||
}
|
||||
|
||||
// Index of found time index (may be 'one-past-the-end' if not found)
|
||||
const int i = std::distance(tvals.begin(), it);
|
||||
const ptrdiff_t i = std::distance(tvals.begin(), it);
|
||||
|
||||
// Only truncate if needed
|
||||
const bool truncate = it != tvals.end();
|
||||
|
||||
// Number of steps we are keeping
|
||||
nsteps = i;
|
||||
H5LTset_attribute_int(vtk, "Steps", "NSteps", &nsteps, 1);
|
||||
H5LTset_attribute_ulong(vtk, "Steps", "NSteps", &nsteps, 1);
|
||||
|
||||
// We want to continue writing immediately after step 'i - 1'. If i = 0,
|
||||
// then this is at the beginning of the file, and the offsets do not need
|
||||
@@ -509,7 +510,7 @@ void VTKHDF::UpdateSteps(real_t t)
|
||||
|
||||
// Set the NSteps attribute
|
||||
++nsteps;
|
||||
H5LTset_attribute_int(steps, ".", "NSteps", &nsteps, 1);
|
||||
H5LTset_attribute_ulong(steps, ".", "NSteps", &nsteps, 1);
|
||||
|
||||
AppendValue(steps, "Values", t);
|
||||
AppendValue(steps, "PartOffsets", part_offset);
|
||||
@@ -618,16 +619,16 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
|
||||
|
||||
for (int i = 0; i < pmat.Width(); i++)
|
||||
{
|
||||
points.push_back(pmat(0,i));
|
||||
if (pmat.Height() > 1) { points.push_back(pmat(1,i)); }
|
||||
points.push_back(FP_T(pmat(0,i)));
|
||||
if (pmat.Height() > 1) { points.push_back(FP_T(pmat(1,i))); }
|
||||
else { points.push_back(0.0); }
|
||||
if (pmat.Height() > 2) { points.push_back(pmat(2,i)); }
|
||||
if (pmat.Height() > 2) { points.push_back(FP_T(pmat(2,i))); }
|
||||
else { points.push_back(0.0); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hsize_t ne_0 = mesh.GetNE();
|
||||
const int ne_0 = mesh.GetNE();
|
||||
const hsize_t ne = high_order ? ne_0 : ne_ref;
|
||||
|
||||
AppendParData(vtk, "NumberOfPoints", 1, mpi_rank, mpi_dims, &np);
|
||||
@@ -657,7 +658,7 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
|
||||
if (high_order)
|
||||
{
|
||||
Array<int> local_connectivity;
|
||||
for (size_t e = 0; e < ne; ++e)
|
||||
for (int e = 0; e < int(ne); ++e)
|
||||
{
|
||||
offsets[e] = off;
|
||||
const Geometry::Type geom = mesh.GetElementGeometry(e);
|
||||
@@ -675,7 +676,7 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
|
||||
{
|
||||
int off_0 = 0;
|
||||
int e_ref = 0;
|
||||
for (hsize_t e = 0; e < ne_0; ++e)
|
||||
for (int e = 0; e < ne_0; ++e)
|
||||
{
|
||||
const Geometry::Type geom = mesh.GetElementGeometry(e);
|
||||
const int nv = get_nv(e);
|
||||
@@ -714,12 +715,13 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
|
||||
const int *vtk_geom_map =
|
||||
high_order ? VTKGeometry::HighOrderMap : VTKGeometry::Map;
|
||||
int e_ref = 0;
|
||||
for (hsize_t e = 0; e < ne_0; ++e)
|
||||
for (int e = 0; e < ne_0; ++e)
|
||||
{
|
||||
const int ne_ref = get_ne_ref(e, ref_0);
|
||||
for (int i = 0; i < ne_ref; ++i, ++e_ref)
|
||||
const int ne_ref_e = get_ne_ref(e, ref_0);
|
||||
for (int i = 0; i < ne_ref_e; ++i, ++e_ref)
|
||||
{
|
||||
cell_types[e_ref] = vtk_geom_map[mesh.GetElementGeometry(e)];
|
||||
cell_types[e_ref] = static_cast<unsigned char>(
|
||||
vtk_geom_map[mesh.GetElementGeometry(e)]);
|
||||
}
|
||||
}
|
||||
AppendParData(vtk, "Types", ne, e_offset, Dims({ne_total}),
|
||||
@@ -732,11 +734,11 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
|
||||
EnsureGroup("CellData", cell_data);
|
||||
std::vector<int> attributes(ne);
|
||||
hsize_t e_ref = 0;
|
||||
for (hsize_t e = 0; e < ne_0; ++e)
|
||||
for (int e = 0; e < ne_0; ++e)
|
||||
{
|
||||
const int attr = mesh.GetAttribute(e);
|
||||
const int ne_ref = get_ne_ref(e, ref_0);
|
||||
for (int i = 0; i < ne_ref; ++i, ++e_ref)
|
||||
const int ne_ref_e = get_ne_ref(e, ref_0);
|
||||
for (int i = 0; i < ne_ref_e; ++i, ++e_ref)
|
||||
{
|
||||
attributes[e_ref] = attr;
|
||||
}
|
||||
@@ -772,7 +774,7 @@ void VTKHDF::SaveGridFunction(const GridFunction &gf, const std::string &name)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; ++vd)
|
||||
{
|
||||
point_values[off] = vec_val(vd, i);
|
||||
point_values[off] = FP_T(vec_val(vd, i));
|
||||
++off;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -76,14 +76,14 @@ private:
|
||||
/// Wrapper for storing dataset dimensions (max ndims is 2D in VTKHDF).
|
||||
struct Dims
|
||||
{
|
||||
static constexpr int MAX_NDIMS = 2;
|
||||
static constexpr size_t MAX_NDIMS = 2;
|
||||
std::array<hsize_t, MAX_NDIMS> data = { }; // Zero initialized
|
||||
int ndims = 0;
|
||||
Dims() = default;
|
||||
Dims(int ndims_) : ndims(ndims_) { MFEM_ASSERT(ndims <= MAX_NDIMS, ""); }
|
||||
Dims(int ndims_, hsize_t val) : Dims(ndims_) { data.fill(val); }
|
||||
template <typename T>
|
||||
Dims(std::initializer_list<T> data_) : Dims(data_.size())
|
||||
Dims(std::initializer_list<T> data_) : Dims(int(data_.size()))
|
||||
{ std::copy(data_.begin(), data_.end(), data.begin()); }
|
||||
operator hsize_t*() { return data.data(); }
|
||||
hsize_t &operator[](int i) { return data[i]; }
|
||||
@@ -97,7 +97,7 @@ private:
|
||||
hid_t steps = H5I_INVALID_HID;
|
||||
|
||||
/// Number of time steps saved.
|
||||
int nsteps = 0;
|
||||
unsigned long nsteps = 0;
|
||||
|
||||
/// Keep track of the offsets into the data arrays at each time step.
|
||||
struct Offsets
|
||||
@@ -123,8 +123,8 @@ private:
|
||||
class MeshId
|
||||
{
|
||||
const Mesh *mesh_ptr = nullptr;
|
||||
int sequence = -1;
|
||||
int nodes_sequence = -1;
|
||||
long sequence = -1;
|
||||
long nodes_sequence = -1;
|
||||
bool high_order = true;
|
||||
int ref = -1;
|
||||
public:
|
||||
@@ -187,8 +187,10 @@ private:
|
||||
/// The rank (number of dimensions) of the dataset is given by @a ndims and
|
||||
/// its data type is given by @a type.
|
||||
///
|
||||
/// The dataset will initially have zero size and unlimited maximum size.
|
||||
hid_t EnsureDataset(hid_t f, const std::string &name, hid_t type, int ndims);
|
||||
/// If the dataset does not exist, it will initially have size @a dims.
|
||||
/// Otherwise, it will be resized to append data of size @a dims, and @a dims
|
||||
/// will be set to the new total size.
|
||||
hid_t EnsureDataset(hid_t f, const std::string &name, hid_t type, Dims &dims);
|
||||
|
||||
/// @brief Ensure the named group is open, creating it if needed. Set @a
|
||||
/// group to the ID.
|
||||
|
||||
@@ -224,5 +224,6 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
|
||||
delete metric;
|
||||
delete fec_mesh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -52,20 +52,6 @@ if (MFEM_USE_MPI)
|
||||
${NAVIER_COMMON_FILES}
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(incompressible_navier_dfem
|
||||
MAIN incompressible_navier_dfem.cpp
|
||||
EXTRA_HEADERS incompressible_navier_nvtx.hpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(incompNS_2Dtest
|
||||
MAIN incompNS_2Dtest.cpp
|
||||
EXTRA_SOURCES incompressible_navier_solver.cpp
|
||||
incompressible_navier_tests.cpp
|
||||
EXTRA_HEADERS incompressible_navier_solver.hpp
|
||||
incompressible_navier_nvtx.hpp
|
||||
EXTRA_DEFINES MFEM_USE_CMAKE_TESTS
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(navier_turbchan
|
||||
MAIN navier_turbchan.cpp
|
||||
${NAVIER_COMMON_FILES}
|
||||
@@ -98,4 +84,4 @@ if (MFEM_USE_MPI)
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
// 3D flow over a cylinder benchmark example
|
||||
|
||||
#include "incompressible_navier_solver.hpp"
|
||||
|
||||
#define NVTX_COLOR ::gpu::nvtx::kLawnGreen
|
||||
#include "incompressible_navier_nvtx.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using namespace incompressible_navier;
|
||||
|
||||
void vel(const Vector &x, real_t t, Vector &u)
|
||||
{
|
||||
// real_t xi = x(0), yi = x(1);
|
||||
u = 0.0;
|
||||
}
|
||||
|
||||
void vel_inlet(const Vector &x, real_t t, Vector &u)
|
||||
{
|
||||
u = 0.0;
|
||||
if (x(0) < 0.001) { u(0) = -0.001 * (std::pow(x(1) - 0.5, 2.0) - 0.25); }
|
||||
}
|
||||
|
||||
MFEM_EXPORT int navier(int argc, char *argv[], double &u, double &p, double &Ψ)
|
||||
{
|
||||
dbg();
|
||||
static mfem::MPI_Session mpi(argc, argv);
|
||||
const int myid = mpi.WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
const char *device_config = "cpu";
|
||||
int serial_refinements = 1;
|
||||
int nx = 90, ny = 30;
|
||||
int v_order = 2;
|
||||
int p_order = 1;
|
||||
int t_order = 1;
|
||||
real_t kin_vis = 20.0;
|
||||
real_t dt = 1e-2;
|
||||
real_t t = 0.0;
|
||||
real_t t_final = 1.0;
|
||||
bool last_step = false;
|
||||
bool visualization = true;
|
||||
bool use_paraview = false;
|
||||
bool pa = false;
|
||||
int vis_steps = 100;
|
||||
int max_tsteps = -1;
|
||||
|
||||
constexpr int precision = 8;
|
||||
std::cout.precision(precision);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&serial_refinements, "-sr", "--serial-refinements",
|
||||
"Number serial refinements.");
|
||||
args.AddOption(&nx, "-nx", "--nx", "Number of elements in X.");
|
||||
args.AddOption(&ny, "-ny", "--ny", "Number of elements in Y.");
|
||||
args.AddOption(&v_order, "-vo", "--vo", "Order.");
|
||||
args.AddOption(&p_order, "-po", "--po", "Order.");
|
||||
args.AddOption(&t_order, "-to", "--to", "Order.");
|
||||
args.AddOption(&kin_vis, "-kv", "--kin-vis",
|
||||
"Kineic viscosity coefficient.");
|
||||
args.AddOption(&dt, "-dt", "--time-step", "Initial time step size.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly",
|
||||
"Enable or disable partial assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&use_paraview, "-pv", "--paraview", "-no-pv", "--no-paraview",
|
||||
"Use ParaView.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.AddOption(&max_tsteps, "-ms", "--max-steps",
|
||||
"Maximum number of steps (negative means no restriction).");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0) { args.PrintUsage(mfem::out); }
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (myid == 0) { args.PrintOptions(mfem::out); }
|
||||
|
||||
// Mesh *mesh = new Mesh("box-cylinder.mesh");
|
||||
const real_t sx = 3.0, sy = 1.0;
|
||||
const bool generate_edges = true;
|
||||
const auto QUAD = Element::QUADRILATERAL;
|
||||
Mesh mesh = Mesh::MakeCartesian2D(nx, ny, QUAD, generate_edges, sx, sy);
|
||||
|
||||
for (int i = 0; i < serial_refinements; ++i) { mesh.UniformRefinement(); }
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
std::cout << "Number of elements: " << mesh.GetNE() << std::endl;
|
||||
}
|
||||
|
||||
auto *pmesh = new ParMesh(MPI_COMM_WORLD, mesh);
|
||||
|
||||
// Create the flow solver.
|
||||
IncompressibleNavierSolver flowsolver(pmesh, v_order, p_order, t_order,
|
||||
kin_vis);
|
||||
flowsolver.EnablePA(pa);
|
||||
|
||||
// // Set the initial condition.
|
||||
// ParGridFunction *u_ic = flowsolver.GetCurrentVelocity();
|
||||
// VectorFunctionCoefficient u_excoeff(pmesh->Dimension(), vel);
|
||||
// u_ic->ProjectCoefficient(u_excoeff);
|
||||
|
||||
// Add Dirichlet boundary conditions to velocity space restricted to
|
||||
// selected attributes on the mesh.
|
||||
Array<int> attr(pmesh->bdr_attributes.Max());
|
||||
attr = 0;
|
||||
Array<int> attr_inlet(pmesh->bdr_attributes.Max());
|
||||
attr_inlet = 0;
|
||||
// Inlet is attribute 1.
|
||||
attr[0] = 1;
|
||||
// Walls is attribute 3.
|
||||
attr[2] = 1;
|
||||
flowsolver.AddVelDirichletBC(vel, attr);
|
||||
|
||||
attr_inlet[3] = 1;
|
||||
flowsolver.AddVelDirichletBC(vel_inlet, attr_inlet);
|
||||
|
||||
flowsolver.Setup(dt);
|
||||
|
||||
ParGridFunction *u_gf = flowsolver.GetCurrentVelocity();
|
||||
ParGridFunction *p_gf = flowsolver.GetCurrentPressure();
|
||||
ParGridFunction *psi_gf = flowsolver.GetCurrentPsi();
|
||||
|
||||
ParaViewDataCollection pvdc("3dfoc", pmesh);
|
||||
if (use_paraview)
|
||||
{
|
||||
pvdc.SetDataFormat(VTKFormat::BINARY32);
|
||||
// pvdc.SetHighOrderOutput(true);
|
||||
pvdc.SetCycle(0);
|
||||
pvdc.SetTime(t);
|
||||
pvdc.RegisterField("velocity", u_gf);
|
||||
pvdc.RegisterField("pressure", p_gf);
|
||||
pvdc.RegisterField("psi", psi_gf);
|
||||
pvdc.Save();
|
||||
}
|
||||
|
||||
for (int step = 0; !last_step; ++step)
|
||||
{
|
||||
if (step == max_tsteps) { last_step = true; }
|
||||
if (t + dt >= t_final - dt / 2) { last_step = true; }
|
||||
const bool vis_step = last_step || (step % vis_steps) == 0;
|
||||
|
||||
flowsolver.Step(t, dt, step, vis_step);
|
||||
|
||||
if (vis_step)
|
||||
{
|
||||
if (Mpi::Root() && vis_steps)
|
||||
{
|
||||
printf("%11s %11s\n", "Time", "dt");
|
||||
printf("%.5E %.5E\n", t, dt);
|
||||
}
|
||||
if (use_paraview)
|
||||
{
|
||||
pvdc.SetCycle(step);
|
||||
pvdc.SetTime(t);
|
||||
pvdc.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flowsolver.PrintTimingData();
|
||||
|
||||
auto reduce = [](ParGridFunction *gf) -> real_t { return (*gf) * (*gf); };
|
||||
// auto reduce = [](ParGridFunction *gf) -> real_t { return gf->Norml2(); };
|
||||
u = reduce(u_gf), p = reduce(p_gf), Ψ = reduce(psi_gf);
|
||||
|
||||
fflush(stdout);
|
||||
delete pmesh;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef MFEM_USE_CMAKE_TESTS
|
||||
int main(int argc, char *argv[])
|
||||
try
|
||||
{
|
||||
dbg();
|
||||
double u, p, Ψ; // unused
|
||||
return navier(argc, argv, u, p, Ψ);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::cerr << "\033[31m..xxxXXX[ERROR]XXXxxx.." << std::endl;
|
||||
std::cerr << "\033[31m{}" << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif // MFEM_USE_CMAKE_TESTS
|
||||
@@ -1,397 +0,0 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "mfem.hpp"
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
|
||||
#include "linalg/tensor.hpp"
|
||||
using mfem::future::tensor;
|
||||
|
||||
#define NVTX_COLOR ::gpu::nvtx::kOrchid
|
||||
#include "incompressible_navier_nvtx.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM>
|
||||
void DiffusionSetup(const ParFiniteElementSpace &sfes,
|
||||
const IntegrationRule &ir,
|
||||
const Array<int> &domain_attributes,
|
||||
ParameterFunction &qdata)
|
||||
{
|
||||
NVTX_MARK_FUNCTION;
|
||||
auto pmesh = sfes.GetParMesh();
|
||||
auto nodes = static_cast<ParGridFunction *>(pmesh->GetNodes());
|
||||
auto mfes = nodes->ParFESpace();
|
||||
|
||||
constexpr int U = 0, Ξ = 1, Δ = 2;
|
||||
|
||||
DifferentiableOperator dop(
|
||||
{{ U, &sfes }},
|
||||
{
|
||||
{ { Ξ, mfes },
|
||||
{ Δ, &qdata.GetParameterSpace() }
|
||||
}
|
||||
},
|
||||
*pmesh);
|
||||
const auto qfunc =
|
||||
[] MFEM_HOST_DEVICE(const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
tensor<real_t, DIM, DIM> C{};
|
||||
C(0, 0) = M_PI;
|
||||
C(0, 1) = 0, C(1, 0) = 0;
|
||||
assert(C(0, 1) == 0 && C(1, 0) == 0); // diff otherwise
|
||||
C(1, 1) = 1.0 / M_PI;
|
||||
return tuple{ C * invJ * transpose(invJ) * det(J) * w };
|
||||
};
|
||||
dop.AddDomainIntegrator(qfunc,
|
||||
tuple{ Gradient<Ξ>{}, Weight{} }, // inputs
|
||||
tuple{ Identity<Δ>{} }, // outputs
|
||||
ir, domain_attributes);
|
||||
dop.SetParameters({ nodes, &qdata });
|
||||
|
||||
Vector unused(sfes.GetTrueVSize());
|
||||
dop.Mult(unused, qdata);
|
||||
qdata.HostRead();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM>
|
||||
void DiffusionApply(const ParFiniteElementSpace &sfes,
|
||||
const IntegrationRule &ir,
|
||||
const Array<int> &domain_attributes,
|
||||
ParameterFunction &qdata,
|
||||
const Vector &x, Vector &y)
|
||||
{
|
||||
NVTX_MARK_FUNCTION;
|
||||
auto pmesh = sfes.GetParMesh();
|
||||
constexpr int U = 0, Q = 1;
|
||||
auto qd_ps = &qdata.GetParameterSpace();
|
||||
DifferentiableOperator dop({ { U, &sfes } }, { { Q, qd_ps } }, *pmesh);
|
||||
const auto qfunc =[] MFEM_HOST_DEVICE(const tensor<real_t, DIM> &∇u,
|
||||
const tensor<real_t, DIM, DIM> &Q)
|
||||
{
|
||||
return tuple{ Q * ∇u };
|
||||
};
|
||||
dop.AddDomainIntegrator(
|
||||
qfunc,
|
||||
tuple{ Gradient<U>{}, Identity<Q>{} },
|
||||
tuple{ Gradient<U>{} },
|
||||
ir, domain_attributes);
|
||||
dop.SetParameters({ &qdata });
|
||||
dop.Mult(x, y);
|
||||
y.HostRead();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM>
|
||||
int DiffVerification(ParFiniteElementSpace &h1fes,
|
||||
const IntegrationRule &ir,
|
||||
const Vector &qdata,
|
||||
const Vector &x, const Vector &y)
|
||||
{
|
||||
NVTX_MARK_FUNCTION;
|
||||
constexpr real_t ϵ = 1e-12;
|
||||
|
||||
MatrixFunctionCoefficient matrix_coeff(
|
||||
DIM, [](const Vector &, DenseMatrix &C)
|
||||
{
|
||||
C.SetSize(DIM);
|
||||
C(0, 0) = M_PI;
|
||||
C(0, 1) = 0, C(1, 0) = 0;
|
||||
assert(C(0, 1) == 0 && C(1, 0) == 0); // diff otherwise
|
||||
C(1, 1) = 1.0 / M_PI;
|
||||
});
|
||||
|
||||
ParBilinearForm a(&h1fes);
|
||||
|
||||
auto diff_integ = new DiffusionIntegrator(matrix_coeff);
|
||||
diff_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(diff_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
OperatorPtr A;
|
||||
a.Assemble(), a.Finalize();
|
||||
a.FormSystemMatrix(Array<int> {}, A);
|
||||
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
y2 = 0.0;
|
||||
A->Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
const auto diff_norm = diff.Norml2();
|
||||
if (diff_norm > ϵ)
|
||||
{
|
||||
dbg("\x1B[31m||dFdu_FD u^* - ex||_l2 = {}", diff_norm);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dbg("\x1B[32m||dFdu_FD u^* - ex||_l2 = {}", diff_norm);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM>
|
||||
void MassApply(const ParFiniteElementSpace &sfes,
|
||||
const IntegrationRule &ir,
|
||||
const Array<int> &domain_attributes,
|
||||
const Vector &x, Vector &y)
|
||||
{
|
||||
dbg();
|
||||
auto &pmesh = *sfes.GetParMesh();
|
||||
auto *nodes = static_cast<ParGridFunction *>(pmesh.GetNodes());
|
||||
auto *mfes = nodes->ParFESpace();
|
||||
constexpr int U = 0, Coords = 1;
|
||||
DifferentiableOperator dop({{ U, &sfes }}, {{ Coords, mfes }}, pmesh);
|
||||
const auto mf_mass_qf =
|
||||
[](const real_t &dudxi,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
return tuple{ dudxi * w * det(J) };
|
||||
};
|
||||
dop.AddDomainIntegrator(
|
||||
mf_mass_qf,
|
||||
tuple{ Value<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Value<U>{} },
|
||||
ir, domain_attributes);
|
||||
dop.SetParameters({ nodes });
|
||||
// Vector X(sfes.GetTrueVSize()), Y(sfes.GetTrueVSize());
|
||||
// sfes.GetRestrictionMatrix()->Mult(x, X);
|
||||
// dop.Mult(X, Y);
|
||||
dop.Mult(x, y);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM>
|
||||
int MassVerification(ParFiniteElementSpace &h1fes,
|
||||
const IntegrationRule &ir,
|
||||
const Vector &x, Vector &y)
|
||||
{
|
||||
ParBilinearForm a(&h1fes);
|
||||
auto mass_integ = new MassIntegrator;
|
||||
mass_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(mass_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.Assemble(), a.Finalize();
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
a.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
|
||||
const auto diff_norm = diff.Norml2();
|
||||
constexpr real_t ϵ = 1e-12;
|
||||
if (diff_norm > ϵ)
|
||||
{
|
||||
dbg("\x1B[31m||dFdu_FD u^* - ex||_l2 = {}", diff_norm);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
dbg("\x1B[32m||dFdu_FD u^* - ex||_l2 = {}", diff_norm);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM>
|
||||
void VectorDiffApply(const ParFiniteElementSpace &vfes,
|
||||
const IntegrationRule &ir,
|
||||
const Array<int> &domain_attributes,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
NVTX_MARK_FUNCTION;
|
||||
auto pmesh = vfes.GetParMesh();
|
||||
auto nodes = static_cast<ParGridFunction *>(pmesh->GetNodes());
|
||||
auto mfes = nodes->ParFESpace();
|
||||
constexpr int U = 0, Coords = 1;
|
||||
DifferentiableOperator dop({{ U, &vfes }}, {{ Coords, mfes }}, *pmesh);
|
||||
const auto qfunc =
|
||||
[] MFEM_HOST_DEVICE(const tensor<real_t, DIM, DIM> &∇u,
|
||||
const tensor<real_t, DIM, DIM> &J, const real_t &w)
|
||||
{
|
||||
return tuple{ ∇u * inv(J) * det(J) * w * transpose(inv(J)) };
|
||||
};
|
||||
dop.AddDomainIntegrator(qfunc,
|
||||
tuple{ Gradient<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Gradient<U>{} },
|
||||
ir, domain_attributes);
|
||||
dop.SetParameters({ nodes });
|
||||
dop.Mult(x, y);
|
||||
y.HostRead();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <int DIM, int VDIM = DIM>
|
||||
int VectorDiffVerif(ParFiniteElementSpace &h1fes,
|
||||
const IntegrationRule &ir, const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
NVTX_MARK_FUNCTION;
|
||||
ParBilinearForm a(&h1fes);
|
||||
auto A_integ = new VectorDiffusionIntegrator(VDIM);
|
||||
A_integ->SetIntRule(&ir);
|
||||
a.AddDomainIntegrator(A_integ);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.Assemble(), a.Finalize();
|
||||
Vector y2(h1fes.TrueVSize());
|
||||
a.Mult(x, y2);
|
||||
y2.HostRead();
|
||||
|
||||
Vector diff(y2);
|
||||
diff -= y;
|
||||
|
||||
const auto diff_norm = diff.Norml2();
|
||||
constexpr real_t ϵ = 1e-12;
|
||||
if (diff_norm > ϵ)
|
||||
{
|
||||
dbg("\x1B[31m||dFdu_FD u^* - ex||_l2 = {}", diff_norm);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
dbg("\x1B[32m||dFdu_FD u^* - ex||_l2 = {}", diff_norm);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char *argv[]) try
|
||||
{
|
||||
NVTX_MARK_FUNCTION;
|
||||
constexpr int DIM = 2, VDIM = DIM;
|
||||
|
||||
static mfem::MPI_Session mpi(argc, argv);
|
||||
const int myid = mpi.WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
const char *device_config = "cpu";
|
||||
const char *mesh_file = "none";
|
||||
int serial_refinements = 0;
|
||||
int nx = 1, ny = 1;
|
||||
int p = 1;
|
||||
bool visualization = false;
|
||||
bool pa = false;
|
||||
|
||||
std::cout.precision(8);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&p, "-o", "--order", "Finite element order.");
|
||||
args.AddOption(&serial_refinements, "-sr", "--serial-refinements",
|
||||
"Number serial refinements.");
|
||||
args.AddOption(&nx, "-nx", "--nx", "Number of elements in X.");
|
||||
args.AddOption(&ny, "-ny", "--ny", "Number of elements in Y.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly",
|
||||
"Enable or disable partial assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0) { args.PrintUsage(mfem::out); }
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (myid == 0) { args.PrintOptions(mfem::out); }
|
||||
|
||||
Mesh smesh;
|
||||
if (std::string(mesh_file) != "none")
|
||||
{
|
||||
smesh = Mesh(mesh_file);
|
||||
}
|
||||
else
|
||||
{
|
||||
const real_t sx = 3.0, sy = 1.0;
|
||||
const bool generate_edges = true;
|
||||
const auto QUAD = Element::QUADRILATERAL;
|
||||
smesh = Mesh::MakeCartesian2D(nx, ny, QUAD, generate_edges, sx, sy);
|
||||
}
|
||||
MFEM_ASSERT(smesh.Dimension() == 2, "2D mesh required!");
|
||||
|
||||
for (int i = 0; i < serial_refinements; ++i) { smesh.UniformRefinement(); }
|
||||
|
||||
dbg("Number of elements: {}", smesh.GetNE());
|
||||
ParMesh pmesh(MPI_COMM_WORLD, smesh);
|
||||
smesh.Clear();
|
||||
|
||||
pmesh.EnsureNodes();
|
||||
pmesh.SetCurvature(p);
|
||||
assert(DIM == pmesh.Dimension());
|
||||
|
||||
Array<int> domain_attributes;
|
||||
if (pmesh.attributes.Size() > 0)
|
||||
{
|
||||
domain_attributes.SetSize(pmesh.attributes.Max());
|
||||
domain_attributes = 1;
|
||||
}
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace fes(&pmesh, &fec), vfes(&pmesh, &fec, VDIM);
|
||||
dbg("#dofs:{} ", fes.GetTrueVSize());
|
||||
|
||||
const auto &fe = *fes.GetFE(0);
|
||||
const auto &ir =
|
||||
IntRules.Get(fe.GetGeomType(), fe.GetOrder() + fe.GetOrder() + fe.GetDim() - 1);
|
||||
|
||||
dbg("#ndof per el = {}", fe.GetDof());
|
||||
dbg("#nqp = {}", ir.GetNPoints());
|
||||
dbg("#q1d = {}", (int)floor(pow(ir.GetNPoints(), 1.0 / DIM) + 0.5));
|
||||
|
||||
ParGridFunction f1_gf(&fes);
|
||||
|
||||
auto f1 = [](const Vector &coords)
|
||||
{
|
||||
assert(DIM == 2);
|
||||
const double x = coords(0), y = coords(1);
|
||||
return M_PI + x + x * x + x * y + y;
|
||||
};
|
||||
FunctionCoefficient f1_c(f1);
|
||||
f1_gf.ProjectCoefficient(f1_c);
|
||||
|
||||
Vector x(f1_gf), y(fes.GetTrueVSize());
|
||||
|
||||
UniformParameterSpace qd_ps(pmesh, ir, DIM * DIM);
|
||||
ParameterFunction qdata(qd_ps);
|
||||
|
||||
dbg("Diffusion setup, apply & verification");
|
||||
DiffusionSetup<DIM>(fes, ir, domain_attributes, qdata);
|
||||
DiffusionApply<DIM>(fes, ir, domain_attributes, qdata, x, y);
|
||||
if (DiffVerification<DIM>(fes, ir, qdata, x, y) != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
|
||||
dbg("Mass apply & verification");
|
||||
MassApply<DIM>(fes, ir, domain_attributes, x, y);
|
||||
if (MassVerification<DIM>(fes, ir, x, y) != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
|
||||
dbg("Vector diffusion apply");
|
||||
VectorFunctionCoefficient vf1_c(VDIM, [](const Vector &coords, Vector &u)
|
||||
{
|
||||
assert(DIM == 2);
|
||||
const double x = coords(0), y = coords(1);
|
||||
u(0) = M_PI + 0.25 * x * x * y + y * y * x;
|
||||
u(1) = M_PI - 0.25 * x * y * y + y * x * x;
|
||||
});
|
||||
ParGridFunction vf1_gf(&vfes);
|
||||
vf1_gf.ProjectCoefficient(vf1_c);
|
||||
Vector vx(vf1_gf), vy(vfes.GetTrueVSize());
|
||||
VectorDiffApply<DIM>(vfes, ir, domain_attributes, vx, vy);
|
||||
if (VectorDiffVerif<DIM>(vfes, ir, vx, vy) != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::cerr << "\033[31m..xxxXXX[ERROR]XXXxxx.." << std::endl;
|
||||
std::cerr << "\033[31m{}" << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -1,478 +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
|
||||
|
||||
#define FMT_HEADER_ONLY
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
#ifdef MFEM_USE_CALIPER
|
||||
#include <caliper/cali.h>
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#include <cudaProfiler.h>
|
||||
#include <cuda_runtime_api.h>
|
||||
#include <nvToolsExt.h>
|
||||
#else
|
||||
struct nvtxEventAttributes_t
|
||||
{
|
||||
int version;
|
||||
int size;
|
||||
int category;
|
||||
int colorType;
|
||||
uint32_t color;
|
||||
int payloadType;
|
||||
uint64_t payload;
|
||||
int messageType;
|
||||
struct
|
||||
{
|
||||
std::string ascii;
|
||||
} message;
|
||||
};
|
||||
#define NVTX_VERSION 1
|
||||
#define NVTX_EVENT_ATTRIB_STRUCT_SIZE 256
|
||||
#define NVTX_COLOR_ARGB 0
|
||||
#define NVTX_MESSAGE_TYPE_ASCII 0
|
||||
#define nvtxRangePushEx(...)
|
||||
#define nvtxRangePop(...)
|
||||
#define cudaStreamSynchronize(...)
|
||||
#endif
|
||||
|
||||
namespace gpu::nvtx
|
||||
{
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// https://en.wikipedia.org/wiki/Web_colors#Extended_colors
|
||||
// http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html
|
||||
// clang-format off
|
||||
enum color_names
|
||||
{
|
||||
kBlack = 0, kNavyBlue, kDarkBlue, kMediumBlue, kBlue, kDarkGreen, kWebGreen, kTeal,
|
||||
kDarkCyan, kDeepSkyBlue, kDarkTurquoise, kMediumSpringGreen, kGreen, kLime,
|
||||
kSpringGreen, kAqua, kCyan, kMidnightBlue, kDodgerBlue, kLightSeaGreen, kForestGreen,
|
||||
kSeaGreen, kDarkSlateGray, kLimeGreen, kMediumSeaGreen, kTurquoise, kRoyalBlue,
|
||||
kSteelBlue, kDarkSlateBlue, kMediumTurquoise, kIndigo, kDarkOliveGreen, kCadetBlue,
|
||||
kCornflower, kRebeccaPurple, kMediumAquamarine, kDimGray, kSlateBlue, kOliveDrab,
|
||||
kSlateGray, kLightSlateGray, kMediumSlateBlue, kLawnGreen, kWebMaroon, kWebPurple,
|
||||
kChartreuse, kAquamarine, kOlive, kWebGray, kSkyBlue, kLightSkyBlue, kBlueViolet,
|
||||
kDarkRed, kDarkMagenta, kSaddleBrown, kDarkSeaGreen, kLightGreen, kMediumPurple,
|
||||
kDarkViolet, kPaleGreen, kDarkOrchid, kYellowGreen, kPurple, kSienna, kBrown,
|
||||
kDarkGray, kLightBlue, kGreenYellow, kPaleTurquoise, kMaroon, kLightSteelBlue,
|
||||
kPowderBlue, kFirebrick, kDarkGoldenrod, kMediumOrchid, kRosyBrown, kDarkKhaki,
|
||||
kGray, kSilver, kMediumVioletRed, kIndianRed, kPeru, kChocolate, kTan, kLightGray,
|
||||
kThistle, kOrchid, kGoldenrod, kPaleVioletRed, kCrimson, kGainsboro, kPlum, kBurlywood,
|
||||
kLightCyan, kLavender, kDarkSalmon, kViolet, kPaleGoldenrod, kLightCoral, kKhaki,
|
||||
kAliceBlue, kHoneydew, kAzure, kSandyBrown, kWheat, kBeige, kWhiteSmoke, kMintCream,
|
||||
kGhostWhite, kSalmon, kAntiqueWhite, kLinen, kLightGoldenrod, kOldLace, kRed,
|
||||
kFuchsia, kMagenta, kDeepPink, kOrangeRed, kTomato, kHotPink, kCoral, kDarkOrange,
|
||||
kLightSalmon, kOrange, kLightPink, kPink, kGold, kPeachPuff, kNavajoWhite, kMoccasin,
|
||||
kBisque, kMistyRose, kBlanchedAlmond, kPapayaWhip, kLavenderBlush, kSeashell,
|
||||
kCornsilk, kLemonChiffon, kFloralWhite, kSnow, kYellow, kLightYellow, kIvory, kWhite,
|
||||
kNvidia
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static constexpr int kNumHexColors = 146;
|
||||
static constexpr std::array<uint32_t, kNumHexColors> kHexColors =
|
||||
{
|
||||
{
|
||||
0x000000, 0x000080, 0x00008B, 0x0000CD, 0x0000FF, 0x006400, 0x008000,
|
||||
0x008080, 0x008B8B, 0x00BFFF, 0x00CED1, 0x00FA9A, 0x00FF00, 0x00FF00,
|
||||
0x00FF7F, 0x00FFFF, 0x00FFFF, 0x191970, 0x1E90FF, 0x20B2AA, 0x228B22,
|
||||
0x2E8B57, 0x2F4F4F, 0x32CD32, 0x3CB371, 0x40E0D0, 0x4169E1, 0x4682B4,
|
||||
0x483D8B, 0x48D1CC, 0x4B0082, 0x556B2F, 0x5F9EA0, 0x6495ED, 0x663399,
|
||||
0x66CDAA, 0x696969, 0x6A5ACD, 0x6B8E23, 0x708090, 0x778899, 0x7B68EE,
|
||||
0x7CFC00, 0x7F0000, 0x7F007F, 0x7FFF00, 0x7FFFD4, 0x808000, 0x808080,
|
||||
0x87CEEB, 0x87CEFA, 0x8A2BE2, 0x8B0000, 0x8B008B, 0x8B4513, 0x8FBC8F,
|
||||
0x90EE90, 0x9370DB, 0x9400D3, 0x98FB98, 0x9932CC, 0x9ACD32, 0xA020F0,
|
||||
0xA0522D, 0xA52A2A, 0xA9A9A9, 0xADD8E6, 0xADFF2F, 0xAFEEEE, 0xB03060,
|
||||
0xB0C4DE, 0xB0E0E6, 0xB22222, 0xB8860B, 0xBA55D3, 0xBC8F8F, 0xBDB76B,
|
||||
0xBEBEBE, 0xC0C0C0, 0xC71585, 0xCD5C5C, 0xCD853F, 0xD2691E, 0xD2B48C,
|
||||
0xD3D3D3, 0xD8BFD8, 0xDA70D6, 0xDAA520, 0xDB7093, 0xDC143C, 0xDCDCDC,
|
||||
0xDDA0DD, 0xDEB887, 0xE0FFFF, 0xE6E6FA, 0xE9967A, 0xEE82EE, 0xEEE8AA,
|
||||
0xF08080, 0xF0E68C, 0xF0F8FF, 0xF0FFF0, 0xF0FFFF, 0xF4A460, 0xF5DEB3,
|
||||
0xF5F5DC, 0xF5F5F5, 0xF5FFFA, 0xF8F8FF, 0xFA8072, 0xFAEBD7, 0xFAF0E6,
|
||||
0xFAFAD2, 0xFDF5E6, 0xFF0000, 0xFF00FF, 0xFF00FF, 0xFF1493, 0xFF4500,
|
||||
0xFF6347, 0xFF69B4, 0xFF7F50, 0xFF8C00, 0xFFA07A, 0xFFA500, 0xFFB6C1,
|
||||
0xFFC0CB, 0xFFD700, 0xFFDAB9, 0xFFDEAD, 0xFFE4B5, 0xFFE4C4, 0xFFE4E1,
|
||||
0xFFEBCD, 0xFFEFD5, 0xFFF0F5, 0xFFF5EE, 0xFFF8DC, 0xFFFACD, 0xFFFAF0,
|
||||
0xFFFAFA, 0xFFFF00, 0xFFFFE0, 0xFFFFF0, 0xFFFFFF, 0x76B900
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
constexpr size_t static_strlen(const char *str)
|
||||
{
|
||||
return *str == '\0' ? 0 : static_strlen(str + 1) + 1;
|
||||
}
|
||||
|
||||
constexpr uint8_t static_checksum8(const char *bfr)
|
||||
{
|
||||
unsigned int chk = 0;
|
||||
size_t len = static_strlen(bfr);
|
||||
for (; len; len--, bfr++) { chk += static_cast<unsigned int>(*bfr); }
|
||||
return static_cast<uint8_t>(chk);
|
||||
}
|
||||
|
||||
constexpr char *static_strrnchr(const char *str, const char c, int n)
|
||||
{
|
||||
size_t len = static_strlen(str);
|
||||
char *p = const_cast<char *>(str) + len - 1;
|
||||
for (; n; n--, p--, len--)
|
||||
{
|
||||
for (; len; p--, len--)
|
||||
{
|
||||
if (*p == c) { break; }
|
||||
}
|
||||
if (!len) { return nullptr; }
|
||||
if (n == 1) { return p; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline uint32_t static_color(const uint8_t COLOR, const int RANK,
|
||||
const char *FILE)
|
||||
{
|
||||
constexpr auto kMpiColorShift = 1;
|
||||
const auto rank_shift = kMpiColorShift * RANK;
|
||||
if (COLOR > 0) { return kHexColors[COLOR + rank_shift]; }
|
||||
const auto file_color = static_checksum8(FILE);
|
||||
return kHexColors[(file_color + rank_shift) % kNumHexColors];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers to generate unique variable names
|
||||
#define NVTX_FLF __FILE__, __LINE__, __FUNCTION__
|
||||
#define NVTX_PRIVATE_NAME(prefix) NVTX_PRIVATE_CONCAT(prefix, __LINE__)
|
||||
#define NVTX_PRIVATE_CONCAT(a, b) NVTX_PRIVATE_CONCAT2(a, b)
|
||||
#define NVTX_PRIVATE_CONCAT2(a, b) a##b
|
||||
|
||||
#ifndef NVTX_COLOR
|
||||
#define NVTX_COLOR ::gpu::nvtx::kBlack
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct Debug
|
||||
{
|
||||
const bool debug = false, end = true;
|
||||
|
||||
inline Debug() = default;
|
||||
|
||||
inline Debug(const int RANK, const char *FILE, const int LINE,
|
||||
const char *FUNC, uint8_t COLOR, bool ini = true,
|
||||
bool END = true): debug(true), end(END)
|
||||
{
|
||||
const char *base = static_strrnchr(FILE, '/', 2);
|
||||
const char *file = base ? base + 1 : FILE;
|
||||
const uint32_t rgb = static_color(COLOR, RANK, FILE);
|
||||
const uint8_t r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF,
|
||||
b = rgb & 0xFF;
|
||||
std::cout << "\033[38;2;";
|
||||
std::cout << std::to_string(r) << ";";
|
||||
std::cout << std::to_string(g) << ";";
|
||||
std::cout << std::to_string(b) << "m";
|
||||
if (ini)
|
||||
{
|
||||
std::cout << RANK << std::setw(64) << file << ":";
|
||||
std::cout << "\033[2m" << std::setw(4) << std::left << LINE
|
||||
<< "\033[22m: ";
|
||||
if (FUNC) { std::cout << "[" << FUNC << "] "; }
|
||||
}
|
||||
std::cout << std::right << "\033[1m";
|
||||
}
|
||||
|
||||
inline ~Debug()
|
||||
{
|
||||
if (debug) { std::cout << "\033[m" << (end ? "\n" : "") << std::flush; }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator<<(const T &arg) const noexcept
|
||||
{
|
||||
if (debug) { std::cout << arg; }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator()(const T &arg) const noexcept
|
||||
{
|
||||
if (debug) { this->operator<<(arg); }
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void operator()(const char *fmt, Args &&...args) const noexcept
|
||||
{
|
||||
if (debug) { std::cout << fmt::format(fmt, std::forward<Args>(args)...); }
|
||||
}
|
||||
|
||||
inline void operator()() const noexcept {}
|
||||
|
||||
static Debug Set(const char *FILE, const int LINE, const char *FUNC,
|
||||
uint8_t COLOR, bool INI = true, bool END = true)
|
||||
{
|
||||
static int mpi_rank = 0, dbg_mpi_rank = 0;
|
||||
static bool env_mpi = false, env_dbg = false;
|
||||
if (static bool ini = false; !std::exchange(ini, true))
|
||||
{
|
||||
env_dbg = (getenv("MFEM_DEBUG") != nullptr);
|
||||
env_mpi = getenv("MFEM_DEBUG_MPI") != nullptr;
|
||||
// int mpi_flag = 0;
|
||||
// MPI_Initialized(&mpi_flag);
|
||||
// if (mpi_flag) { MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); }
|
||||
dbg_mpi_rank = atoi(env_mpi ? getenv("MFEM_DEBUG_MPI") : "0");
|
||||
}
|
||||
const bool debug = (env_dbg && (!env_mpi || (dbg_mpi_rank == mpi_rank)));
|
||||
return debug ? Debug(mpi_rank, FILE, LINE, FUNC, COLOR, INI, END)
|
||||
: Debug();
|
||||
}
|
||||
};
|
||||
|
||||
// Debug console traces, unnamed
|
||||
#define NVTX_DEBUG(...) \
|
||||
::gpu::nvtx::Debug::Set(NVTX_FLF, NVTX_COLOR).operator()(__VA_ARGS__)
|
||||
|
||||
#define NVTX_DEBUG_NO_INI(...) \
|
||||
::gpu::nvtx::Debug::Set(NVTX_FLF, NVTX_COLOR, false, true) \
|
||||
.operator()(__VA_ARGS__)
|
||||
|
||||
#define NVTX_DEBUG_APPEND(...) \
|
||||
::gpu::nvtx::Debug::Set(NVTX_FLF, NVTX_COLOR, false, false) \
|
||||
.operator()(__VA_ARGS__)
|
||||
|
||||
#define NVTX_DEBUG_NO_END(...) \
|
||||
::gpu::nvtx::Debug::Set(NVTX_FLF, NVTX_COLOR, true, false) \
|
||||
.operator()(__VA_ARGS__)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct Nvtx
|
||||
{
|
||||
const bool nvtx = false, enforce_kernel_sync = false;
|
||||
const char *base, *file;
|
||||
const uint32_t color = kBlack;
|
||||
mutable std::string ascii;
|
||||
mutable nvtxEventAttributes_t event;
|
||||
mutable bool pushed = false;
|
||||
|
||||
inline Nvtx() = default;
|
||||
|
||||
Nvtx(bool enforce_kernel_sync, const char *FILE, const int LINE,
|
||||
const char *FUNC, uint8_t COLOR):
|
||||
nvtx(true), enforce_kernel_sync(enforce_kernel_sync),
|
||||
base(static_strrnchr(FILE, '/', 2)), file(base ? base + 1 : FILE),
|
||||
color(COLOR), ascii(file), event({})
|
||||
{
|
||||
event.version = NVTX_VERSION;
|
||||
event.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
|
||||
event.colorType = NVTX_COLOR_ARGB;
|
||||
event.color = static_color(COLOR, 0, FILE);
|
||||
event.messageType = NVTX_MESSAGE_TYPE_ASCII;
|
||||
|
||||
ascii += ":";
|
||||
ascii += std::to_string(LINE);
|
||||
ascii += ":[";
|
||||
ascii += FUNC;
|
||||
ascii += "] ";
|
||||
|
||||
pushed = false;
|
||||
}
|
||||
|
||||
explicit Nvtx(const char *title, uint8_t color = kWheat,
|
||||
bool enforce_kernel_sync = true):
|
||||
nvtx(true), enforce_kernel_sync(enforce_kernel_sync), color(color),
|
||||
ascii(title), event({})
|
||||
{
|
||||
event.version = NVTX_VERSION;
|
||||
event.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
|
||||
event.colorType = NVTX_COLOR_ARGB;
|
||||
event.color = static_color(color, 0, "");
|
||||
event.messageType = NVTX_MESSAGE_TYPE_ASCII;
|
||||
event.message.ascii = ascii.c_str();
|
||||
nvtxRangePushEx(&event);
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
inline void operator()() const
|
||||
{
|
||||
if (!nvtx) { return; }
|
||||
event.message.ascii = ascii.c_str();
|
||||
assert(!pushed);
|
||||
nvtxRangePushEx(&event);
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator()(const T &arg) const
|
||||
{
|
||||
if (!nvtx) { return; }
|
||||
this->operator<<(arg);
|
||||
event.message.ascii = ascii.c_str();
|
||||
assert(!pushed);
|
||||
nvtxRangePushEx(&event);
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void operator()(fmt::format_string<Args...> fmt, Args &&...args) const
|
||||
{
|
||||
if (!nvtx) { return; }
|
||||
ascii += fmt::format(fmt, std::forward<Args>(args)...);
|
||||
event.message.ascii = ascii.c_str();
|
||||
assert(!pushed);
|
||||
nvtxRangePushEx(&event);
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void operator<<(const T &arg) const
|
||||
{
|
||||
if (nvtx) { ascii += arg; }
|
||||
}
|
||||
|
||||
inline ~Nvtx()
|
||||
{
|
||||
if (!nvtx) { return; }
|
||||
if (enforce_kernel_sync)
|
||||
{
|
||||
nvtxEventAttributes_t eks = {};
|
||||
eks.version = NVTX_VERSION;
|
||||
eks.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
|
||||
eks.category = 0; // user value
|
||||
eks.colorType = NVTX_COLOR_ARGB;
|
||||
eks.messageType = NVTX_MESSAGE_TYPE_ASCII;
|
||||
eks.message.ascii = "!"; // enforce kernel synchronization
|
||||
eks.color = kHexColors[kYellow];
|
||||
nvtxRangePushEx(&eks);
|
||||
cudaStreamSynchronize(nullptr);
|
||||
nvtxRangePop(/*eks*/);
|
||||
}
|
||||
assert(pushed);
|
||||
nvtxRangePop(/*event*/);
|
||||
}
|
||||
|
||||
using nvtx_ptr = std::unique_ptr<Nvtx>;
|
||||
using nvtx_stack_t = std::stack<nvtx_ptr>;
|
||||
|
||||
static nvtx_ptr Set(const char *FILE, const int LINE, const char *FUNC,
|
||||
uint8_t COLOR)
|
||||
{
|
||||
static bool nvtx = false, eks = false;
|
||||
if (static bool ini = false; !std::exchange(ini, true))
|
||||
{
|
||||
eks = getenv("MFEM_EKS") != nullptr;
|
||||
nvtx = getenv("MFEM_NVTX") != nullptr;
|
||||
Nvtx force_first_eks("Init EKS", kYellow, true);
|
||||
}
|
||||
return nvtx_ptr(nvtx ? new Nvtx(eks, FILE, LINE, FUNC, COLOR)
|
||||
: new Nvtx());
|
||||
}
|
||||
|
||||
static nvtx_stack_t &Stack()
|
||||
{
|
||||
auto nvtx_events = []() -> nvtx_stack_t &
|
||||
{
|
||||
static nvtx_stack_t events;
|
||||
return events;
|
||||
};
|
||||
static std::once_flag ready;
|
||||
// one touch to guarantee the object is ready
|
||||
std::call_once(ready, [&] { nvtx_events(); });
|
||||
return nvtx_events();
|
||||
}
|
||||
};
|
||||
|
||||
// Temporary object only alive for the current statement
|
||||
#define NVTX_(COLOR, ...) \
|
||||
NVTX_DEBUG(__VA_ARGS__); \
|
||||
std::unique_ptr<::gpu::nvtx::Nvtx> NVTX_PRIVATE_NAME(nvtx) = \
|
||||
::gpu::nvtx::Nvtx::Set(NVTX_FLF, COLOR); \
|
||||
NVTX_PRIVATE_NAME(nvtx)->operator()(__VA_ARGS__)
|
||||
|
||||
// Temporary object only alive for the current statement
|
||||
#define NVTX(...) NVTX_(NVTX_COLOR, __VA_ARGS__)
|
||||
|
||||
// Begin(with color)/End NVTX event traces
|
||||
#define NVTX_BEGIN_(COLOR, ...) \
|
||||
NVTX_DEBUG(__VA_ARGS__); \
|
||||
::gpu::nvtx::Nvtx::Stack().push(::gpu::nvtx::Nvtx::Set(NVTX_FLF, COLOR)); \
|
||||
::gpu::nvtx::Nvtx::Stack().top()->operator()(__VA_ARGS__)
|
||||
|
||||
// Begin/End NVTX event traces
|
||||
#define NVTX_BEGIN(...) NVTX_BEGIN_(NVTX_COLOR, __VA_ARGS__);
|
||||
|
||||
#define NVTX_END(...) \
|
||||
::gpu::nvtx::Nvtx::Stack().top().reset(); \
|
||||
::gpu::nvtx::Nvtx::Stack().pop()
|
||||
|
||||
#ifdef USE_CALIPER
|
||||
// CALIPER & NVTX marks
|
||||
#define NVTX_MARK_FUNCTION \
|
||||
NVTX(); \
|
||||
std::unique_ptr<cali::Function> __cali_ann##__func__; \
|
||||
__cali_ann##__func__ = std::make_unique<cali::Function>(__func__);
|
||||
|
||||
#define NVTX_MARK(...) \
|
||||
NVTX(__VA_ARGS__); \
|
||||
std::unique_ptr<cali::Function> __cali_ann##__func__; \
|
||||
__cali_ann##__func__ = std::make_unique<cali::Function>(__VA_ARGS__);
|
||||
|
||||
#define NVTX_MARK_FUNCTION_NAME(STR_NAME) \
|
||||
NVTX(STR_NAME); \
|
||||
std::unique_ptr<cali::Function> __cali_ann##__func__; \
|
||||
if (g_caliper) \
|
||||
{ \
|
||||
__cali_ann##__func__ = std::make_unique<cali::Function>(STR_NAME); \
|
||||
}
|
||||
|
||||
#define NVTX_MARK_BEGIN(...) \
|
||||
CALI_MARK_BEGIN(__VA_ARGS__); \
|
||||
NVTX_BEGIN(__VA_ARGS__);
|
||||
|
||||
#define NVTX_MARK_END(...) \
|
||||
NVTX_END(__VA_ARGS__); \
|
||||
CALI_MARK_END(__VA_ARGS__);
|
||||
#else
|
||||
#define NVTX_MARK_FUNCTION NVTX()
|
||||
#define NVTX_MARK(...) NVTX(__VA_ARGS__)
|
||||
#define NVTX_MARK_FUNCTION_NAME(...) NVTX(__VA_ARGS__)
|
||||
#define NVTX_MARK_BEGIN(...) NVTX_BEGIN(__VA_ARGS__)
|
||||
#define NVTX_MARK_END(...) NVTX_END(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
} // namespace gpu::nvtx
|
||||
|
||||
// Debug console traces, unnamed
|
||||
#if 1
|
||||
#define dbg(...) NVTX_DEBUG(__VA_ARGS__)
|
||||
#define dbl(...) NVTX_DEBUG_NO_END(__VA_ARGS__)
|
||||
#define dba(...) NVTX_DEBUG_APPEND(__VA_ARGS__)
|
||||
#define dbc(...) NVTX_DEBUG_NO_INI(__VA_ARGS__)
|
||||
#else
|
||||
#define dbg(...)
|
||||
#define dbl(...) (void)0
|
||||
#define dba(...)
|
||||
#define dbc(...)
|
||||
#endif
|
||||
|
||||
inline bool ClearScreen()
|
||||
{
|
||||
dbg("\x1B[2J\x1B[3J\x1B[H");
|
||||
return true;
|
||||
}
|
||||
@@ -1,490 +0,0 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "incompressible_navier_solver.hpp"
|
||||
|
||||
#define NVTX_COLOR ::gpu::nvtx::kCyan
|
||||
#include "incompressible_navier_nvtx.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using namespace incompressible_navier;
|
||||
|
||||
IncompressibleNavierSolver::IncompressibleNavierSolver(ParMesh *mesh,
|
||||
int velorder, int porder,
|
||||
int torder,
|
||||
real_t kin_vis):
|
||||
pmesh(mesh), velorder(velorder), porder(porder), torder(torder),
|
||||
kin_vis(kin_vis), gll_rules(0, Quadrature1D::GaussLobatto),
|
||||
vfec(new H1_FECollection(velorder, pmesh->Dimension())),
|
||||
psifec(new H1_FECollection(porder)), pfec(new H1_FECollection(porder)),
|
||||
vfes(new ParFiniteElementSpace(pmesh, vfec, pmesh->Dimension())),
|
||||
psifes(new ParFiniteElementSpace(pmesh, pfec)),
|
||||
pfes(new ParFiniteElementSpace(pmesh, pfec)),
|
||||
|
||||
velGF(torder + 1, nullptr), pGF(torder + 1, nullptr)
|
||||
{
|
||||
NVTX();
|
||||
// Check if fully periodic mesh
|
||||
if (!(pmesh->bdr_attributes.Size() == 0))
|
||||
{
|
||||
vel_ess_attr.SetSize(pmesh->bdr_attributes.Max());
|
||||
vel_ess_attr = 0;
|
||||
|
||||
pres_ess_attr.SetSize(pmesh->bdr_attributes.Max());
|
||||
pres_ess_attr = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < torder + 1; i++)
|
||||
{
|
||||
velGF[i] = new ParGridFunction(vfes);
|
||||
*velGF[i] = 0.0;
|
||||
pGF[i] = new ParGridFunction(pfes);
|
||||
*pGF[i] = 0.0;
|
||||
}
|
||||
|
||||
psiGF.SetSpace(psifes);
|
||||
DvGF.SetSpace(vfes);
|
||||
divVelGF.SetSpace(pfes);
|
||||
pRHS.SetSpace(pfes);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Setup(real_t dt)
|
||||
{
|
||||
if (verbose && pmesh->GetMyRank() == 0)
|
||||
{
|
||||
mfem::out << "Setup" << std::endl;
|
||||
if (partial_assembly)
|
||||
{
|
||||
mfem::out << "Using Partial Assembly" << std::endl;
|
||||
}
|
||||
else { mfem::out << "Using Full Assembly" << std::endl; }
|
||||
}
|
||||
|
||||
this->Setup_velocity(dt);
|
||||
|
||||
this->Setup_auxiliary(dt);
|
||||
|
||||
this->Setup_pressure(dt);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Setup_velocity(real_t dt)
|
||||
{
|
||||
// GLL integration rule (Numerical Integration)
|
||||
const IntegrationRule &ir_ni =
|
||||
gll_rules.Get(vfes->GetFE(0)->GetGeomType(), 2 * velorder - 1);
|
||||
|
||||
vfes->GetEssentialTrueDofs(vel_ess_attr, vel_ess_tdof);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Setup of coefficient for mass term of Eq(13)
|
||||
dtCoeff = new ConstantCoefficient(1.0 / dt);
|
||||
auto *vmass_blfi = new VectorMassIntegrator(*dtCoeff);
|
||||
|
||||
// Setup of coefficient for stiffness term of Eq(13)
|
||||
kinvisCoeff = new ConstantCoefficient(kin_vis);
|
||||
auto *vdiff_blfi = new VectorDiffusionIntegrator(*kinvisCoeff);
|
||||
|
||||
// setup of Bilinear form of Eq(13)
|
||||
velBForm = new ParBilinearForm(vfes);
|
||||
if (numerical_integ)
|
||||
{
|
||||
vmass_blfi->SetIntRule(&ir_ni);
|
||||
vdiff_blfi->SetIntRule(&ir_ni);
|
||||
}
|
||||
velBForm->AddDomainIntegrator(vmass_blfi);
|
||||
velBForm->AddDomainIntegrator(vdiff_blfi);
|
||||
if (partial_assembly) { velBForm->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
|
||||
velBForm->Assemble();
|
||||
velBForm->FormSystemMatrix(vel_ess_tdof, vOp);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Setup of coefficient for Eq(18)
|
||||
pUnitVectorCoeff = new UnitVectorGridFunctionCoeff(pmesh->Dimension());
|
||||
auto *pvel_lfi = new VectorDomainLFGradIntegrator(*pUnitVectorCoeff);
|
||||
|
||||
// Setup of coefficient for Eq(20)
|
||||
nonlinTermCoeff = new NonLinTermVectorGridFunctionCoeff(pmesh->Dimension());
|
||||
auto *p_nonlintermlfi = new VectorDomainLFIntegrator(*nonlinTermCoeff);
|
||||
|
||||
// Setup of coefficient for Eq(21)
|
||||
prevVelLoadCoeff = new PrevVelVectorGridFunctionCoeff(pmesh->Dimension());
|
||||
auto *prevVelLoadLFi = new VectorDomainLFIntegrator(*prevVelLoadCoeff);
|
||||
|
||||
// Setup of linear form of Eq(13)
|
||||
velLForm = new ParLinearForm(vfes);
|
||||
if (numerical_integ)
|
||||
{
|
||||
prevVelLoadLFi->SetIntRule(&ir_ni);
|
||||
pvel_lfi->SetIntRule(&ir_ni);
|
||||
p_nonlintermlfi->SetIntRule(&ir_ni);
|
||||
}
|
||||
velLForm->AddDomainIntegrator(prevVelLoadLFi);
|
||||
velLForm->AddDomainIntegrator(pvel_lfi);
|
||||
velLForm->AddDomainIntegrator(p_nonlintermlfi);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
if (partial_assembly)
|
||||
{
|
||||
Vector diag_pa(vfes->GetTrueVSize());
|
||||
velBForm->AssembleDiagonal(diag_pa);
|
||||
velInvPC = new OperatorJacobiSmoother(diag_pa, vel_ess_tdof);
|
||||
}
|
||||
else
|
||||
{
|
||||
velInvPC = new HypreSmoother(*vOp.As<HypreParMatrix>());
|
||||
dynamic_cast<HypreSmoother *>(velInvPC)->SetType(HypreSmoother::Jacobi,
|
||||
1);
|
||||
}
|
||||
|
||||
velInv = new CGSolver(vfes->GetComm());
|
||||
velInv->iterative_mode = true;
|
||||
velInv->SetOperator(*vOp);
|
||||
velInv->SetPreconditioner(*velInvPC);
|
||||
velInv->SetPrintLevel(pl_velsolve);
|
||||
velInv->SetRelTol(rtol_velsolve);
|
||||
velInv->SetAbsTol(0.0);
|
||||
velInv->SetMaxIter(1200);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Setup_auxiliary(real_t dt)
|
||||
{
|
||||
// GLL integration rule (Numerical Integration)
|
||||
const IntegrationRule &ir_ni =
|
||||
gll_rules.Get(vfes->GetFE(0)->GetGeomType(), 2 * velorder - 1);
|
||||
Array<int> empty;
|
||||
|
||||
// setup of Bilinear form of Eq(14)
|
||||
psiBForm = new ParBilinearForm(psifes);
|
||||
auto *psidiff_blfi = new DiffusionIntegrator;
|
||||
|
||||
if (numerical_integ) { psidiff_blfi->SetIntRule(&ir_ni); }
|
||||
psiBForm->AddDomainIntegrator(psidiff_blfi);
|
||||
if (partial_assembly) { psiBForm->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
|
||||
psiBForm->Assemble();
|
||||
psiBForm->FormSystemMatrix(empty, psiOp);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Setup of coefficient for linear form in Eq(14)
|
||||
DvelCoeff = new VectorGridFunctionCoefficient;
|
||||
auto *Dvel_lfi = new DomainLFGradIntegrator(*DvelCoeff);
|
||||
|
||||
// Setup of linear form of Eq(14)
|
||||
psiLForm = new ParLinearForm(psifes);
|
||||
|
||||
if (numerical_integ) { Dvel_lfi->SetIntRule(&ir_ni); }
|
||||
psiLForm->AddDomainIntegrator(Dvel_lfi);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
if (partial_assembly)
|
||||
{
|
||||
int psifes_truevsize = psifes->GetTrueVSize();
|
||||
mfem::Vector psin(psifes_truevsize);
|
||||
psin = 0.0;
|
||||
mfem::Vector respsi(psifes_truevsize);
|
||||
respsi = 0.0;
|
||||
|
||||
lor = new ParLORDiscretization(*psiBForm, empty);
|
||||
psiInvPC = new HypreBoomerAMG(lor->GetAssembledMatrix());
|
||||
psiInvPC->SetPrintLevel(0);
|
||||
psiInvPC->Mult(respsi, psin);
|
||||
SpInvOrthoPC = new OrthoSolver(psifes->GetComm());
|
||||
SpInvOrthoPC->SetSolver(*psiInvPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
psiInvPC = new HypreBoomerAMG(*psiOp.As<HypreParMatrix>());
|
||||
psiInvPC->SetPrintLevel(0);
|
||||
SpInvOrthoPC = new OrthoSolver(psifes->GetComm());
|
||||
SpInvOrthoPC->SetSolver(*psiInvPC);
|
||||
}
|
||||
|
||||
psiInv = new CGSolver(psifes->GetComm());
|
||||
psiInv->iterative_mode = true;
|
||||
psiInv->SetOperator(*psiOp);
|
||||
psiInv->SetPreconditioner(*SpInvOrthoPC);
|
||||
psiInv->SetPrintLevel(pl_psisolve);
|
||||
psiInv->SetRelTol(rtol_psisolve);
|
||||
psiInv->SetAbsTol(0.0);
|
||||
psiInv->SetMaxIter(1000);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Setup_pressure(real_t dt)
|
||||
{
|
||||
// GLL integration rule (Numerical Integration)
|
||||
const IntegrationRule &ir_ni =
|
||||
gll_rules.Get(vfes->GetFE(0)->GetGeomType(), 2 * velorder - 1);
|
||||
Array<int> empty;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// setup of Bilinear form of Eq(15)
|
||||
pBForm = new ParBilinearForm(pfes);
|
||||
auto *pmass_blfi = new MassIntegrator;
|
||||
|
||||
if (numerical_integ) { pmass_blfi->SetIntRule(&ir_ni); }
|
||||
pBForm->AddDomainIntegrator(pmass_blfi);
|
||||
if (partial_assembly) { pBForm->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
|
||||
pBForm->Assemble();
|
||||
pBForm->FormSystemMatrix(empty, pOp);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Setup of divergence of velocity coefficient for linear form in Eq(15)
|
||||
divVelCoeff = new DivergenceGridFunctionCoefficient(velGF[0]);
|
||||
|
||||
// Setup of coefficient for linear form in Eq(14)
|
||||
pRHSCoeff = new GridFunctionCoefficient(&pRHS);
|
||||
auto *p_lfi = new DomainLFIntegrator(*pRHSCoeff);
|
||||
|
||||
// Setup of linear form of Eq(15)
|
||||
pLForm = new ParLinearForm(pfes);
|
||||
if (numerical_integ) { p_lfi->SetIntRule(&ir_ni); }
|
||||
pLForm->AddDomainIntegrator(p_lfi);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
if (partial_assembly)
|
||||
{
|
||||
Vector diag_pa(pfes->GetTrueVSize());
|
||||
pBForm->AssembleDiagonal(diag_pa);
|
||||
pInvPC = new OperatorJacobiSmoother(diag_pa, empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
pInvPC = new HypreSmoother(*pOp.As<HypreParMatrix>());
|
||||
dynamic_cast<HypreSmoother *>(pInvPC)->SetType(HypreSmoother::Jacobi, 1);
|
||||
}
|
||||
|
||||
pInv = new CGSolver(pfes->GetComm());
|
||||
pInv->iterative_mode = true;
|
||||
pInv->SetOperator(*pOp);
|
||||
pInv->SetPreconditioner(*pInvPC);
|
||||
pInv->SetPrintLevel(pl_psolve);
|
||||
pInv->SetRelTol(rtol_psolve);
|
||||
pInv->SetAbsTol(0.0);
|
||||
pInv->SetMaxIter(1000);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::UpdateTimestepHistory(real_t dt) {}
|
||||
|
||||
void IncompressibleNavierSolver::Step(real_t &time, real_t dt, int current_step,
|
||||
const bool vis_step)
|
||||
{
|
||||
this->Step_velocity(time, dt, current_step);
|
||||
|
||||
this->Step_auxiliary(time, dt, current_step);
|
||||
|
||||
this->Step_pressure(time, dt, current_step);
|
||||
|
||||
*velGF[1] = *velGF[0];
|
||||
*pGF[1] = *pGF[0];
|
||||
|
||||
if (vis_step)
|
||||
{
|
||||
mfem::out << "It: " << iter << " | Iter_U: " << iter_vsolve
|
||||
<< " | Iter_Psi: " << iter_psisolve
|
||||
<< " | Iter_P: " << iter_psolve << "\n";
|
||||
mfem::out << "It: " << iter << " | Resid_U: " << res_vsolve
|
||||
<< " | Resid_Psi: " << res_psisolve
|
||||
<< " | Resid_P: " << res_psisolve << "\n";
|
||||
}
|
||||
|
||||
time += dt;
|
||||
iter++;
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Step_velocity(real_t &time, real_t dt,
|
||||
int current_step)
|
||||
{
|
||||
for (auto &vel_dbc : vel_dbcs)
|
||||
{
|
||||
velGF[0]->ProjectBdrCoefficient(*vel_dbc.coeff, vel_dbc.attr);
|
||||
velGF[1]->ProjectBdrCoefficient(*vel_dbc.coeff, vel_dbc.attr);
|
||||
}
|
||||
|
||||
// Update state in coefficient for Eq(18)
|
||||
pUnitVectorCoeff->SetGridFunction(pGF[1]);
|
||||
|
||||
// Update state in coefficient for Eq(20)
|
||||
nonlinTermCoeff->SetGridFunction(velGF[1]);
|
||||
|
||||
// Update state in coefficient for Eq(21)
|
||||
prevVelLoadCoeff->SetGridFunction(velGF[1], dt);
|
||||
|
||||
velLForm->Assemble();
|
||||
velLForm->ParallelAssemble(velLF);
|
||||
|
||||
Vector X1, B1;
|
||||
|
||||
if (partial_assembly)
|
||||
{
|
||||
auto *vpC = vOp.As<ConstrainedOperator>();
|
||||
EliminateRHS(*velBForm, *vpC, vel_ess_tdof, *velGF[0], velLF, X1, B1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
velBForm->FormLinearSystem(vel_ess_tdof, *velGF[0], velLF, vOp, X1, B1,
|
||||
1);
|
||||
}
|
||||
|
||||
velInv->Mult(B1, X1);
|
||||
iter_vsolve = velInv->GetNumIterations();
|
||||
res_vsolve = velInv->GetFinalNorm();
|
||||
velBForm->RecoverFEMSolution(X1, velLF, *velGF[0]);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Step_auxiliary(real_t &time, real_t dt,
|
||||
int current_step)
|
||||
{
|
||||
// Compute new increment GF for LF of Eq(14) and update state in coefficient
|
||||
subtract(1.0 / dt, *velGF[0], *velGF[1], DvGF);
|
||||
DvelCoeff->SetGridFunction(&DvGF);
|
||||
|
||||
psiLForm->Assemble();
|
||||
psiLForm->ParallelAssemble(psiLF);
|
||||
|
||||
Vector X2, B2;
|
||||
Array<int> empty;
|
||||
if (partial_assembly)
|
||||
{
|
||||
auto *psipC = psiOp.As<ConstrainedOperator>();
|
||||
EliminateRHS(*psiBForm, *psipC, empty, psiGF, psiLF, X2, B2, 1);
|
||||
}
|
||||
else { psiBForm->FormLinearSystem(empty, psiGF, psiLF, psiOp, X2, B2, 1); }
|
||||
|
||||
psiInv->Mult(B2, X2);
|
||||
iter_psisolve = psiInv->GetNumIterations();
|
||||
res_psisolve = psiInv->GetFinalNorm();
|
||||
psiBForm->RecoverFEMSolution(X2, psiLF, psiGF);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::Step_pressure(real_t &time, real_t dt,
|
||||
int current_step)
|
||||
{
|
||||
Array<int> empty;
|
||||
|
||||
// Compute new GF for LF of Eq(15) and update state in coefficient
|
||||
divVelCoeff->SetGridFunction(velGF[0]);
|
||||
divVelGF.ProjectCoefficient(*divVelCoeff);
|
||||
|
||||
add(*pGF[1], psiGF, pRHS);
|
||||
add(pRHS, -1.0 * kin_vis, divVelGF, pRHS);
|
||||
pRHSCoeff->SetGridFunction(&pRHS);
|
||||
|
||||
pLForm->Assemble();
|
||||
pLForm->ParallelAssemble(pLF);
|
||||
|
||||
Vector X3, B3;
|
||||
|
||||
if (partial_assembly)
|
||||
{
|
||||
auto *ppC = pOp.As<ConstrainedOperator>();
|
||||
EliminateRHS(*pBForm, *ppC, empty, *pGF[0], pLF, X3, B3, 1);
|
||||
}
|
||||
else { pBForm->FormLinearSystem(empty, *pGF[0], pLF, pOp, X3, B3, 1); }
|
||||
|
||||
pInv->Mult(B3, X3);
|
||||
iter_psolve = pInv->GetNumIterations();
|
||||
res_psisolve = pInv->GetFinalNorm();
|
||||
pBForm->RecoverFEMSolution(X3, pLF, *pGF[0]);
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::EliminateRHS(Operator &A,
|
||||
ConstrainedOperator &constrainedA,
|
||||
const Array<int> &ess_tdof_list,
|
||||
Vector &x, Vector &b, Vector &X,
|
||||
Vector &B, int copy_interior)
|
||||
{
|
||||
const Operator *Po = A.GetOutputProlongation();
|
||||
const Operator *Pi = A.GetProlongation();
|
||||
const Operator *Ri = A.GetRestriction();
|
||||
A.InitTVectors(Po, Ri, Pi, x, b, X, B);
|
||||
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
|
||||
constrainedA.EliminateRHS(X, B);
|
||||
}
|
||||
|
||||
real_t IncompressibleNavierSolver::ComputeCFL(ParGridFunction &u, real_t dt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::AddVelDirichletBC(VectorCoefficient *coeff,
|
||||
Array<int> &attr)
|
||||
{
|
||||
vel_dbcs.emplace_back(attr, coeff);
|
||||
|
||||
if (verbose && pmesh->GetMyRank() == 0)
|
||||
{
|
||||
mfem::out << "Adding Velocity Dirichlet BC to attributes ";
|
||||
for (int i = 0; i < attr.Size(); ++i)
|
||||
{
|
||||
if (attr[i] == 1) { mfem::out << i << " "; }
|
||||
}
|
||||
mfem::out << std::endl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < attr.Size(); ++i)
|
||||
{
|
||||
MFEM_ASSERT((vel_ess_attr[i] && attr[i]) == 0,
|
||||
"Duplicate boundary definition deteceted.");
|
||||
if (attr[i] == 1) { vel_ess_attr[i] = 1; }
|
||||
}
|
||||
}
|
||||
|
||||
void IncompressibleNavierSolver::AddVelDirichletBC(VecFuncT *f,
|
||||
Array<int> &attr)
|
||||
{
|
||||
AddVelDirichletBC(new VectorFunctionCoefficient(pmesh->Dimension(), f),
|
||||
attr);
|
||||
}
|
||||
|
||||
IncompressibleNavierSolver::~IncompressibleNavierSolver()
|
||||
{
|
||||
delete velBForm;
|
||||
delete psiBForm;
|
||||
delete pBForm;
|
||||
|
||||
delete kinvisCoeff;
|
||||
delete dtCoeff;
|
||||
|
||||
for (int i = 0; i < torder + 1; i++)
|
||||
{
|
||||
delete velGF[i];
|
||||
delete pGF[i];
|
||||
}
|
||||
|
||||
delete DvelCoeff;
|
||||
delete divVelCoeff;
|
||||
delete pRHSCoeff;
|
||||
delete pUnitVectorCoeff;
|
||||
|
||||
delete velInv;
|
||||
delete velInvPC;
|
||||
delete psiInv;
|
||||
delete SpInvOrthoPC;
|
||||
delete psiInvPC;
|
||||
delete lor;
|
||||
delete pInv;
|
||||
delete pInvPC;
|
||||
|
||||
delete vfec;
|
||||
delete psifec;
|
||||
delete pfec;
|
||||
delete vfes;
|
||||
delete psifes;
|
||||
delete pfes;
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define INCOMP_NAVIER_VERSION 0.1
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
namespace mfem::incompressible_navier
|
||||
{
|
||||
|
||||
using VecFuncT = void(const Vector &x, real_t t, Vector &u);
|
||||
using ScalarFuncT = real_t(const Vector &x, real_t t);
|
||||
|
||||
// Coefficient which computed contribution of Eq(18)
|
||||
class UnitVectorGridFunctionCoeff : public VectorCoefficient
|
||||
{
|
||||
public:
|
||||
UnitVectorGridFunctionCoeff(int dim): VectorCoefficient(dim * dim) {}
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override
|
||||
{
|
||||
real_t coeffVal = gridfunc_->GetValue(T, ip);
|
||||
|
||||
V.SetSize(vdim);
|
||||
V = 0.0; // FIXME
|
||||
V[0] = coeffVal;
|
||||
V[3] = coeffVal;
|
||||
}
|
||||
|
||||
void SetGridFunction(GridFunction *gridfunc) { gridfunc_ = gridfunc; }
|
||||
|
||||
GridFunction *gridfunc_ = nullptr;
|
||||
};
|
||||
|
||||
// Coefficient which computed contribution of Eq(21)
|
||||
class PrevVelVectorGridFunctionCoeff : public VectorCoefficient
|
||||
{
|
||||
public:
|
||||
PrevVelVectorGridFunctionCoeff(int dim): VectorCoefficient(dim) {}
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{
|
||||
V.SetSize(vdim);
|
||||
gridFuncCoeff->Eval(V, T, ip);
|
||||
|
||||
V *= 1.0 / dt_;
|
||||
}
|
||||
|
||||
void SetGridFunction(GridFunction *gridfunc, real_t dt)
|
||||
{
|
||||
gridfunc_ = gridfunc;
|
||||
dt_ = dt;
|
||||
delete gridFuncCoeff;
|
||||
gridFuncCoeff = new VectorGridFunctionCoefficient(gridfunc);
|
||||
}
|
||||
|
||||
GridFunction *gridfunc_ = nullptr;
|
||||
VectorGridFunctionCoefficient *gridFuncCoeff = nullptr;
|
||||
real_t dt_;
|
||||
};
|
||||
|
||||
// Coefficient which computed contribution of Eq(20)
|
||||
class NonLinTermVectorGridFunctionCoeff : public VectorCoefficient
|
||||
{
|
||||
public:
|
||||
NonLinTermVectorGridFunctionCoeff(int dim): VectorCoefficient(dim) {}
|
||||
|
||||
void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{
|
||||
Vector val(vdim);
|
||||
Vector resultVal(vdim);
|
||||
DenseMatrix vecGrad;
|
||||
V.SetSize(vdim);
|
||||
gridFuncCoeff->Eval(val, T, ip);
|
||||
|
||||
gridfunc_->GetVectorGradient(T, vecGrad);
|
||||
|
||||
vecGrad.MultTranspose(val, V);
|
||||
|
||||
V *= -1.0;
|
||||
}
|
||||
|
||||
void SetGridFunction(ParGridFunction *gridfunc)
|
||||
{
|
||||
delete gridFuncCoeff;
|
||||
gridfunc_ = gridfunc;
|
||||
gridFuncCoeff = new VectorGridFunctionCoefficient(gridfunc);
|
||||
}
|
||||
|
||||
VectorGridFunctionCoefficient *gridFuncCoeff = nullptr;
|
||||
ParGridFunction *gridfunc_ = nullptr;
|
||||
};
|
||||
|
||||
/// Container for a Dirichlet boundary condition of the velocity field.
|
||||
class VelDirichletBC_T
|
||||
{
|
||||
public:
|
||||
VelDirichletBC_T(Array<int> attr, VectorCoefficient *coeff):
|
||||
attr(attr), coeff(coeff)
|
||||
{
|
||||
}
|
||||
|
||||
VelDirichletBC_T(VelDirichletBC_T &&obj)
|
||||
{
|
||||
// Deep copy the attribute array
|
||||
this->attr = obj.attr;
|
||||
|
||||
// Move the coefficient pointer
|
||||
this->coeff = obj.coeff;
|
||||
obj.coeff = nullptr;
|
||||
}
|
||||
|
||||
~VelDirichletBC_T() { delete coeff; }
|
||||
|
||||
Array<int> attr;
|
||||
VectorCoefficient *coeff;
|
||||
};
|
||||
|
||||
/// Transient incompressible Navier Stokes solver in a split scheme formulation.
|
||||
/**
|
||||
* This implementation of a transient incompressible Navier Stokes solver uses
|
||||
* the non-dimensionalized formulation. The coupled momentum and
|
||||
* incompressibility equations are decoupled using the split scheme described in
|
||||
* [1]. This leads to three solving steps.
|
||||
*
|
||||
*/
|
||||
class IncompressibleNavierSolver
|
||||
{
|
||||
public:
|
||||
/// Initialize data structures, set FE space order and kinematic viscosity.
|
||||
/**
|
||||
* The ParMesh @a mesh can be a linear or curved parallel mesh. The @a order
|
||||
* of the finite element spaces is
|
||||
*/
|
||||
IncompressibleNavierSolver(ParMesh *mesh, int velorder, int porder,
|
||||
int tOrder, real_t kin_vis);
|
||||
|
||||
/// Initialize forms, solvers and preconditioners.
|
||||
void Setup(real_t dt);
|
||||
|
||||
void Setup_velocity(real_t dt);
|
||||
|
||||
void Setup_auxiliary(real_t dt);
|
||||
|
||||
void Setup_pressure(real_t dt);
|
||||
|
||||
/// Compute solution at the next time step t+dt.
|
||||
/**
|
||||
* This method can
|
||||
*/
|
||||
void Step(real_t &time, real_t dt, int cur_step, bool vis_step);
|
||||
|
||||
void Step_velocity(real_t &time, real_t dt, int cur_step);
|
||||
|
||||
void Step_auxiliary(real_t &time, real_t dt, int cur_step);
|
||||
|
||||
void Step_pressure(real_t &time, real_t dt, int cur_step);
|
||||
|
||||
/// Return a pointer to the provisional velocity ParGridFunction.
|
||||
ParGridFunction *GetProvisionalVelocity() { return velGF[1]; }
|
||||
|
||||
/// Return a pointer to the current velocity ParGridFunction.
|
||||
ParGridFunction *GetCurrentVelocity() { return velGF[0]; }
|
||||
|
||||
/// Return a pointer to the current pressure ParGridFunction.
|
||||
ParGridFunction *GetCurrentPressure() { return pGF[0]; }
|
||||
|
||||
/// Return a pointer to the current pressure ParGridFunction.
|
||||
ParGridFunction *GetCurrentPsi() { return &psiGF; }
|
||||
|
||||
/// Add a Dirichlet boundary condition to the velocity field.
|
||||
void AddVelDirichletBC(VectorCoefficient *coeff, Array<int> &attr);
|
||||
|
||||
void AddVelDirichletBC(VecFuncT *f, Array<int> &attr);
|
||||
|
||||
/// Add a Dirichlet boundary condition to the pressure field.
|
||||
// void AddPresDirichletBC(Coefficient *coeff, Array<int> &attr);
|
||||
|
||||
// void AddPresDirichletBC(ScalarFuncT *f, Array<int> &attr);
|
||||
|
||||
/// Enable partial assembly for every operator.
|
||||
void EnablePA(bool pa) { partial_assembly = pa; }
|
||||
|
||||
/// Enable numerical integration rules. This means collocated quadrature at
|
||||
/// the nodal points.
|
||||
void EnableNI(bool ni) { numerical_integ = ni; }
|
||||
|
||||
/// Print timing summary of the solving routine.
|
||||
void PrintTimingData();
|
||||
|
||||
~IncompressibleNavierSolver();
|
||||
|
||||
/// Rotate entries in the time step and solution history arrays.
|
||||
void UpdateTimestepHistory(real_t dt);
|
||||
|
||||
/// Compute CFL
|
||||
real_t ComputeCFL(ParGridFunction &u, real_t dt);
|
||||
|
||||
protected:
|
||||
/// Eliminate essential BCs in an Operator and apply to RHS.
|
||||
void EliminateRHS(Operator &A, ConstrainedOperator &constrainedA,
|
||||
const Array<int> &ess_tdof_list, Vector &x, Vector &b,
|
||||
Vector &X, Vector &B, int copy_interior = 0);
|
||||
|
||||
/// Enable/disable debug output.
|
||||
bool debug = false;
|
||||
|
||||
/// Enable/disable verbose output.
|
||||
bool verbose = true;
|
||||
|
||||
/// Enable/disable partial assembly of forms.
|
||||
bool partial_assembly = false;
|
||||
|
||||
/// Enable/disable numerical integration rules of forms.
|
||||
bool numerical_integ = false;
|
||||
|
||||
/// The parallel mesh.
|
||||
ParMesh *pmesh = nullptr;
|
||||
|
||||
/// The order of the velocity and pressure space.
|
||||
const int velorder;
|
||||
const int porder;
|
||||
const int torder;
|
||||
|
||||
/// Kinematic viscosity (dimensionless).
|
||||
const real_t kin_vis;
|
||||
Coefficient *kinvisCoeff = nullptr;
|
||||
|
||||
Coefficient *dtCoeff = nullptr;
|
||||
|
||||
IntegrationRules gll_rules;
|
||||
|
||||
/// Velocity $H^1$ finite element collection.
|
||||
FiniteElementCollection *vfec = nullptr;
|
||||
|
||||
/// Psi $H^1$ finite element collection.
|
||||
FiniteElementCollection *psifec = nullptr;
|
||||
|
||||
/// Pressure $H^1$ finite element collection.
|
||||
FiniteElementCollection *pfec = nullptr;
|
||||
|
||||
/// Velocity $(H^1)^d$ finite element space.
|
||||
ParFiniteElementSpace *vfes = nullptr;
|
||||
|
||||
/// Psi $(H^1)^d$ finite element space.
|
||||
ParFiniteElementSpace *psifes = nullptr;
|
||||
|
||||
/// Pressure $H^1$ finite element space.
|
||||
ParFiniteElementSpace *pfes = nullptr;
|
||||
|
||||
ParBilinearForm *velBForm = nullptr; // vmass + vdiff
|
||||
ParBilinearForm *psiBForm = nullptr; // diffusion
|
||||
ParBilinearForm *pBForm = nullptr; // mass
|
||||
|
||||
ParLinearForm *velLForm = nullptr; // vLF + vLFGrad + vLF
|
||||
ParLinearForm *psiLForm = nullptr; // LFGrad
|
||||
ParLinearForm *pLForm = nullptr; // LF
|
||||
|
||||
// current (0) and provisional (1) velocity
|
||||
std::vector<ParGridFunction *> velGF;
|
||||
|
||||
// current (0) pressure ParGridFunction.
|
||||
std::vector<ParGridFunction *> pGF;
|
||||
ParGridFunction psiGF;
|
||||
|
||||
ParGridFunction DvGF, divVelGF, pRHS;
|
||||
VectorGridFunctionCoefficient *DvelCoeff = nullptr;
|
||||
DivergenceGridFunctionCoefficient *divVelCoeff = nullptr;
|
||||
GridFunctionCoefficient *pRHSCoeff = nullptr;
|
||||
UnitVectorGridFunctionCoeff *pUnitVectorCoeff = nullptr;
|
||||
NonLinTermVectorGridFunctionCoeff *nonlinTermCoeff = nullptr;
|
||||
PrevVelVectorGridFunctionCoeff *prevVelLoadCoeff = nullptr;
|
||||
|
||||
OperatorHandle vOp;
|
||||
OperatorHandle psiOp;
|
||||
OperatorHandle pOp;
|
||||
|
||||
Solver *velInvPC = nullptr;
|
||||
CGSolver *velInv = nullptr;
|
||||
|
||||
ParLORDiscretization *lor = nullptr;
|
||||
HypreBoomerAMG *psiInvPC = nullptr;
|
||||
OrthoSolver *SpInvOrthoPC = nullptr;
|
||||
CGSolver *psiInv = nullptr;
|
||||
|
||||
Solver *pInvPC = nullptr;
|
||||
CGSolver *pInv = nullptr;
|
||||
|
||||
Vector velLF, psiLF, pLF;
|
||||
|
||||
// All essential attributes.
|
||||
Array<int> vel_ess_attr;
|
||||
Array<int> pres_ess_attr;
|
||||
|
||||
// All essential true dofs.
|
||||
Array<int> vel_ess_tdof;
|
||||
Array<int> pres_ess_tdof;
|
||||
|
||||
// Bookkeeping for velocity dirichlet bcs.
|
||||
std::vector<VelDirichletBC_T> vel_dbcs;
|
||||
|
||||
// Print levels.
|
||||
int pl_psolve = 0;
|
||||
int pl_psisolve = 0;
|
||||
int pl_velsolve = 0;
|
||||
int pl_amg = 0;
|
||||
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
real_t rtol_psolve = 1e-12;
|
||||
real_t rtol_psisolve = 1e-12;
|
||||
real_t rtol_velsolve = 1e-12;
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
real_t rtol_psolve = 1e-9;
|
||||
real_t rtol_psisolve = 1e-5;
|
||||
real_t rtol_velsolve = 1e-7;
|
||||
#else
|
||||
#error "Only single and double precision are supported!"
|
||||
#endif
|
||||
|
||||
// Iteration counts.
|
||||
int iter = 1, iter_vsolve = 0, iter_psolve = 0, iter_psisolve = 0;
|
||||
|
||||
// Residuals.
|
||||
real_t res_vsolve = 0.0, res_psolve = 0.0, res_psisolve = 0.0;
|
||||
};
|
||||
|
||||
} // namespace mfem::incompressible_navier
|
||||
@@ -1,206 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#define NVTX_COLOR ::gpu::nvtx::kMagenta
|
||||
#include "incompressible_navier_nvtx.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int navier(int argc, char *argv[], double &u, double &p, double &Ψ);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
template <class T>
|
||||
std::enable_if_t<!std::numeric_limits<T>::is_integer, bool>
|
||||
AlmostEq(T x, T y, T tolerance = 100.0 * std::numeric_limits<T>::epsilon())
|
||||
{
|
||||
const T neg = std::abs(x - y);
|
||||
constexpr T min = std::numeric_limits<T>::min();
|
||||
constexpr T eps = std::numeric_limits<T>::epsilon();
|
||||
const T min_abs = std::min(std::abs(x), std::abs(y));
|
||||
if (std::abs(min_abs) == 0.0) { return neg < eps; }
|
||||
return (neg / (1.0 + std::max(min, min_abs))) < tolerance;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
using char_uptr = std::unique_ptr<char[]>;
|
||||
using args_ptr_t = std::vector<char_uptr>;
|
||||
using args_t = std::vector<char *>;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct Results
|
||||
{
|
||||
double u{}, p{}, Ψ {};
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
struct Test
|
||||
{
|
||||
static constexpr const char *binary = "incompNS_2Dtest ";
|
||||
static constexpr const char *common = "-no-vis -no-pv";
|
||||
const std::string options;
|
||||
const Results results;
|
||||
Test(const char *args, const Results &res):
|
||||
options(std::string(args) + " " + common), results(res)
|
||||
{
|
||||
dbg("options: {}", options.c_str());
|
||||
dbg("results: U={:.15e}, P={:.15e}, Ψ={:.15e}",
|
||||
results.u, results.p, results.Ψ);
|
||||
}
|
||||
std::string Command() const { return binary + options; }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#if 1 // dot product reduction (miniapps/navier/incompNS_2Dtest.cpp#L182)
|
||||
static const Test gold[] =
|
||||
{
|
||||
{
|
||||
"-nx 9 -ny 3 -sr 0",
|
||||
{ 3.056430866716070e-06, 1.504027632950462e-01, 7.132601171183242e-08 }
|
||||
},
|
||||
{
|
||||
"-nx 16 -ny 8 -sr 0",
|
||||
{ 1.409287729554512e-05, 5.718053801962010e-01, 1.904938419441012e-07 }
|
||||
},
|
||||
// {
|
||||
// "-nx 9 -ny 3 -sr 1",
|
||||
// { 1.23258426138828e-05, 5.18207619597952e-01, 1.956175418199867e-07 }
|
||||
// },
|
||||
// {
|
||||
// "-nx 9 -ny 3 -sr 2",
|
||||
// { 4.74381190869396e-05, 1.80653923294262e+00, 5.90778654333642e-07 }
|
||||
// },
|
||||
};
|
||||
#else // Norml2 reduction
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const Test runs[] =
|
||||
{
|
||||
{
|
||||
"-nx 9 -ny 3 -sr 0",
|
||||
{ 1.746844586767688e-03, 3.874421956807944e-01, 2.670296315544763e-04 }
|
||||
},
|
||||
// 1.748265101955712e-03, 3.878179512284757e-01, 2.670693013280680e-04 //
|
||||
// Release { "-nx 9 -ny 3 -sr 1",
|
||||
// { 1.232584261388279e-05, 5.182076195979519e-01, 1.956175418199866e-07 }
|
||||
// },
|
||||
// { "-nx 9 -ny 3 -sr 2",
|
||||
// { 4.743811908693962e-05, 1.806539232942622e+00, 5.907786543336419e-07 }
|
||||
// },
|
||||
};
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int NavierTest(const int k, const Test &run)
|
||||
{
|
||||
dbg();
|
||||
static args_ptr_t args_ptr;
|
||||
args_t args;
|
||||
|
||||
std::istringstream iss(run.Command());
|
||||
|
||||
auto add_arg = [&](std::string token) -> char_uptr
|
||||
{
|
||||
auto arg_ptr = std::make_unique<char[]>(token.size() + 1);
|
||||
std::memcpy(arg_ptr.get(), token.c_str(), token.size() + 1);
|
||||
arg_ptr[token.size()] = '\0';
|
||||
return arg_ptr;
|
||||
};
|
||||
|
||||
std::string token;
|
||||
while (iss >> token)
|
||||
{
|
||||
auto arg_ptr = add_arg(token);
|
||||
args.push_back(arg_ptr.get());
|
||||
args_ptr.emplace_back(std::move(arg_ptr));
|
||||
}
|
||||
args.push_back(nullptr);
|
||||
|
||||
auto launch = [&args, &run, &k]() -> int
|
||||
{
|
||||
// dbg("Launching test #{}: \x1B[33m{}\x1B[m", k, gold.Command().c_str());
|
||||
Results res{};
|
||||
navier(args.size() - 1, args.data(), res.u, res.p, res.Ψ);
|
||||
// dbg("Results: U={:.15e}, P={:.15e}, Ψ={:.15e}", res.u, res.p, res.Ψ);
|
||||
|
||||
const bool u = AlmostEq(res.u, run.results.u);
|
||||
const bool p = AlmostEq(res.p, run.results.p);
|
||||
const bool Ψ = AlmostEq(res.Ψ, run.results.Ψ);
|
||||
|
||||
constexpr auto ok = [](bool ok) -> int { return ok ? 32 : 31; };
|
||||
constexpr auto to_string = [](args_t &args) -> std::string
|
||||
{
|
||||
std::string args_str;
|
||||
for (auto &arg : args)
|
||||
{
|
||||
if (!arg) { break; }
|
||||
args_str += std::string(arg) + " ";
|
||||
}
|
||||
return args_str;
|
||||
};
|
||||
|
||||
dbg("#{} \x1B[33m{}\x1B[m", k, to_string(args).c_str());
|
||||
dbg("U: \x1B[33m{:.15e} \x1B[{}m{:.15e}", run.results.u, ok(u), res.u );
|
||||
dbg("P: \x1B[33m{:.15e} \x1B[{}m{:.15e}", run.results.p, ok(p), res.p);
|
||||
dbg("Ψ: \x1B[33m{:.15e} \x1B[{}m{:.15e}", run.results.Ψ, ok(Ψ), res.Ψ);
|
||||
|
||||
if (u && p && Ψ) { return std::cout << "✅" << std::endl, EXIT_SUCCESS; }
|
||||
else { return std::cout << "❌" << std::endl, EXIT_FAILURE; }
|
||||
};
|
||||
|
||||
// first launch with default arguments
|
||||
if (launch() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
|
||||
// second launch with the same arguments, but with -pa
|
||||
args.pop_back(); // nullptr
|
||||
auto arg_pa_ptr = add_arg("-pa");
|
||||
args.push_back(arg_pa_ptr.get());
|
||||
args_ptr.emplace_back(std::move(arg_pa_ptr));
|
||||
args.push_back(nullptr);
|
||||
if (launch() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
int main(int argc, char *argv[])
|
||||
try
|
||||
{
|
||||
dbg();
|
||||
|
||||
int opt;
|
||||
int test = -1;
|
||||
auto show_usage = [](const int ret = EXIT_FAILURE)
|
||||
{
|
||||
printf("Usage: program [-a <arg>] [-b <arg>] [-h]\n");
|
||||
printf(" -t <test> Optional test number \n");
|
||||
printf(" -h Show this help message\n");
|
||||
exit(ret);
|
||||
};
|
||||
|
||||
while ((opt = getopt(argc, argv, "t:h")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 't': test = std::atoi(optarg); break;
|
||||
case 'h': show_usage(EXIT_SUCCESS);
|
||||
default: show_usage(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int N_TESTS = sizeof(gold) / sizeof(Test);
|
||||
if (test >= 0 && test < N_TESTS) { return NavierTest(test, gold[test]); }
|
||||
|
||||
int k = 0;
|
||||
for (auto &run : gold)
|
||||
{
|
||||
if (NavierTest(k++, run) != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::cerr << "\033[31m..xxxXXX[ERROR]XXXxxx.." << std::endl;
|
||||
std::cerr << "\033[31m{}" << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -80,6 +80,10 @@ add_mfem_miniapp(nurbs_solenoidal
|
||||
LIBRARIES mfem)
|
||||
add_dependencies(nurbs_solenoidal copy_miniapps_nurbs_data)
|
||||
|
||||
add_mfem_miniapp(nurbs_surface
|
||||
MAIN nurbs_surface.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME nurbs_ex1_1d_r1_o2_ser
|
||||
COMMAND $<TARGET_FILE:nurbs_ex1> -no-vis
|
||||
@@ -247,6 +251,14 @@ if (MFEM_ENABLE_TESTING)
|
||||
COMMAND $<TARGET_FILE:nurbs_solenoidal> -no-vis
|
||||
-m ${PROJECT_SOURCE_DIR}/data/cube-nurbs.mesh -r 1 -o 2)
|
||||
|
||||
add_test(NAME nurbs_surface_10_10_10_10_ex1_o3_ser
|
||||
COMMAND $<TARGET_FILE:nurbs_surface> -no-vis
|
||||
-o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 1 -orig)
|
||||
|
||||
add_test(NAME nurbs_surface_10_10_40_40_ex1_o3_ser
|
||||
COMMAND $<TARGET_FILE:nurbs_surface> -no-vis
|
||||
-o 3 -nx 10 -ny 10 -fnx 40 -fny 14 -ex 1)
|
||||
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
|
||||
@@ -21,7 +21,7 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_ex3 nurbs_ex5 nurbs_ex24 \
|
||||
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh
|
||||
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh nurbs_surface
|
||||
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
@@ -158,6 +158,13 @@ nurbs_naca_cmesh-test-seq: nurbs_naca_cmesh
|
||||
nurbs_printfunc-test-seq: nurbs_printfunc
|
||||
@$(call mfem-test,$<,, NURBS miniapp)
|
||||
|
||||
SURF_ARGS_1 := -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 1 -orig
|
||||
SURF_ARGS_2 := -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 1
|
||||
|
||||
nurbs_surface-test-seq: nurbs_surface
|
||||
@$(call mfem-test,$<,, NURBS miniapp,$(SURF_ARGS_1))
|
||||
@$(call mfem-test,$<,, NURBS miniapp,$(SURF_ARGS_2))
|
||||
|
||||
EX1P_ARGS_1 :=
|
||||
EX1P_ARGS_2 := -m ../../data/pipe-nurbs-2d.mesh -o 2 -no-ibp
|
||||
EX1P_ARGS_3 := -m ../../data/ball-nurbs.mesh -o 2 --weak-bc -r 0
|
||||
@@ -192,6 +199,6 @@ clean-build:
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh sin-fit.mesh ex5.mesh exsol.mesh mesh.* sol.* mode_*
|
||||
@rm -f naca-cmesh.mesh sol_?.gf
|
||||
@rm -f naca-cmesh.mesh sol_?.gf *-Surface.mesh
|
||||
@rm -rf Example1* Example3* Example5* Solenoidal_* ParaView
|
||||
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh solution.dat
|
||||
|
||||
@@ -0,0 +1,655 @@
|
||||
// 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.
|
||||
//
|
||||
// --------------------------------------------------------
|
||||
// NURBS Surface: Interpolate a 3D Surface in a NURBS Patch
|
||||
// --------------------------------------------------------
|
||||
//
|
||||
// Compile with: make nurbs_surface
|
||||
//
|
||||
// Sample runs: nurbs_surface -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 1 -orig
|
||||
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 1
|
||||
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 10 -fny 10 -ex 1
|
||||
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 40 -fny 40 -ex 1 -j 0.5
|
||||
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 2 -orig
|
||||
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 2
|
||||
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 10 -fny 10 -ex 2
|
||||
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 3 -orig
|
||||
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 3
|
||||
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 10 -fny 10 -ex 3
|
||||
// nurbs_surface -o 3 -nx 20 -ny 10 -fnx 20 -fny 10 -ex 4 -orig
|
||||
// * nurbs_surface -o 3 -nx 20 -ny 10 -fnx 80 -fny 40 -ex 4
|
||||
// * nurbs_surface -o 3 -nx 40 -ny 20 -fnx 20 -fny 10 -ex 4
|
||||
// * nurbs_surface -o 3 -nx 100 -ny 100 -fnx 100 -fny 100 -ex 5 -orig
|
||||
// * nurbs_surface -o 3 -nx 100 -ny 100 -fnx 400 -fny 400 -ex 5
|
||||
// * nurbs_surface -o 3 -nx 200 -ny 200 -fnx 100 -fny 100 -ex 5
|
||||
//
|
||||
// Description: This example demonstrates the use of MFEM to interpolate an
|
||||
// input surface point grid in 3D using a NURBS surface. The NURBS
|
||||
// surface can then be sampled to generate an output mesh of
|
||||
// arbitrary resolution while staying close to the input geometry.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Example data for 3D point grid on surface, given by an analytic function.
|
||||
void SurfaceGridExample(int example, int nx, int ny, Array3D<real_t> &vertices,
|
||||
real_t jitter);
|
||||
|
||||
// Write a linear surface mesh with given vertex positions in v.
|
||||
void WriteLinearMesh(int nx, int ny, const Array3D<real_t> &v,
|
||||
const std::string &basename, bool visualization = false,
|
||||
int x = 0, int y = 0, int w = 500, int h = 500);
|
||||
|
||||
// Given an input grid of 3D points on a surface, this class computes a NURBS
|
||||
// surface of given order that interpolates the vertices of the input grid.
|
||||
class SurfaceInterpolator
|
||||
{
|
||||
public:
|
||||
/// Constructor for a given 2D point grid size and NURBS order.
|
||||
SurfaceInterpolator(int num_elem_x, int num_elem_y, int order);
|
||||
|
||||
/// Create a surface interpolating the 2D grid of 3D points in @a input3D.
|
||||
void CreateSurface(const Array3D<real_t> &input3D);
|
||||
|
||||
/// Sample the surface with the given grid size, storing points in
|
||||
/// @a output3D.
|
||||
void SampleSurface(int num_elem_x, int num_elem_y, bool compareOriginal,
|
||||
Array3D<real_t> &output3D);
|
||||
|
||||
/** @brief Write the NURBS surface mesh to file, defined coordinate-wise by
|
||||
the entries of @a cmesh. */
|
||||
void WriteNURBSMesh(const std::string &basename, bool visualization = false,
|
||||
int x = 0, int y = 0, int w = 500, int h = 500);
|
||||
|
||||
protected:
|
||||
/** @brief Compute the NURBS mesh interpolating the given coordinate of the
|
||||
grid of 3D points in @a input3D. */
|
||||
void ComputeNURBS(int coordinate, const Array3D<real_t> &input3D);
|
||||
|
||||
private:
|
||||
int nx, ny; // Number of elements in two directions of the surface grid
|
||||
int orderNURBS; // NURBS degree
|
||||
real_t hx, hy, hz; // Grid size in reference space
|
||||
|
||||
Array3D<real_t> initial3D; // Initial grid of points
|
||||
|
||||
static constexpr int dim = 3;
|
||||
Array<int> ncp; // Number of control points in each direction
|
||||
Array<int> nks; // Number of knot-spans in each direction
|
||||
|
||||
std::vector<Vector> ugrid; // Parameter space [0,1]^2 grid point coordinates
|
||||
|
||||
std::vector<KnotVector> kv; // KnotVectors in each direction
|
||||
|
||||
std::unique_ptr<NURBSPatch> patch; // Pointer to the only patch in the mesh
|
||||
|
||||
Mesh mesh; // NURBS mesh representing the surface
|
||||
std::vector<Mesh> cmesh; // NURBS meshes representing point components
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Parse command-line options
|
||||
int nx = 4;
|
||||
int ny = 4;
|
||||
int fnx = 40;
|
||||
int fny = 40;
|
||||
int order = 3;
|
||||
int example = 1;
|
||||
bool visualization = true;
|
||||
bool compareOriginal = false;
|
||||
real_t jitter = 0.0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&example, "-ex", "--example",
|
||||
"Example data");
|
||||
args.AddOption(&nx, "-nx", "--nx",
|
||||
"Number of elements in x");
|
||||
args.AddOption(&ny, "-ny", "--ny",
|
||||
"Number of elements in y");
|
||||
args.AddOption(&fnx, "-fnx", "--fnx",
|
||||
"Number of resampled elements in x");
|
||||
args.AddOption(&fny, "-fny", "--fny",
|
||||
"Number of resampled elements in y");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"NURBS finite element order (polynomial degree)");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&compareOriginal, "-orig", "--compare-original", "-no-orig",
|
||||
"--no-compare-original",
|
||||
"Compare to the original mesh?");
|
||||
args.AddOption(&jitter, "-j", "--jitter",
|
||||
"Relative jittering in (0,1) to add to the input point "
|
||||
"coordinates on a uniform nx x ny grid (0 by default)");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
if (compareOriginal && (fnx != nx || fny != ny))
|
||||
{
|
||||
cout << "Comparing to the original mesh requires the same number of "
|
||||
<< "samples!\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Dimensions of the 3 surfaces (Input, NURBS, Output)
|
||||
cout << "Input Surface: " << nx << " x " << ny << " linear elements\n";
|
||||
cout << "NURBS Surface: " << nx + 1 - order << " x " << ny + 1 - order
|
||||
<< " knot elements of order " << order << "\n";
|
||||
cout << "Output Surface: " << fnx << " x " << fny << " linear elements\n";
|
||||
|
||||
// Set the vertex coordinates of the initial linear mesh
|
||||
constexpr int dim = 3;
|
||||
Array3D<real_t> input3D(nx + 1, ny + 1, dim);
|
||||
SurfaceGridExample(example, nx, ny, input3D, jitter);
|
||||
|
||||
// Create a NURBS surface for the given nx, ny and order parameters that
|
||||
// interpolates the input vertex coordinates
|
||||
SurfaceInterpolator surf(nx, ny, order);
|
||||
surf.CreateSurface(input3D);
|
||||
|
||||
// Compute the vertex coordinates of the output linear mesh by sampling the
|
||||
// values from the NURBS surface
|
||||
Array3D<real_t> output3D(fnx + 1, fny + 1, dim);
|
||||
surf.SampleSurface(fnx, fny, compareOriginal, output3D);
|
||||
|
||||
// Save and optionally visualize the 3 surfaces (Input, NURBS, Output)
|
||||
WriteLinearMesh(nx, ny, input3D, "Input-Surface", visualization, 0, 0);
|
||||
surf.WriteNURBSMesh("NURBS-Surface", visualization, 502, 0);
|
||||
WriteLinearMesh(fnx, fny, output3D, "Output-Surface", visualization, 1004, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// f(x,y) = sin(2 * pi * x) * sin(2 * pi * y)
|
||||
void Function1(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
|
||||
{
|
||||
x = u;
|
||||
y = v;
|
||||
z = sin(2.0 * M_PI * u) * sin(2.0 * M_PI * v);
|
||||
}
|
||||
|
||||
// Part of the parametric surface of a sphere, using spherical coordinates.
|
||||
void Function2(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
|
||||
{
|
||||
constexpr real_t r = 1.0;
|
||||
constexpr real_t pi_4 = M_PI * 0.25;
|
||||
constexpr real_t phi0 = -3*pi_4;
|
||||
constexpr real_t phi1 = 3*pi_4;
|
||||
constexpr real_t theta0 = pi_4;
|
||||
constexpr real_t theta1 = 3 * pi_4;
|
||||
|
||||
const real_t phi = (phi0 * (1.0 - v)) + (phi1 * v);
|
||||
const real_t theta = (theta0 * (1.0 - u)) + (theta1 * u);
|
||||
x = r * sin(theta) * cos(phi);
|
||||
y = r * sin(theta) * sin(phi);
|
||||
z = r * cos(theta);
|
||||
}
|
||||
|
||||
// Helicoid surface
|
||||
void Function3(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
|
||||
{
|
||||
x = u * cos(2.0 * M_PI * v);
|
||||
y = u * sin(2.0 * M_PI * v);
|
||||
z = v;
|
||||
}
|
||||
|
||||
// Mobius strip
|
||||
void Function4(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
|
||||
{
|
||||
constexpr int twists = 1;
|
||||
const real_t a = 1.0 + 0.5 * ((2.0 * v) - 1.0) * cos(2.0 * M_PI * twists * u);
|
||||
x = a * cos(2.0 * M_PI * u);
|
||||
y = a * sin(2.0 * M_PI * u);
|
||||
z = 0.5 * (2.0 * v - 1.0) * sin(2.0 * M_PI * twists * u);
|
||||
}
|
||||
|
||||
// Breather surface
|
||||
void Function5(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
|
||||
{
|
||||
const real_t m = 13.2 * ((2.0 * u) - 1.0);
|
||||
const real_t n = 37.4 * ((2.0 * v) - 1.0);
|
||||
constexpr real_t b = 0.4;
|
||||
constexpr real_t r = 1.0 - (b*b);
|
||||
const real_t w = sqrt(r);
|
||||
const real_t denom = b * (pow(w*cosh(b*m),2) + pow(b*sin(w*n),2));
|
||||
x = -m + (2*r*cosh(b*m)*sinh(b*m)) / denom;
|
||||
y = (2*w*cosh(b*m)*(-(w*cos(n)*cos(w*n)) - sin(n)*sin(w*n))) / denom;
|
||||
z = (2*w*cosh(b*m)*(-(w*sin(n)*cos(w*n)) + cos(n)*sin(w*n))) / denom;
|
||||
}
|
||||
|
||||
void SurfaceFunction(int example, real_t u, real_t v,
|
||||
real_t &x, real_t &y, real_t &z)
|
||||
{
|
||||
switch (example)
|
||||
{
|
||||
case 1:
|
||||
Function1(u, v, x, y, z);
|
||||
break;
|
||||
case 2:
|
||||
Function2(u, v, x, y, z);
|
||||
break;
|
||||
case 3:
|
||||
Function3(u, v, x, y, z);
|
||||
break;
|
||||
case 4:
|
||||
Function4(u, v, x, y, z);
|
||||
break;
|
||||
default:
|
||||
Function5(u, v, x, y, z);
|
||||
};
|
||||
}
|
||||
|
||||
// Example data for 3D point grid on surface, given by an analytic function.
|
||||
void SurfaceExample(int example, const std::vector<Vector> &grid,
|
||||
Array3D<real_t> &v3D, real_t jitter)
|
||||
{
|
||||
int seed = (int)time(0);
|
||||
srand((unsigned)seed);
|
||||
|
||||
real_t h0 = grid[0][1]-grid[0][0], h1 = grid[1][1]-grid[1][0];
|
||||
for (int i = 0; i < grid[0].Size(); i++)
|
||||
{
|
||||
for (int j = 0; j < grid[1].Size(); j++)
|
||||
{
|
||||
if (i != 0 && i != grid[0].Size()-1 && j != 0 && j != grid[1].Size()-1)
|
||||
{
|
||||
SurfaceFunction(example, grid[0][i] + rand_real()*h0*jitter,
|
||||
grid[1][j] + rand_real()*h1*jitter,
|
||||
v3D(i, j, 0), v3D(i, j, 1), v3D(i, j, 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
SurfaceFunction(example, grid[0][i], grid[1][j],
|
||||
v3D(i, j, 0), v3D(i, j, 1), v3D(i, j, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceGridExample(int example, int nx, int ny, Array3D<real_t> &vertices,
|
||||
real_t jitter = 0)
|
||||
{
|
||||
// Define a uniform grid of the reference parameter space [0,1]^2
|
||||
std::vector<Vector> uniformGrid(2);
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
const int n = (i == 0) ? nx : ny;
|
||||
const real_t h = 1.0 / n;
|
||||
uniformGrid[i].SetSize(n + 1);
|
||||
for (int j = 0; j <= n; ++j) { uniformGrid[i][j] = j * h; }
|
||||
}
|
||||
|
||||
SurfaceExample(example, uniformGrid, vertices, jitter);
|
||||
}
|
||||
|
||||
// Write a linear surface mesh with given vertex positions in v.
|
||||
void WriteLinearMesh(int nx, int ny, const Array3D<real_t> &v,
|
||||
const std::string &basename, bool visualization,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
const int nv = (nx + 1) * (ny + 1);
|
||||
const int nelem = nx * ny;
|
||||
constexpr int dim = 3; // Spatial dimension
|
||||
|
||||
Mesh lmesh(2, nv, nelem, 0, dim);
|
||||
Vector vertex(dim);
|
||||
|
||||
for (int i = 0; i <= nx; ++i)
|
||||
{
|
||||
for (int j = 0; j <= ny; ++j)
|
||||
{
|
||||
for (int k = 0; k < dim; ++k) { vertex[k] = v(i, j, k); }
|
||||
lmesh.AddVertex(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> verts(4);
|
||||
|
||||
auto vID = [&](int i, int j)
|
||||
{
|
||||
return j + (i * (ny + 1));
|
||||
};
|
||||
|
||||
for (int i = 0; i < nx; ++i)
|
||||
{
|
||||
for (int j = 0; j < ny; ++j)
|
||||
{
|
||||
verts[0] = vID(i, j);
|
||||
verts[1] = vID(i+1, j);
|
||||
verts[2] = vID(i+1, j+1);
|
||||
verts[3] = vID(i, j+1);
|
||||
|
||||
Element* el = lmesh.NewElement(Element::QUADRILATERAL);
|
||||
el->SetVertices(verts);
|
||||
lmesh.AddElement(el);
|
||||
}
|
||||
}
|
||||
|
||||
lmesh.FinalizeTopology();
|
||||
|
||||
ofstream mesh_ofs(basename + ".mesh");
|
||||
mesh_ofs.precision(8);
|
||||
lmesh.Print(mesh_ofs);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
constexpr int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "mesh\n" << lmesh
|
||||
<< "window_title '" << basename << "'"
|
||||
<< "window_geometry "
|
||||
<< x << " " << y << " " << w << " " << h << "\n"
|
||||
<< "keys PPPPPPPPAattttt******\n"
|
||||
<< flush;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute error of interpolation with respect to an input grid of point data.
|
||||
void CheckError(const Array3D<real_t> &a, const Array3D<real_t> &b, int c,
|
||||
int nx, int ny)
|
||||
{
|
||||
real_t maxErr = 0.0;
|
||||
for (int i = 0; i <= nx; ++i)
|
||||
{
|
||||
for (int j = 0; j <= ny; ++j)
|
||||
{
|
||||
const real_t err_ij = std::abs(a(i, j, c) - b(i, j, 2));
|
||||
maxErr = std::max(maxErr, err_ij);
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Max error: " << maxErr << " for coordinate " << c << endl;
|
||||
}
|
||||
|
||||
|
||||
// Sample a NURBS mesh to generate a first-order mesh.
|
||||
void SampleNURBS(bool uniform, int nx, int ny, const Mesh &mesh,
|
||||
const Array<int> &nks, const std::vector<Vector> &ugrid,
|
||||
Array3D<real_t> &vpos)
|
||||
{
|
||||
const GridFunction *nodes = mesh.GetNodes();
|
||||
|
||||
const real_t hx = 1.0 / (real_t) nx;
|
||||
const real_t hy = 1.0 / (real_t) ny;
|
||||
|
||||
const real_t hxks = 1.0 / (real_t) nks[0];
|
||||
const real_t hyks = 1.0 / (real_t) nks[1];
|
||||
|
||||
Vector vertex;
|
||||
IntegrationPoint ip;
|
||||
|
||||
ip.z = 1.0;
|
||||
for (int i = 0; i <= nx; ++i)
|
||||
{
|
||||
const real_t xref = uniform ? i * hx : ugrid[0][i];
|
||||
const int nurbsElem0 = std::min((int) (xref / hxks), nks[0] - 1);
|
||||
const real_t ipx = (xref - (nurbsElem0 * hxks)) / hxks;
|
||||
ip.x = ipx;
|
||||
|
||||
for (int j = 0; j <= ny; ++j)
|
||||
{
|
||||
const real_t yref = uniform ? j * hy : ugrid[1][j];
|
||||
const int nurbsElem1 = std::min((int) (yref / hyks), nks[1] - 1);
|
||||
const real_t ipy = (yref - (nurbsElem1 * hyks)) / hyks;
|
||||
ip.y = ipy;
|
||||
|
||||
const int nurbsElem = nurbsElem0 + (nurbsElem1 * nks[0]);
|
||||
nodes->GetVectorValue(nurbsElem, ip, vertex);
|
||||
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
vpos(i, j, k) = vertex[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SurfaceInterpolator::SurfaceInterpolator(int num_elem_x, int num_elem_y,
|
||||
int order) :
|
||||
nx(num_elem_x), ny(num_elem_y), orderNURBS(order),
|
||||
ncp(dim), nks(dim), ugrid(dim - 1)
|
||||
{
|
||||
ncp[0] = nx + 1;
|
||||
ncp[1] = ny + 1;
|
||||
ncp[2] = order + 1;
|
||||
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
nks[i] = ncp[i] - order;
|
||||
|
||||
Vector intervals(nks[i]);
|
||||
Array<int> continuity(nks[i] + 1);
|
||||
|
||||
intervals = 1.0 / (real_t) nks[i];
|
||||
continuity = order - 1;
|
||||
continuity[0] = -1;
|
||||
continuity[nks[i]] = -1;
|
||||
|
||||
kv.emplace_back(order, intervals, continuity);
|
||||
}
|
||||
|
||||
patch.reset(new NURBSPatch(&kv[0], &kv[1], &kv[2], dim + 1));
|
||||
|
||||
hx = 1.0 / (real_t) (ncp[0] - 1);
|
||||
hy = 1.0 / (real_t) (ncp[1] - 1);
|
||||
hz = 1.0 / (real_t) (ncp[2] - 1);
|
||||
|
||||
Vector xi_args;
|
||||
Array<int> i_args;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
kv[i].FindMaxima(i_args, xi_args, ugrid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceInterpolator::CreateSurface(const Array3D<real_t> &input3D)
|
||||
{
|
||||
cmesh.clear();
|
||||
for (int c = 0; c < dim; ++c) // Loop over coordinates
|
||||
{
|
||||
ComputeNURBS(c, input3D);
|
||||
cmesh.emplace_back(mesh);
|
||||
}
|
||||
|
||||
initial3D = input3D;
|
||||
}
|
||||
|
||||
void SurfaceInterpolator::SampleSurface(int num_elem_x, int num_elem_y,
|
||||
bool compareOriginal,
|
||||
Array3D<real_t> &output3D)
|
||||
{
|
||||
Array3D<real_t> vpos(num_elem_x + 1, num_elem_y + 1, dim);
|
||||
for (int c = 0; c < dim; ++c) // Loop over coordinates
|
||||
{
|
||||
SampleNURBS(true, num_elem_x, num_elem_y, cmesh[c], nks, ugrid, vpos);
|
||||
|
||||
if (compareOriginal)
|
||||
{
|
||||
SampleNURBS(false, num_elem_x, num_elem_y, cmesh[c], nks, ugrid, vpos);
|
||||
CheckError(initial3D, vpos, c, nx, ny);
|
||||
}
|
||||
|
||||
for (int i = 0; i <= num_elem_x; ++i)
|
||||
{
|
||||
for (int j = 0; j <= num_elem_y; ++j)
|
||||
{
|
||||
output3D(i,j,c) = vpos(i,j,2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceInterpolator::ComputeNURBS(int coordinate,
|
||||
const Array3D<real_t> &input3D)
|
||||
{
|
||||
Array<Vector*> x;
|
||||
for (int i = 0; i < dim; ++i) { x.Append(new Vector(ncp[0])); }
|
||||
|
||||
for (int k = 0; k < ncp[2]; ++k)
|
||||
{
|
||||
const real_t z = k * hz;
|
||||
|
||||
// For each horizontal slice (fixed k), interpolate a 2D surface by
|
||||
// sweeping curve interpolations in each direction. See Algorithm A9.4 of
|
||||
// "The NURBS Book" - 2nd ed - Piegl and Tiller.
|
||||
|
||||
// Resize for sweep in first direction
|
||||
for (int i = 0; i < dim; ++i) { x[i]->SetSize(ncp[0]); }
|
||||
|
||||
// Sweep in the first direction
|
||||
for (int j = 0; j < ncp[1]; ++j)
|
||||
{
|
||||
for (int i = 0; i < ncp[0]; i++)
|
||||
{
|
||||
(*x[0])[i] = ugrid[0][i];
|
||||
(*x[1])[i] = ugrid[1][j];
|
||||
|
||||
const real_t s_ij = input3D(i, j, coordinate);
|
||||
(*x[2])[i] = -1.0 + z + s_ij;
|
||||
}
|
||||
|
||||
const bool reuse_factorization = j > 0;
|
||||
kv[0].FindInterpolant(x, reuse_factorization);
|
||||
|
||||
for (int i = 0; i < ncp[0]; i++)
|
||||
{
|
||||
(*patch)(i,j,k,0) = (*x[0])[i];
|
||||
(*patch)(i,j,k,1) = (*x[1])[i];
|
||||
(*patch)(i,j,k,2) = (*x[2])[i];
|
||||
(*patch)(i,j,k,3) = 1.0; // weight
|
||||
}
|
||||
}
|
||||
|
||||
// Resize for sweep in second direction
|
||||
for (int i = 0; i < dim; ++i) { x[i]->SetSize(ncp[1]); }
|
||||
|
||||
// Do another sweep in the second direction
|
||||
for (int i = 0; i < ncp[0]; i++)
|
||||
{
|
||||
for (int j = 0; j < ncp[1]; ++j)
|
||||
{
|
||||
(*x[0])[j] = (*patch)(i,j,k,0);
|
||||
(*x[1])[j] = (*patch)(i,j,k,1);
|
||||
(*x[2])[j] = (*patch)(i,j,k,2);
|
||||
}
|
||||
|
||||
const bool reuse_factorization = i > 0;
|
||||
kv[1].FindInterpolant(x, reuse_factorization);
|
||||
|
||||
for (int j = 0; j < ncp[1]; ++j)
|
||||
{
|
||||
(*patch)(i,j,k,0) = (*x[0])[j];
|
||||
(*patch)(i,j,k,1) = (*x[1])[j];
|
||||
(*patch)(i,j,k,2) = (*x[2])[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto p : x) { delete p; }
|
||||
|
||||
Array<const NURBSPatch*> patches(1);
|
||||
patches[0] = patch.get();
|
||||
Mesh patch_topology = Mesh::MakeCartesian3D(1, 1, 1, Element::HEXAHEDRON);
|
||||
NURBSExtension nurbsExt(&patch_topology, patches);
|
||||
|
||||
mesh = Mesh(nurbsExt);
|
||||
}
|
||||
|
||||
void SurfaceInterpolator::WriteNURBSMesh(const std::string &basename,
|
||||
bool visualization,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
GridFunction *nodes = cmesh[0].GetNodes();
|
||||
NURBSPatch patch2D(&kv[0], &kv[1], dim);
|
||||
Array<const NURBSPatch*> patches(1);
|
||||
patches[0] = &patch2D;
|
||||
Mesh patch_topology = Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL);
|
||||
Array<int> dofs;
|
||||
cmesh[0].NURBSext->GetPatchDofs(0, dofs);
|
||||
|
||||
MFEM_VERIFY(dofs.Size() == (nx + 1) * (ny + 1) * (orderNURBS + 1), "");
|
||||
|
||||
for (int j = 0; j < ncp[1]; ++j)
|
||||
{
|
||||
for (int i = 0; i < ncp[0]; i++)
|
||||
{
|
||||
const int dof = dofs[i + (ncp[0] * (j + (ncp[1] * orderNURBS)))];
|
||||
for (int k = 0; k < 2; ++k) { patch2D(i,j,k) = (*nodes)[dim*dof + k]; }
|
||||
patch2D(i,j,2) = 1.0; // weight
|
||||
}
|
||||
}
|
||||
|
||||
NURBSExtension nurbsExt(&patch_topology, patches);
|
||||
Mesh mesh2D(nurbsExt);
|
||||
|
||||
FiniteElementCollection *fec = nodes->OwnFEC();
|
||||
FiniteElementSpace fespace(&mesh2D, fec, dim, Ordering::byVDIM);
|
||||
GridFunction nodes2D(&fespace);
|
||||
|
||||
const int n = mesh2D.GetNodes()->Size() / (dim - 1);
|
||||
MFEM_VERIFY((dim - 1) * n == mesh2D.GetNodes()->Size(), "");
|
||||
MFEM_VERIFY(dim * n == nodes2D.Size(), "");
|
||||
|
||||
Array<int> dofs2D;
|
||||
mesh2D.NURBSext->GetPatchDofs(0, dofs2D);
|
||||
|
||||
for (int k = 0; k < dim; ++k)
|
||||
{
|
||||
const GridFunction &nodes_k = *cmesh[k].GetNodes();
|
||||
|
||||
for (int j = 0; j < ncp[1]; ++j)
|
||||
{
|
||||
for (int i = 0; i < ncp[0]; i++)
|
||||
{
|
||||
const int dof = dofs[i + (ncp[0] * (j + (ncp[1] * orderNURBS)))];
|
||||
const int dof2D = dofs2D[i + (ncp[0] * j)];
|
||||
nodes2D[(dim*dof2D) + k] = nodes_k[dim*dof + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make mesh2D into a surface mesh with nodes given by nodes2D
|
||||
mesh2D.NewNodes(nodes2D);
|
||||
|
||||
ofstream mesh_ofs(basename + ".mesh");
|
||||
mesh_ofs.precision(8);
|
||||
mesh2D.Print(mesh_ofs);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
constexpr int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "mesh\n" << mesh2D
|
||||
<< "window_title '" << basename << "'"
|
||||
<< "window_geometry "
|
||||
<< x << " " << y << " " << w << " " << h << "\n"
|
||||
<< "keys PPPPPPPPAattttt******\n"
|
||||
<< flush;
|
||||
}
|
||||
}
|
||||
@@ -52,12 +52,13 @@
|
||||
// (respectively 0), essential (respectively natural) boundary condition
|
||||
// will be imposed on boundary with the i-th attribute.
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "bramble_pasciak.hpp"
|
||||
#include "div_free_solver.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
@@ -83,48 +84,54 @@ real_t natural_bc(const Vector & x);
|
||||
D: subset of the boundary where natural boundary condition is imposed. */
|
||||
class DarcyProblem
|
||||
{
|
||||
OperatorPtr M_;
|
||||
OperatorPtr B_;
|
||||
Vector rhs_;
|
||||
Vector ess_data_;
|
||||
ParGridFunction u_;
|
||||
ParGridFunction p_;
|
||||
OperatorPtr M_, B_;
|
||||
Vector rhs_, ess_data_;
|
||||
ParGridFunction u_, p_;
|
||||
ParMesh mesh_;
|
||||
ParBilinearForm *mVarf_;
|
||||
ParMixedBilinearForm *bVarf_;
|
||||
DFSSpaces dfs_spaces_;
|
||||
std::function<bool (int)> refine_fn = [&](int num_refs)
|
||||
{
|
||||
for (int l = 0; l < num_refs; l++)
|
||||
{
|
||||
mesh_.UniformRefinement();
|
||||
dfs_spaces_.CollectDFSData();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const bool dfs_refine_;
|
||||
ParBilinearForm mVarf_;
|
||||
ParMixedBilinearForm bVarf_;
|
||||
VectorFunctionCoefficient ucoeff_;
|
||||
FunctionCoefficient pcoeff_;
|
||||
DFSSpaces dfs_spaces_;
|
||||
PWConstCoefficient mass_coeff;
|
||||
const IntegrationRule *irs_[Geometry::NumGeom];
|
||||
public:
|
||||
DarcyProblem(Mesh &mesh, int num_refines, int order, const char *coef_file,
|
||||
Array<int> &ess_bdr, DFSParameters param);
|
||||
|
||||
HypreParMatrix& GetM() { return *M_.As<HypreParMatrix>(); }
|
||||
HypreParMatrix& GetB() { return *B_.As<HypreParMatrix>(); }
|
||||
const HypreParMatrix& GetM() const { return *M_.As<HypreParMatrix>(); }
|
||||
const HypreParMatrix& GetB() const { return *B_.As<HypreParMatrix>(); }
|
||||
const Vector& GetRHS() { return rhs_; }
|
||||
const Vector& GetEssentialBC() { return ess_data_; }
|
||||
const DFSData& GetDFSData() const { return dfs_spaces_.GetDFSData(); }
|
||||
void ShowError(const Vector &sol, bool verbose);
|
||||
void VisualizeSolution(const Vector &sol, std::string tag, int visport = 19916);
|
||||
ParBilinearForm* GetMform() const { return mVarf_; }
|
||||
ParMixedBilinearForm* GetBform() const { return bVarf_; }
|
||||
ParBilinearForm& GetMform() { return mVarf_; }
|
||||
ParMixedBilinearForm& GetBform() { return bVarf_; }
|
||||
};
|
||||
|
||||
DarcyProblem::DarcyProblem(Mesh &mesh, int num_refs, int order,
|
||||
const char *coef_file, Array<int> &ess_bdr,
|
||||
DFSParameters dfs_param)
|
||||
: mesh_(MPI_COMM_WORLD, mesh), ucoeff_(mesh.Dimension(), u_exact),
|
||||
pcoeff_(p_exact), dfs_spaces_(order, num_refs, &mesh_, ess_bdr, dfs_param),
|
||||
: mesh_(MPI_COMM_WORLD, mesh),
|
||||
dfs_spaces_(order, num_refs, &mesh_, ess_bdr, dfs_param),
|
||||
dfs_refine_(refine_fn(num_refs)),
|
||||
mVarf_(dfs_spaces_.GetHdivFES()),
|
||||
bVarf_(dfs_spaces_.GetHdivFES(), dfs_spaces_.GetL2FES()),
|
||||
ucoeff_(mesh.Dimension(), u_exact),
|
||||
pcoeff_(p_exact),
|
||||
mass_coeff()
|
||||
{
|
||||
for (int l = 0; l < num_refs; l++)
|
||||
{
|
||||
mesh_.UniformRefinement();
|
||||
dfs_spaces_.CollectDFSData();
|
||||
}
|
||||
|
||||
Vector coef_vector(mesh.GetNE());
|
||||
coef_vector = 1.0;
|
||||
if (std::strcmp(coef_file, ""))
|
||||
@@ -153,24 +160,20 @@ DarcyProblem::DarcyProblem(Mesh &mesh, int num_refs, int order,
|
||||
gform.AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
|
||||
gform.Assemble();
|
||||
|
||||
mVarf_ = new ParBilinearForm(dfs_spaces_.GetHdivFES());
|
||||
bVarf_ = new ParMixedBilinearForm(dfs_spaces_.GetHdivFES(),
|
||||
dfs_spaces_.GetL2FES());
|
||||
mVarf_.AddDomainIntegrator(new VectorFEMassIntegrator(mass_coeff));
|
||||
mVarf_.ComputeElementMatrices();
|
||||
mVarf_.Assemble();
|
||||
mVarf_.EliminateEssentialBC(ess_bdr, u_, fform);
|
||||
|
||||
mVarf_->AddDomainIntegrator(new VectorFEMassIntegrator(mass_coeff));
|
||||
mVarf_->ComputeElementMatrices();
|
||||
mVarf_->Assemble();
|
||||
mVarf_->EliminateEssentialBC(ess_bdr, u_, fform);
|
||||
mVarf_.Finalize();
|
||||
M_.Reset(mVarf_.ParallelAssemble());
|
||||
|
||||
mVarf_->Finalize();
|
||||
M_.Reset(mVarf_->ParallelAssemble());
|
||||
|
||||
bVarf_->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
||||
bVarf_->Assemble();
|
||||
bVarf_->SpMat() *= -1.0;
|
||||
bVarf_->EliminateTrialEssentialBC(ess_bdr, u_, gform);
|
||||
bVarf_->Finalize();
|
||||
B_.Reset(bVarf_->ParallelAssemble());
|
||||
bVarf_.AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
||||
bVarf_.Assemble();
|
||||
bVarf_.SpMat() *= -1.0;
|
||||
bVarf_.EliminateTrialEssentialBC(ess_bdr, u_, gform);
|
||||
bVarf_.Finalize();
|
||||
B_.Reset(bVarf_.ParallelAssemble());
|
||||
|
||||
rhs_.SetSize(M_->NumRows() + B_->NumRows());
|
||||
Vector rhs_block0(rhs_.GetData(), M_->NumRows());
|
||||
@@ -341,8 +344,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Generate components of the saddle point problem
|
||||
DarcyProblem darcy(*mesh, par_ref_levels, order, coef_file, ess_bdr, param);
|
||||
HypreParMatrix& M = darcy.GetM();
|
||||
HypreParMatrix& B = darcy.GetB();
|
||||
const HypreParMatrix &M = darcy.GetM();
|
||||
const HypreParMatrix &B = darcy.GetB();
|
||||
const DFSData& DFS_data = darcy.GetDFSData();
|
||||
delete mesh;
|
||||
|
||||
|
||||
@@ -14,29 +14,27 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
BlockFESpaceOperator::BlockFESpaceOperator(const
|
||||
std::vector<const FiniteElementSpace*> &fespaces):
|
||||
BlockFESpaceOperator::BlockFESpaceOperator(const FESVector &fespaces):
|
||||
Operator(GetHeight(fespaces)),
|
||||
offsets(GetBlockOffsets(fespaces)),
|
||||
prolongColOffsets(GetProColBlockOffsets(fespaces)),
|
||||
restrictRowOffsets(GetResRowBlockOffsets(fespaces)),
|
||||
A(offsets),
|
||||
prolongation(offsets,prolongColOffsets),
|
||||
prolongation(offsets, prolongColOffsets),
|
||||
restriction(restrictRowOffsets, offsets)
|
||||
{
|
||||
for (size_t i = 0; i <fespaces.size(); i++)
|
||||
{
|
||||
// Since const_cast is required here, be sure to avoid using
|
||||
// BlockOperator::GetBlock on restriction or prolongation.
|
||||
prolongation.SetDiagonalBlock(i,
|
||||
const_cast<Operator *>(fespaces[i]->GetProlongationMatrix()));
|
||||
restriction.SetDiagonalBlock(i,
|
||||
const_cast<Operator *>(fespaces[i]->GetRestrictionOperator()));
|
||||
auto prolongation_matrix = fespaces[i]->GetProlongationMatrix();
|
||||
auto restriction_matrix = fespaces[i]->GetRestrictionOperator();
|
||||
prolongation.SetDiagonalBlock(i, const_cast<Operator *>(prolongation_matrix));
|
||||
restriction.SetDiagonalBlock(i, const_cast<Operator *>(restriction_matrix));
|
||||
}
|
||||
}
|
||||
|
||||
int BlockFESpaceOperator::GetHeight(const std::vector<const FiniteElementSpace*>
|
||||
&fespaces)
|
||||
int BlockFESpaceOperator::GetHeight(const FESVector &fespaces)
|
||||
{
|
||||
int height = 0;
|
||||
for (size_t i = 0; i < fespaces.size(); i++)
|
||||
@@ -46,8 +44,7 @@ int BlockFESpaceOperator::GetHeight(const std::vector<const FiniteElementSpace*>
|
||||
return height;
|
||||
}
|
||||
|
||||
Array<int> BlockFESpaceOperator::GetBlockOffsets(const
|
||||
std::vector<const FiniteElementSpace*> &fespaces)
|
||||
Array<int> BlockFESpaceOperator::GetBlockOffsets(const FESVector &fespaces)
|
||||
{
|
||||
Array<int> offsets(fespaces.size()+1);
|
||||
offsets[0] = 0;
|
||||
@@ -60,8 +57,8 @@ Array<int> BlockFESpaceOperator::GetBlockOffsets(const
|
||||
return offsets;
|
||||
}
|
||||
|
||||
Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const
|
||||
std::vector<const FiniteElementSpace*> &fespaces)
|
||||
Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const FESVector
|
||||
&fespaces)
|
||||
{
|
||||
Array<int> offsets(fespaces.size()+1);
|
||||
offsets[0] = 0;
|
||||
@@ -83,8 +80,8 @@ Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const
|
||||
return offsets;
|
||||
}
|
||||
|
||||
Array<int> BlockFESpaceOperator::GetResRowBlockOffsets(const
|
||||
std::vector<const FiniteElementSpace*> &fespaces)
|
||||
Array<int> BlockFESpaceOperator::GetResRowBlockOffsets(const FESVector
|
||||
&fespaces)
|
||||
{
|
||||
Array<int> offsets(fespaces.size()+1);
|
||||
std::cout << "fespaces.size() = " << fespaces.size() << std::endl;
|
||||
|
||||
@@ -25,7 +25,8 @@ namespace mfem
|
||||
/// L-Vectors. For example, a block may be a BilinearForm.
|
||||
class BlockFESpaceOperator : public Operator
|
||||
{
|
||||
private:
|
||||
using FESVector = std::vector<const FiniteElementSpace*>;
|
||||
|
||||
/// Offsets for the square "A" operator.
|
||||
Array<int> offsets;
|
||||
/// Column offsets for the prolongation operator.
|
||||
@@ -39,33 +40,27 @@ private:
|
||||
/// Maps true dofs of each block to local dofs.
|
||||
BlockOperator restriction;
|
||||
/// Computes height for parent operator.
|
||||
static int GetHeight(const std::vector<const FiniteElementSpace*>
|
||||
&fespaces);
|
||||
static int GetHeight(const FESVector &fespaces);
|
||||
/// Computes offsets for A BlockOperator.
|
||||
static Array<int> GetBlockOffsets(const std::vector<const FiniteElementSpace*>
|
||||
&fespaces);
|
||||
static Array<int> GetBlockOffsets(const FESVector &fespaces);
|
||||
/// Computes col_offsets for prolongation operator.
|
||||
static Array<int> GetProColBlockOffsets(const
|
||||
std::vector<const FiniteElementSpace*> &fespaces);
|
||||
static Array<int> GetProColBlockOffsets(const FESVector &fespaces);
|
||||
/// Computes row_offsets for restriction operator.
|
||||
static Array<int> GetResRowBlockOffsets(const
|
||||
std::vector<const FiniteElementSpace*> &fespaces);
|
||||
static Array<int> GetResRowBlockOffsets(const FESVector &fespaces);
|
||||
|
||||
public:
|
||||
/// @brief Constructor for BlockFESpaceOperator.
|
||||
/// @param[in] fespaces Finite element spaces for diagonal blocks. Spaces are not owned.
|
||||
BlockFESpaceOperator(const std::vector<const FiniteElementSpace*> &fespaces);
|
||||
BlockFESpaceOperator(const FESVector &fespaces);
|
||||
const Operator* GetProlongation () const override;
|
||||
const Operator* GetRestriction () const override;
|
||||
void Mult(const Vector &x, Vector &y) const override {A.Mult(x,y);};
|
||||
/// @brief Wraps BlockOperator::SetBlock. Eventually would like this class to inherit
|
||||
/// from BlockOperator instead, but can't easily due to ownership of offset data
|
||||
/// in BlockOperator being by reference.
|
||||
void SetBlock( int iRow,
|
||||
int iCol,
|
||||
Operator * op,
|
||||
real_t c = 1.0) {A.SetBlock(iRow, iCol, op, c);};
|
||||
void SetBlock(int iRow, int iCol, Operator *op, real_t c = 1.0) { A.SetBlock(iRow, iCol, op, c); };
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
#endif // MFEM_BLOCK_FESPACE_OPERATOR
|
||||
|
||||
@@ -9,70 +9,65 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
|
||||
#include "bramble_pasciak.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace blocksolvers
|
||||
namespace mfem::blocksolvers
|
||||
{
|
||||
|
||||
/// Bramble-Pasciak Solver
|
||||
BramblePasciakSolver::BramblePasciakSolver(
|
||||
ParBilinearForm *mVarf,
|
||||
ParMixedBilinearForm *bVarf,
|
||||
const BPSParameters ¶m)
|
||||
: DarcySolver(mVarf->ParFESpace()->GetTrueVSize(),
|
||||
bVarf->TestFESpace()->GetTrueVSize())
|
||||
BramblePasciakSolver::BramblePasciakSolver(ParBilinearForm &mVarf,
|
||||
ParMixedBilinearForm &bVarf,
|
||||
const BPSParameters ¶m)
|
||||
: DarcySolver(mVarf.ParFESpace()->GetTrueVSize(),
|
||||
bVarf.TestFESpace()->GetTrueVSize())
|
||||
{
|
||||
M_.reset(mVarf->ParallelAssemble());
|
||||
B_.reset(bVarf->ParallelAssemble());
|
||||
Q_.reset(ConstructMassPreconditioner(*mVarf, param.q_scaling));
|
||||
M_.reset(mVarf.ParallelAssemble());
|
||||
B_.reset(bVarf.ParallelAssemble());
|
||||
Q_.reset(ConstructMassPreconditioner(mVarf, param.q_scaling));
|
||||
|
||||
Vector diagM;
|
||||
M_->GetDiag(diagM);
|
||||
auto BT = B_->Transpose();
|
||||
auto invDBt = new HypreParMatrix(*BT);
|
||||
std::unique_ptr<HypreParMatrix> invDBt(B_->Transpose());
|
||||
invDBt->InvScaleRows(diagM);
|
||||
auto S = ParMult(B_.get(), invDBt);
|
||||
S_.reset(ParMult(B_.get(), invDBt.get(), true));
|
||||
M0_.Reset(new HypreDiagScale(*M_));
|
||||
M1_.Reset(new HypreBoomerAMG(*S));
|
||||
M1_.Reset(new HypreBoomerAMG(*S_));
|
||||
M1_.As<HypreBoomerAMG>()->SetPrintLevel(0);
|
||||
|
||||
Init(*M_, *B_, *Q_, *M0_.As<Solver>(), *M1_.As<Solver>(), param);
|
||||
}
|
||||
|
||||
BramblePasciakSolver::BramblePasciakSolver(
|
||||
HypreParMatrix &M, HypreParMatrix &B, HypreParMatrix &Q,
|
||||
Solver &M0, Solver &M1,
|
||||
const BPSParameters ¶m)
|
||||
BramblePasciakSolver::BramblePasciakSolver(HypreParMatrix &M,
|
||||
HypreParMatrix &B,
|
||||
HypreParMatrix &Q,
|
||||
Solver &M0, Solver &M1,
|
||||
const BPSParameters ¶m)
|
||||
: DarcySolver(M.NumRows(), B.NumRows())
|
||||
{
|
||||
Init(M, B, Q, M0, M1, param);
|
||||
}
|
||||
|
||||
void BramblePasciakSolver::Init(
|
||||
HypreParMatrix &M, HypreParMatrix &B, HypreParMatrix &Q,
|
||||
Solver &M0, Solver &M1,
|
||||
const BPSParameters ¶m)
|
||||
void BramblePasciakSolver::Init(HypreParMatrix &M,
|
||||
HypreParMatrix &B,
|
||||
HypreParMatrix &Q,
|
||||
Solver &M0, Solver &M1,
|
||||
const BPSParameters ¶m)
|
||||
{
|
||||
auto Bt = new TransposeOperator(&B);
|
||||
Bt_ = std::make_unique<TransposeOperator>(&B);
|
||||
auto invQ = new HypreDiagScale(Q);
|
||||
|
||||
use_bpcg = param.use_bpcg;
|
||||
|
||||
if (use_bpcg)
|
||||
{
|
||||
oop_ = new BlockOperator(offsets_);
|
||||
oop_->owns_blocks = false;
|
||||
oop_ = std::make_unique<BlockOperator>(offsets_);
|
||||
oop_->SetBlock(0, 0, &M);
|
||||
oop_->SetBlock(0, 1, Bt);
|
||||
oop_->SetBlock(0, 1, Bt_.get());
|
||||
oop_->SetBlock(1, 0, &B);
|
||||
|
||||
// cpc_ unused in bpcg
|
||||
auto temp_cpc = new BlockDiagonalPreconditioner(offsets_);
|
||||
temp_cpc->owns_blocks = true;
|
||||
temp_cpc->SetDiagonalBlock(0, invQ);
|
||||
temp_cpc->SetDiagonalBlock(1, &M1);
|
||||
// tri(1,0) = B M0 = B invQ
|
||||
@@ -81,51 +76,48 @@ void BramblePasciakSolver::Init(
|
||||
auto BinvQ = new ProductOperator(&B, invQ, false, false);
|
||||
// tri
|
||||
auto temp_tri = new BlockOperator(offsets_);
|
||||
temp_tri->owns_blocks = true;
|
||||
temp_tri->SetBlock(0, 0, id_m);
|
||||
temp_tri->SetBlock(1, 1, id_b, -1.0);
|
||||
temp_tri->SetBlock(1, 0, BinvQ);
|
||||
temp_tri->owns_blocks = 1;
|
||||
|
||||
ppc_ = new ProductOperator(temp_cpc, temp_tri, true, true);
|
||||
ppc_ = std::make_unique<ProductOperator>(temp_cpc, temp_tri, true, true);
|
||||
|
||||
ipc_ = new BlockOperator(offsets_);
|
||||
ipc_->owns_blocks = false;
|
||||
ipc_ = std::make_unique<BlockOperator>(offsets_);
|
||||
ipc_->SetDiagonalBlock(0, invQ);
|
||||
ipc_->owns_blocks = 1;
|
||||
|
||||
// bpcg
|
||||
solver_.reset(new BPCGSolver(M.GetComm(), *ipc_, *ppc_));
|
||||
solver_ = std::make_unique<BPCGSolver>(M.GetComm(), ipc_.get(), ppc_.get());
|
||||
solver_->SetOperator(*oop_);
|
||||
}
|
||||
else
|
||||
{
|
||||
// oop_ unused in cg
|
||||
auto temp_oop = new BlockOperator(offsets_);
|
||||
temp_oop->owns_blocks = false;
|
||||
temp_oop->SetBlock(0, 0, &M);
|
||||
temp_oop->SetBlock(0, 1, Bt);
|
||||
temp_oop->SetBlock(0, 1, Bt_.get());
|
||||
temp_oop->SetBlock(1, 0, &B);
|
||||
|
||||
// ipc_ unused in cg
|
||||
auto temp_ipc = new BlockOperator(offsets_);
|
||||
temp_ipc->owns_blocks = false;
|
||||
temp_ipc->SetDiagonalBlock(0, invQ);
|
||||
temp_ipc->owns_blocks = 1;
|
||||
|
||||
// temp_AN = temp_oop * temp_ipc
|
||||
auto temp_AN = new ProductOperator(temp_oop, temp_ipc, true, true);
|
||||
|
||||
// Required for updating the RHS
|
||||
auto id = new IdentityOperator(M.NumRows()+B.NumRows());
|
||||
map_ = new SumOperator(temp_AN, 1.0, id, -1.0, true, true);
|
||||
map_ = std::make_unique<SumOperator>(temp_AN, 1.0, id, -1.0, true, true);
|
||||
mop_ = std::make_unique<ProductOperator>(map_.get(), temp_oop, false, false);
|
||||
|
||||
mop_ = new ProductOperator(map_, temp_oop, false, true);
|
||||
|
||||
cpc_ = new BlockDiagonalPreconditioner(offsets_);
|
||||
cpc_->owns_blocks = true;
|
||||
cpc_ = std::make_unique<BlockDiagonalPreconditioner>(offsets_);
|
||||
cpc_->SetDiagonalBlock(0, &M0);
|
||||
cpc_->SetDiagonalBlock(1, &M1);
|
||||
|
||||
// (P)CG
|
||||
solver_.reset(new CGSolver(M.GetComm()));
|
||||
solver_ = std::make_unique<CGSolver>(M.GetComm());
|
||||
solver_->SetOperator(*mop_);
|
||||
solver_->SetPreconditioner(*cpc_);
|
||||
}
|
||||
@@ -133,7 +125,7 @@ void BramblePasciakSolver::Init(
|
||||
}
|
||||
|
||||
HypreParMatrix *BramblePasciakSolver::ConstructMassPreconditioner(
|
||||
ParBilinearForm &mVarf, real_t q_scaling)
|
||||
const ParBilinearForm &mVarf, real_t q_scaling)
|
||||
{
|
||||
MFEM_ASSERT((q_scaling > 0.0) && (q_scaling < 1.0),
|
||||
"Invalid Q-scaling factor: q_scaling = " << q_scaling );
|
||||
@@ -167,7 +159,7 @@ HypreParMatrix *BramblePasciakSolver::ConstructMassPreconditioner(
|
||||
Vector x(M_i.Height()), Mx(M_i.Height()), diff(M_i.Height());
|
||||
real_t eval_prev = 0.0;
|
||||
int iter = 0;
|
||||
x.Randomize(696383552+779345*i);
|
||||
x.Randomize(static_cast<int>(696383552LL+779345LL*i));
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
const real_t rel_tol = 1e-12;
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
@@ -400,5 +392,5 @@ void BPCGSolver::Mult(const Vector &b, Vector &x) const
|
||||
final_norm = sqrt(delta);
|
||||
Monitor(final_iter, final_norm, r, x, true);
|
||||
}
|
||||
} // namespace blocksolvers
|
||||
} // namespace mfem
|
||||
|
||||
} // namespace mfem::blocksolvers
|
||||
|
||||
@@ -49,9 +49,7 @@
|
||||
#include "darcy_solver.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace blocksolvers
|
||||
namespace mfem::blocksolvers
|
||||
{
|
||||
|
||||
/// Parameters for the BramblePasciakSolver method
|
||||
@@ -70,11 +68,11 @@ protected:
|
||||
void UpdateVectors();
|
||||
|
||||
public:
|
||||
BPCGSolver(const Operator &ipc, const Operator &ppc) { pprec = &ppc; iprec = &ipc; }
|
||||
BPCGSolver(const Operator *ipc, const Operator *ppc): iprec(ipc), pprec(ppc) {}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
BPCGSolver(MPI_Comm comm_, const Operator &ipc, const Operator &ppc)
|
||||
: IterativeSolver(comm_) { pprec = &ppc; iprec = &ipc; }
|
||||
BPCGSolver(MPI_Comm comm_, const Operator *ipc, const Operator *ppc)
|
||||
: IterativeSolver(comm_), iprec(ipc), pprec(ppc) { }
|
||||
#endif
|
||||
|
||||
void SetOperator(const Operator &op) override
|
||||
@@ -83,11 +81,9 @@ public:
|
||||
void SetPreconditioner(Solver &pc) override
|
||||
{ if (Mpi::Root()) { MFEM_WARNING("SetPreconditioner has no effect on BPCGSolver.\n"); } }
|
||||
|
||||
virtual void SetIncompletePreconditioner(const Operator &ipc)
|
||||
{ iprec = &ipc; }
|
||||
virtual void SetIncompletePreconditioner(const Operator *ipc) { iprec = ipc; }
|
||||
|
||||
virtual void SetParticularPreconditioner(const Operator &ppc)
|
||||
{ pprec = &ppc; }
|
||||
virtual void SetParticularPreconditioner(const Operator *ppc) { pprec = ppc; }
|
||||
|
||||
void Mult(const Vector &b, Vector &x) const override;
|
||||
};
|
||||
@@ -116,23 +112,20 @@ public:
|
||||
1. P. Vassilevski, Multilevel Block Factorization Preconditioners (Appendix
|
||||
F.3), Springer, 2008.
|
||||
|
||||
2. J. Bramble and J. Pasciak. A Preconditioning Technique for Indefinite
|
||||
2. J. Bramble and J. Pasciak. A Preconditioning Technique for Indefinite
|
||||
Systems Resulting From Mixed Approximations of Elliptic Problems,
|
||||
Mathematics of Computation, 50:1-17, 1988. */
|
||||
class BramblePasciakSolver : public DarcySolver
|
||||
{
|
||||
mutable bool use_bpcg;
|
||||
std::unique_ptr<IterativeSolver> solver_;
|
||||
BlockOperator *oop_, *ipc_;
|
||||
ProductOperator *mop_;
|
||||
SumOperator *map_;
|
||||
ProductOperator *ppc_;
|
||||
BlockDiagonalPreconditioner *cpc_;
|
||||
std::unique_ptr<HypreParMatrix> M_;
|
||||
std::unique_ptr<HypreParMatrix> B_;
|
||||
std::unique_ptr<HypreParMatrix> Q_;
|
||||
OperatorPtr M0_;
|
||||
OperatorPtr M1_;
|
||||
std::unique_ptr<BlockOperator> oop_, ipc_;
|
||||
std::unique_ptr<ProductOperator> mop_, ppc_;
|
||||
std::unique_ptr<SumOperator> map_;
|
||||
std::unique_ptr<BlockDiagonalPreconditioner> cpc_;
|
||||
std::unique_ptr<HypreParMatrix> M_, B_, Q_, S_;
|
||||
std::unique_ptr<TransposeOperator> Bt_;
|
||||
OperatorPtr M0_, M1_;
|
||||
Array<int> ess_zero_dofs_;
|
||||
|
||||
void Init(HypreParMatrix &M, HypreParMatrix &B,
|
||||
@@ -142,8 +135,8 @@ class BramblePasciakSolver : public DarcySolver
|
||||
public:
|
||||
/// System and mass preconditioner are constructed from bilinear forms
|
||||
BramblePasciakSolver(
|
||||
ParBilinearForm *mVarf,
|
||||
ParMixedBilinearForm *bVarf,
|
||||
ParBilinearForm &mVarf,
|
||||
ParMixedBilinearForm &bVarf,
|
||||
const BPSParameters ¶m);
|
||||
|
||||
/// System and mass preconditioner are user-provided
|
||||
@@ -158,8 +151,8 @@ public:
|
||||
element T:
|
||||
M_T x_T = lambda_T diag(M_T) x_T.
|
||||
We set Q_T = alpha * min(lambda_T) * diag(M_T), 0 < alpha < 1. */
|
||||
static HypreParMatrix *ConstructMassPreconditioner(ParBilinearForm &mVarf,
|
||||
real_t alpha = 0.5);
|
||||
static HypreParMatrix *ConstructMassPreconditioner(const ParBilinearForm &mVarf,
|
||||
const real_t alpha = 0.5);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void SetOperator(const Operator &op) override { }
|
||||
@@ -167,7 +160,6 @@ public:
|
||||
int GetNumIterations() const override { return solver_->GetNumIterations(); }
|
||||
};
|
||||
|
||||
} // namespace blocksolvers
|
||||
} // namespace mfem
|
||||
} // namespace mfem::blocksolvers
|
||||
|
||||
#endif // MFEM_BP_SOLVER_HPP
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace blocksolvers
|
||||
namespace mfem::blocksolvers
|
||||
{
|
||||
|
||||
void SetOptions(IterativeSolver& solver, const IterSolveParameters& param)
|
||||
{
|
||||
solver.SetPrintLevel(param.print_level);
|
||||
@@ -49,7 +48,7 @@ BDPMinresSolver::BDPMinresSolver(const HypreParMatrix& M,
|
||||
prec_.SetDiagonalBlock(0, new HypreDiagScale(M));
|
||||
prec_.SetDiagonalBlock(1, new HypreBoomerAMG(*S_.As<HypreParMatrix>()));
|
||||
static_cast<HypreBoomerAMG&>(prec_.GetDiagonalBlock(1)).SetPrintLevel(0);
|
||||
prec_.owns_blocks = true;
|
||||
prec_.owns_blocks = 1;
|
||||
|
||||
SetOptions(solver_, param);
|
||||
solver_.SetOperator(op_);
|
||||
@@ -61,5 +60,5 @@ void BDPMinresSolver::Mult(const Vector & x, Vector & y) const
|
||||
solver_.Mult(x, y);
|
||||
for (int dof : ess_zero_dofs_) { y[dof] = 0.0; }
|
||||
}
|
||||
} // namespace blocksolvers
|
||||
} // namespace mfem
|
||||
|
||||
} // namespace mfem::blocksolvers
|
||||
|
||||
@@ -13,13 +13,10 @@
|
||||
#define MFEM_DARCY_SOLVER_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace blocksolvers
|
||||
namespace mfem::blocksolvers
|
||||
{
|
||||
|
||||
struct IterSolveParameters
|
||||
{
|
||||
int print_level = 0;
|
||||
@@ -32,8 +29,6 @@ struct IterSolveParameters
|
||||
real_t rel_tol = 1e-5;
|
||||
#else
|
||||
#error "Only single and double precision are supported!"
|
||||
real_t abs_tol = 1e-12;
|
||||
real_t rel_tol = 1e-9;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -68,7 +63,7 @@ public:
|
||||
void SetEssZeroDofs(const Array<int>& dofs) { dofs.Copy(ess_zero_dofs_); }
|
||||
int GetNumIterations() const override { return solver_.GetNumIterations(); }
|
||||
};
|
||||
} // namespace blocksolvers
|
||||
} // namespace mfem
|
||||
|
||||
} // namespace mfem::blocksolvers
|
||||
|
||||
#endif // MFEM_DARCY_SOLVER_HPP
|
||||
|
||||
@@ -13,16 +13,16 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
namespace mfem::blocksolvers
|
||||
{
|
||||
namespace blocksolvers
|
||||
|
||||
static HypreParMatrix* TwoStepsRAP(const HypreParMatrix *Rt,
|
||||
const HypreParMatrix *A,
|
||||
const HypreParMatrix *P)
|
||||
{
|
||||
HypreParMatrix* TwoStepsRAP(const HypreParMatrix& Rt, const HypreParMatrix& A,
|
||||
const HypreParMatrix& P)
|
||||
{
|
||||
OperatorPtr R(Rt.Transpose());
|
||||
OperatorPtr RA(ParMult(R.As<HypreParMatrix>(), &A));
|
||||
return ParMult(RA.As<HypreParMatrix>(), &P, true);
|
||||
OperatorPtr R(Rt->Transpose());
|
||||
OperatorPtr RA(ParMult(R.As<HypreParMatrix>(), A));
|
||||
return ParMult(RA.As<HypreParMatrix>(), P, true);
|
||||
}
|
||||
|
||||
void GetRowColumnsRef(const SparseMatrix& A, int row, Array<int>& cols)
|
||||
@@ -59,34 +59,36 @@ DFSSpaces::DFSSpaces(int order, int num_refine, ParMesh *mesh,
|
||||
|
||||
if (mesh->Dimension() == 3)
|
||||
{
|
||||
hcurl_fec_.reset(new ND_FECollection(order+1, mesh->Dimension()));
|
||||
hcurl_fec_ = std::make_unique<ND_FECollection>(order+1, mesh->Dimension());
|
||||
}
|
||||
else
|
||||
{
|
||||
hcurl_fec_.reset(new H1_FECollection(order+1, mesh->Dimension()));
|
||||
hcurl_fec_ = std::make_unique<H1_FECollection>(order+1, mesh->Dimension());
|
||||
}
|
||||
|
||||
all_bdr_attr_.SetSize(ess_attr.Size(), 1);
|
||||
hdiv_fes_.reset(new ParFiniteElementSpace(mesh, &hdiv_fec_));
|
||||
l2_fes_.reset(new ParFiniteElementSpace(mesh, &l2_fec_));
|
||||
coarse_hdiv_fes_.reset(new ParFiniteElementSpace(*hdiv_fes_));
|
||||
coarse_l2_fes_.reset(new ParFiniteElementSpace(*l2_fes_));
|
||||
l2_0_fes_.reset(new ParFiniteElementSpace(mesh, &l2_0_fec_));
|
||||
hdiv_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &hdiv_fec_);
|
||||
l2_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &l2_fec_);
|
||||
coarse_hdiv_fes_ = std::make_unique<ParFiniteElementSpace>(*hdiv_fes_);
|
||||
coarse_l2_fes_ = std::make_unique<ParFiniteElementSpace>(*l2_fes_);
|
||||
l2_0_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &l2_0_fec_);
|
||||
l2_0_fes_->SetUpdateOperatorType(Operator::MFEM_SPARSEMAT);
|
||||
el_l2dof_.reserve(num_refine+1);
|
||||
el_l2dof_.push_back(ElemToDof(*coarse_l2_fes_));
|
||||
|
||||
data_.agg_hdivdof.resize(num_refine);
|
||||
data_.agg_l2dof.resize(num_refine);
|
||||
data_.P_hdiv.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
|
||||
data_.P_l2.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
|
||||
data_.P_hdiv.resize(num_refine);
|
||||
data_.P_l2.resize(num_refine);
|
||||
|
||||
data_.Q_l2.resize(num_refine);
|
||||
hdiv_fes_->GetEssentialTrueDofs(ess_attr, data_.coarsest_ess_hdivdofs);
|
||||
data_.C.resize(num_refine+1);
|
||||
data_.Ae.resize(num_refine+1);
|
||||
|
||||
hcurl_fes_.reset(new ParFiniteElementSpace(mesh, hcurl_fec_.get()));
|
||||
coarse_hcurl_fes_.reset(new ParFiniteElementSpace(*hcurl_fes_));
|
||||
data_.P_hcurl.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
|
||||
hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, hcurl_fec_.get());
|
||||
coarse_hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(*hcurl_fes_);
|
||||
data_.P_hcurl.resize(num_refine);
|
||||
}
|
||||
|
||||
SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
|
||||
@@ -104,8 +106,8 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
|
||||
agg_tdof_T.As<HypreParMatrix>()->GetDiag(tdof_agg);
|
||||
agg_tdof_T.As<HypreParMatrix>()->GetOffd(is_shared, trash);
|
||||
|
||||
int * I = new int [tdof_agg.NumRows()+1]();
|
||||
int * J = new int[tdof_agg.NumNonZeroElems()];
|
||||
int *I = new int[tdof_agg.NumRows()+1]();
|
||||
int *J = new int[tdof_agg.NumNonZeroElems()];
|
||||
|
||||
Array<int> is_bdr;
|
||||
FiniteElementSpace::ListToMarker(bdr_truedofs, tdof_agg.NumRows(), is_bdr);
|
||||
@@ -119,7 +121,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
|
||||
J[counter++] = tdof_agg.GetRowColumns(i)[0];
|
||||
}
|
||||
|
||||
real_t * D = new real_t[I[tdof_agg.NumRows()]];
|
||||
auto *D = new real_t[I[tdof_agg.NumRows()]];
|
||||
std::fill_n(D, I[tdof_agg.NumRows()], 1.0);
|
||||
|
||||
SparseMatrix intdof_agg(I, J, D, tdof_agg.NumRows(), tdof_agg.NumCols());
|
||||
@@ -146,20 +148,21 @@ void DFSSpaces::MakeDofRelationTables(int level)
|
||||
|
||||
void DFSSpaces::CollectDFSData()
|
||||
{
|
||||
auto GetP = [this](OperatorPtr& P, unique_ptr<ParFiniteElementSpace>& cfes,
|
||||
ParFiniteElementSpace& fes, bool remove_zero)
|
||||
auto GetP = [&](std::unique_ptr<OperatorPtr> &P,
|
||||
std::unique_ptr<ParFiniteElementSpace> &cfes,
|
||||
ParFiniteElementSpace& fes, const bool remove_zero)
|
||||
{
|
||||
fes.Update();
|
||||
fes.GetTrueTransferOperator(*cfes, P);
|
||||
if (remove_zero)
|
||||
{
|
||||
P.As<HypreParMatrix>()->DropSmallEntries(1e-16);
|
||||
}
|
||||
auto T = new OperatorHandle(Operator::Hypre_ParCSR);
|
||||
fes.GetTrueTransferOperator(*cfes, *T);
|
||||
P.reset(T);
|
||||
if (remove_zero) { P->As<HypreParMatrix>()->DropSmallEntries(1e-16); }
|
||||
(level_ < (int)data_.P_l2.size()-1) ? cfes->Update() : cfes.reset();
|
||||
};
|
||||
|
||||
GetP(data_.P_hdiv[level_], coarse_hdiv_fes_, *hdiv_fes_, true);
|
||||
GetP(data_.P_l2[level_], coarse_l2_fes_, *l2_fes_, false);
|
||||
|
||||
MakeDofRelationTables(level_);
|
||||
|
||||
GetP(data_.P_hcurl[level_], coarse_hcurl_fes_, *hcurl_fes_, true);
|
||||
@@ -171,7 +174,9 @@ void DFSSpaces::CollectDFSData()
|
||||
data_.C[level_+1].Reset(curl.ParallelAssemble());
|
||||
mfem::Array<int> ess_hcurl_tdof;
|
||||
hcurl_fes_->GetEssentialTrueDofs(ess_bdr_attr_, ess_hcurl_tdof);
|
||||
data_.C[level_+1].As<HypreParMatrix>()->EliminateCols(ess_hcurl_tdof);
|
||||
data_.Ae[level_+1].reset(
|
||||
data_.C[level_+1].As<HypreParMatrix>()
|
||||
->EliminateCols(ess_hcurl_tdof));
|
||||
|
||||
++level_;
|
||||
|
||||
@@ -189,7 +194,7 @@ void DFSSpaces::DataFinalize()
|
||||
SparseMatrix P_l2;
|
||||
for (int l = (int)data_.P_l2.size()-1; l >= 0; --l)
|
||||
{
|
||||
data_.P_l2[l].As<HypreParMatrix>()->GetDiag(P_l2);
|
||||
data_.P_l2[l]->As<HypreParMatrix>()->GetDiag(P_l2);
|
||||
OperatorPtr PT_l2(Transpose(P_l2));
|
||||
auto PTW = Mult(*PT_l2.As<SparseMatrix>(), *W.As<SparseMatrix>());
|
||||
auto cW = Mult(*PTW, P_l2);
|
||||
@@ -245,7 +250,7 @@ SaddleSchwarzSmoother::SaddleSchwarzSmoother(const HypreParMatrix& M,
|
||||
const SparseMatrix& agg_hdivdof,
|
||||
const SparseMatrix& agg_l2dof,
|
||||
const HypreParMatrix& P_l2,
|
||||
const HypreParMatrix& Q_l2)
|
||||
const ProductOperator& Q_l2)
|
||||
: Solver(M.NumRows() + B.NumRows()), agg_hdivdof_(agg_hdivdof),
|
||||
agg_l2dof_(agg_l2dof), solvers_loc_(agg_l2dof.NumRows())
|
||||
{
|
||||
@@ -312,23 +317,27 @@ void SaddleSchwarzSmoother::Mult(const Vector & x, Vector & y) const
|
||||
blk_y.GetBlock(1) -= coarse_l2_projection;
|
||||
}
|
||||
|
||||
DivFreeSolver::DivFreeSolver(const HypreParMatrix &M, const HypreParMatrix& B,
|
||||
DivFreeSolver::DivFreeSolver(const HypreParMatrix &M,
|
||||
const HypreParMatrix &B,
|
||||
const DFSData& data)
|
||||
: DarcySolver(M.NumRows(), B.NumRows()), data_(data), param_(data.param),
|
||||
BT_(B.Transpose()), BBT_solver_(B, param_.BBT_solve_param),
|
||||
ops_offsets_(data.P_l2.size()+1), ops_(ops_offsets_.size()),
|
||||
blk_Ps_(ops_.Size()-1), smoothers_(ops_.Size())
|
||||
BT_(B.Transpose()),
|
||||
BBT_solver_(B, param_.BBT_solve_param),
|
||||
ops_offsets_(data.P_l2.size()+1),
|
||||
ops_(ops_offsets_.size()),
|
||||
blk_Ps_(ops_.size()-1),
|
||||
smoothers_(ops_.size())
|
||||
{
|
||||
ops_offsets_.back().MakeRef(DarcySolver::offsets_);
|
||||
ops_.Last() = new BlockOperator(ops_offsets_.back());
|
||||
ops_.Last()->SetBlock(0, 0, const_cast<HypreParMatrix*>(&M));
|
||||
ops_.Last()->SetBlock(1, 0, const_cast<HypreParMatrix*>(&B));
|
||||
ops_.Last()->SetBlock(0, 1, BT_.Ptr());
|
||||
ops_.back() = std::make_unique<BlockOperator>(ops_offsets_.back());
|
||||
ops_.back()->SetBlock(0, 0, const_cast<HypreParMatrix*>(&M));
|
||||
ops_.back()->SetBlock(1, 0, const_cast<HypreParMatrix*>(&B));
|
||||
ops_.back()->SetBlock(0, 1, BT_.Ptr());
|
||||
|
||||
for (int l = data.P_l2.size(); l >= 0; --l)
|
||||
{
|
||||
auto& M_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(0, 0));
|
||||
auto& B_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(1, 0));
|
||||
auto &M_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(0, 0));
|
||||
auto &B_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(1, 0));
|
||||
|
||||
if (l == 0)
|
||||
{
|
||||
@@ -343,123 +352,112 @@ DivFreeSolver::DivFreeSolver(const HypreParMatrix &M, const HypreParMatrix& B,
|
||||
|
||||
const IterSolveParameters& param = param_.coarse_solve_param;
|
||||
auto coarse_solver = new BDPMinresSolver(M_f, B_f, param);
|
||||
if (ops_.Size() > 1)
|
||||
if (ops_.size() > 1)
|
||||
{
|
||||
coarse_solver->SetEssZeroDofs(data.coarsest_ess_hdivdofs);
|
||||
}
|
||||
smoothers_[l] = coarse_solver;
|
||||
smoothers_[l].reset(coarse_solver);
|
||||
continue;
|
||||
}
|
||||
|
||||
HypreParMatrix& P_hdiv_l = *data.P_hdiv[l-1].As<HypreParMatrix>();
|
||||
HypreParMatrix& P_l2_l = *data.P_l2[l-1].As<HypreParMatrix>();
|
||||
auto P_hdiv_l = data.P_hdiv[l-1]->As<HypreParMatrix>();
|
||||
auto P_l2_l = data.P_l2[l-1]->As<HypreParMatrix>();
|
||||
SparseMatrix& agg_hdivdof_l = *data.agg_hdivdof[l-1].As<SparseMatrix>();
|
||||
SparseMatrix& agg_l2dof_l = *data.agg_l2dof[l-1].As<SparseMatrix>();
|
||||
HypreParMatrix& Q_l2_l = *data.Q_l2[l-1].As<HypreParMatrix>();
|
||||
HypreParMatrix* C_l = data.C[l].As<HypreParMatrix>();
|
||||
ProductOperator& Q_l2_l = *data.Q_l2[l-1].As<ProductOperator>();
|
||||
auto* C_l = data.C[l].As<HypreParMatrix>();
|
||||
|
||||
auto S0 = new SaddleSchwarzSmoother(M_f, B_f, agg_hdivdof_l,
|
||||
agg_l2dof_l, P_l2_l, Q_l2_l);
|
||||
agg_l2dof_l, *P_l2_l, Q_l2_l);
|
||||
if (param_.coupled_solve)
|
||||
{
|
||||
auto S1 = new BlockDiagonalPreconditioner(ops_offsets_[l]);
|
||||
S1->SetDiagonalBlock(0, new AuxSpaceSmoother(M_f, C_l));
|
||||
S1->owns_blocks = true;
|
||||
smoothers_[l] = new ProductSolver(ops_[l], S0, S1, false, true, true);
|
||||
S1->owns_blocks = 1;
|
||||
smoothers_[l] =
|
||||
std::make_unique<ProductSolver>(ops_[l].get(), S0, S1, false, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
smoothers_[l] = S0;
|
||||
smoothers_[l].reset(S0);
|
||||
}
|
||||
|
||||
HypreParMatrix* M_c = TwoStepsRAP(P_hdiv_l, M_f, P_hdiv_l);
|
||||
HypreParMatrix* B_c = TwoStepsRAP(P_l2_l, B_f, P_hdiv_l);
|
||||
HypreParMatrix* M_c = TwoStepsRAP(P_hdiv_l, &M_f, P_hdiv_l);
|
||||
HypreParMatrix* B_c = TwoStepsRAP(P_l2_l, &B_f, P_hdiv_l);
|
||||
|
||||
ops_offsets_[l-1].SetSize(3, 0);
|
||||
ops_offsets_[l-1][1] = M_c->NumRows();
|
||||
ops_offsets_[l-1][2] = M_c->NumRows() + B_c->NumRows();
|
||||
|
||||
blk_Ps_[l-1] = new BlockOperator(ops_offsets_[l], ops_offsets_[l-1]);
|
||||
blk_Ps_[l-1]->SetBlock(0, 0, &P_hdiv_l);
|
||||
blk_Ps_[l-1]->SetBlock(1, 1, &P_l2_l);
|
||||
blk_Ps_[l-1] =
|
||||
std::make_unique<BlockOperator>(ops_offsets_[l], ops_offsets_[l-1]);
|
||||
blk_Ps_[l-1]->SetBlock(0, 0, P_hdiv_l);
|
||||
blk_Ps_[l-1]->SetBlock(1, 1, P_l2_l);
|
||||
|
||||
ops_[l-1] = new BlockOperator(ops_offsets_[l-1]);
|
||||
ops_[l-1] =
|
||||
std::make_unique<BlockOperator>(ops_offsets_[l-1]);
|
||||
ops_[l-1]->SetBlock(0, 0, M_c);
|
||||
ops_[l-1]->SetBlock(1, 0, B_c);
|
||||
ops_[l-1]->SetBlock(0, 1, B_c->Transpose());
|
||||
ops_[l-1]->owns_blocks = true;
|
||||
ops_[l-1]->owns_blocks = 1;
|
||||
}
|
||||
|
||||
Array<bool> own_ops(ops_.Size());
|
||||
Array<bool> own_smoothers(smoothers_.Size());
|
||||
Array<bool> own_Ps(blk_Ps_.Size());
|
||||
own_ops = true;
|
||||
own_smoothers = true;
|
||||
own_Ps = true;
|
||||
|
||||
if (data_.P_l2.size() == 0) { return; }
|
||||
|
||||
Array<bool> own_ops(ops_.size());
|
||||
Array<bool> own_smoothers(smoothers_.size());
|
||||
Array<bool> own_blk_Ps(blk_Ps_.size());
|
||||
own_ops = false, own_smoothers = false, own_blk_Ps = false;
|
||||
|
||||
Array<Solver*> smoothers(smoothers_.size());
|
||||
|
||||
if (param_.coupled_solve)
|
||||
{
|
||||
solver_.Reset(new GMRESSolver(B.GetComm()));
|
||||
solver_.As<GMRESSolver>()->SetOperator(*(ops_.Last()));
|
||||
prec_.Reset(new Multigrid(ops_, smoothers_, blk_Ps_,
|
||||
own_ops, own_smoothers, own_Ps));
|
||||
solver_.As<GMRESSolver>()->SetOperator(*(ops_.back()));
|
||||
Array<BlockOperator*> ops(ops_.size()), blk_Ps(blk_Ps_.size());
|
||||
for (size_t i = 0; i < ops_.size(); ++i) { ops[i] = ops_[i].get(); }
|
||||
for (size_t i = 0; i < blk_Ps_.size(); ++i) { blk_Ps[i] = blk_Ps_[i].get(); }
|
||||
for (size_t i = 0; i < smoothers_.size(); ++i) { smoothers[i] = smoothers_[i].get(); }
|
||||
prec_.Reset(new Multigrid(ops, smoothers, blk_Ps,
|
||||
own_ops, own_smoothers, own_blk_Ps));
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<HypreParMatrix*> ops(data_.P_hcurl.size()+1);
|
||||
Array<Solver*> smoothers(ops.Size());
|
||||
Array<HypreParMatrix*> Ps(data_.P_hcurl.size());
|
||||
own_Ps = false;
|
||||
|
||||
HypreParMatrix& C_finest = *data.C.back().As<HypreParMatrix>();
|
||||
ops.Last() = TwoStepsRAP(C_finest, M, C_finest);
|
||||
auto C_finest = data.C.back().As<HypreParMatrix>();
|
||||
ops.Last() = TwoStepsRAP(C_finest, &M, C_finest);
|
||||
ops.Last()->EliminateZeroRows();
|
||||
ops.Last()->DropSmallEntries(1e-14);
|
||||
|
||||
solver_.Reset(new CGSolver(B.GetComm()));
|
||||
solver_.As<CGSolver>()->SetOperator(*ops.Last());
|
||||
smoothers.Last() = new HypreSmoother(*ops.Last());
|
||||
static_cast<HypreSmoother*>(smoothers.Last())->SetOperatorSymmetry(true);
|
||||
|
||||
for (int l = Ps.Size()-1; l >= 0; --l)
|
||||
{
|
||||
Ps[l] = data_.P_hcurl[l].As<HypreParMatrix>();
|
||||
ops[l] = TwoStepsRAP(*Ps[l], *ops[l+1], *Ps[l]);
|
||||
Ps[l] = data_.P_hcurl[l]->As<HypreParMatrix>();
|
||||
ops[l] = TwoStepsRAP(Ps[l], ops[l+1], Ps[l]);
|
||||
ops[l]->DropSmallEntries(1e-14);
|
||||
smoothers[l] = new HypreSmoother(*ops[l]);
|
||||
static_cast<HypreSmoother*>(smoothers[l])->SetOperatorSymmetry(true);
|
||||
}
|
||||
|
||||
prec_.Reset(new Multigrid(ops, smoothers, Ps, own_ops, own_smoothers, own_Ps));
|
||||
own_ops = true, own_smoothers = true;
|
||||
prec_.Reset(new Multigrid(ops, smoothers, Ps,
|
||||
own_ops, own_smoothers, own_blk_Ps));
|
||||
}
|
||||
|
||||
solver_.As<IterativeSolver>()->SetPreconditioner(*prec_.As<Solver>());
|
||||
SetOptions(*solver_.As<IterativeSolver>(), param_);
|
||||
}
|
||||
|
||||
DivFreeSolver::~DivFreeSolver()
|
||||
{
|
||||
if (param_.coupled_solve) { return; }
|
||||
for (int i = 0; i < ops_.Size(); ++i)
|
||||
{
|
||||
delete ops_[i];
|
||||
delete smoothers_[i];
|
||||
if (i == ops_.Size() - 1) { break; }
|
||||
delete blk_Ps_[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DivFreeSolver::SolveParticular(const Vector& rhs, Vector& sol) const
|
||||
{
|
||||
std::vector<Vector> rhss(smoothers_.Size());
|
||||
std::vector<Vector> sols(smoothers_.Size());
|
||||
|
||||
std::vector<Vector> rhss(smoothers_.size()), sols(smoothers_.size());
|
||||
rhss.back().SetDataAndSize(const_cast<real_t*>(rhs.HostRead()), rhs.Size());
|
||||
sols.back().SetDataAndSize(sol.HostWrite(), sol.Size());
|
||||
|
||||
for (int l = blk_Ps_.Size()-1; l >= 0; --l)
|
||||
for (int l = blk_Ps_.size()-1; l >= 0; --l)
|
||||
{
|
||||
rhss[l].SetSize(blk_Ps_[l]->NumCols());
|
||||
sols[l].SetSize(blk_Ps_[l]->NumCols());
|
||||
@@ -470,12 +468,12 @@ void DivFreeSolver::SolveParticular(const Vector& rhs, Vector& sol) const
|
||||
blk_Ps_[l]->MultTranspose(rhss[l+1], rhss[l]);
|
||||
}
|
||||
|
||||
for (int l = 0; l < smoothers_.Size(); ++l)
|
||||
for (size_t l = 0; l < smoothers_.size(); ++l)
|
||||
{
|
||||
smoothers_[l]->Mult(rhss[l], sols[l]);
|
||||
}
|
||||
|
||||
for (int l = 0; l < blk_Ps_.Size(); ++l)
|
||||
for (size_t l = 0; l < blk_Ps_.size(); ++l)
|
||||
{
|
||||
Vector P_sol(blk_Ps_[l]->NumRows());
|
||||
blk_Ps_[l]->Mult(sols[l], P_sol);
|
||||
@@ -507,12 +505,12 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
|
||||
MFEM_VERIFY(x.Size() == offsets_[2], "MLDivFreeSolver: x size is invalid");
|
||||
MFEM_VERIFY(y.Size() == offsets_[2], "MLDivFreeSolver: y size is invalid");
|
||||
|
||||
if (ops_.Size() == 1) { smoothers_[0]->Mult(x, y); return; }
|
||||
if (ops_.size() == 1) { smoothers_[0]->Mult(x, y); return; }
|
||||
|
||||
BlockVector blk_y(y, offsets_);
|
||||
|
||||
BlockVector resid(offsets_);
|
||||
ops_.Last()->Mult(y, resid);
|
||||
ops_.back()->Mult(y, resid);
|
||||
add(1.0, x, -1.0, resid, resid);
|
||||
|
||||
BlockVector correction(offsets_);
|
||||
@@ -539,7 +537,7 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
|
||||
ch.Clear();
|
||||
ch.Start();
|
||||
|
||||
ops_.Last()->Mult(y, resid);
|
||||
ops_.back()->Mult(y, resid);
|
||||
add(1.0, x, -1.0, resid, resid);
|
||||
|
||||
SolveDivFree(resid.GetBlock(0), correction.GetBlock(0));
|
||||
@@ -553,7 +551,7 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
|
||||
ch.Clear();
|
||||
ch.Start();
|
||||
|
||||
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.Last()->GetBlock(0, 0));
|
||||
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.back()->GetBlock(0, 0));
|
||||
M.Mult(-1.0, correction.GetBlock(0), 1.0, resid.GetBlock(0));
|
||||
SolvePotential(resid.GetBlock(0), correction.GetBlock(1));
|
||||
blk_y.GetBlock(1) += correction.GetBlock(1);
|
||||
@@ -567,11 +565,12 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
|
||||
|
||||
int DivFreeSolver::GetNumIterations() const
|
||||
{
|
||||
if (ops_.Size() == 1)
|
||||
if (ops_.size() == 1)
|
||||
{
|
||||
return static_cast<BDPMinresSolver*>(smoothers_[0])->GetNumIterations();
|
||||
return static_cast<BDPMinresSolver*>
|
||||
(smoothers_.at(0).get())->GetNumIterations();
|
||||
}
|
||||
return solver_.As<IterativeSolver>()->GetNumIterations();
|
||||
}
|
||||
} // namespace blocksolvers
|
||||
} // namespace mfem
|
||||
|
||||
} // namespace mfem::blocksolvers
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
#define MFEM_DIVFREE_SOLVER_HPP
|
||||
|
||||
#include "darcy_solver.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace blocksolvers
|
||||
namespace mfem::blocksolvers
|
||||
{
|
||||
|
||||
/// Parameters for the divergence free solver
|
||||
struct DFSParameters : IterSolveParameters
|
||||
{
|
||||
@@ -35,14 +35,18 @@ struct DFSParameters : IterSolveParameters
|
||||
/// Data for the divergence free solver
|
||||
struct DFSData
|
||||
{
|
||||
std::vector<OperatorPtr> agg_hdivdof; // agglomerates to H(div) dofs table
|
||||
std::vector<OperatorPtr> agg_l2dof; // agglomerates to L2 dofs table
|
||||
std::vector<OperatorPtr> P_hdiv; // Interpolation matrix for H(div) space
|
||||
std::vector<OperatorPtr> P_l2; // Interpolation matrix for L2 space
|
||||
std::vector<OperatorPtr> P_hcurl; // Interpolation for kernel space of div
|
||||
std::vector<OperatorPtr> Q_l2; // Q_l2[l] = (W_{l+1})^{-1} P_l2[l]^T W_l
|
||||
Array<int> coarsest_ess_hdivdofs; // coarsest level essential H(div) dofs
|
||||
std::vector<OperatorPtr> C; // discrete curl: ND -> RT, map to Null(B)
|
||||
using UniqueOperatorPtr = std::unique_ptr<OperatorPtr>;
|
||||
using UniqueHypreParMatrix = std::unique_ptr<HypreParMatrix>;
|
||||
|
||||
std::vector<OperatorPtr> agg_hdivdof; // agglomerates to H(div) dofs table
|
||||
std::vector<OperatorPtr> agg_l2dof; // agglomerates to L2 dofs table
|
||||
std::vector<UniqueOperatorPtr> P_hdiv; // Interpolation matrix for H(div) space
|
||||
std::vector<UniqueOperatorPtr> P_l2; // Interpolation matrix for L2 space
|
||||
std::vector<UniqueOperatorPtr> P_hcurl; // Interpolation for kernel space of div
|
||||
std::vector<OperatorPtr> Q_l2; // Q_l2[l] = (W_{l+1})^{-1} P_l2[l]^T W_l
|
||||
Array<int> coarsest_ess_hdivdofs; // coarsest level essential H(div) dofs
|
||||
std::vector<OperatorPtr> C; // discrete curl: ND -> RT, map to Null(B)
|
||||
std::vector<UniqueHypreParMatrix> Ae;
|
||||
DFSParameters param;
|
||||
};
|
||||
|
||||
@@ -92,8 +96,7 @@ public:
|
||||
/// Compute the product B * B^T and solve it with CG preconditioned by BoomerAMG
|
||||
class BBTSolver : public Solver
|
||||
{
|
||||
OperatorPtr BBT_;
|
||||
OperatorPtr BBT_prec_;
|
||||
OperatorPtr BBT_, BBT_prec_;
|
||||
CGSolver BBT_solver_;
|
||||
public:
|
||||
BBTSolver(const HypreParMatrix &B, IterSolveParameters param);
|
||||
@@ -115,14 +118,11 @@ public:
|
||||
/// [ B 0 ]
|
||||
class SaddleSchwarzSmoother : public Solver
|
||||
{
|
||||
const SparseMatrix& agg_hdivdof_;
|
||||
const SparseMatrix& agg_l2dof_;
|
||||
const SparseMatrix &agg_hdivdof_, &agg_l2dof_;
|
||||
OperatorPtr coarse_l2_projector_;
|
||||
|
||||
Array<int> offsets_;
|
||||
mutable Array<int> offsets_loc_;
|
||||
mutable Array<int> hdivdofs_loc_;
|
||||
mutable Array<int> l2dofs_loc_;
|
||||
mutable Array<int> offsets_loc_, hdivdofs_loc_, l2dofs_loc_;
|
||||
std::vector<OperatorPtr> solvers_loc_;
|
||||
public:
|
||||
/** SaddleSchwarzSmoother solves local saddle point problems defined on a
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
const SparseMatrix& agg_hdivdof,
|
||||
const SparseMatrix& agg_l2dof,
|
||||
const HypreParMatrix& P_l2,
|
||||
const HypreParMatrix& Q_l2);
|
||||
const ProductOperator& Q_l2);
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void MultTranspose(const Vector &x, Vector &y) const override { Mult(x, y); }
|
||||
void SetOperator(const Operator &op) override { }
|
||||
@@ -178,11 +178,10 @@ class DivFreeSolver : public DarcySolver
|
||||
OperatorPtr BT_;
|
||||
BBTSolver BBT_solver_;
|
||||
std::vector<Array<int>> ops_offsets_;
|
||||
Array<BlockOperator*> ops_;
|
||||
Array<BlockOperator*> blk_Ps_;
|
||||
Array<Solver*> smoothers_;
|
||||
OperatorPtr prec_;
|
||||
OperatorPtr solver_;
|
||||
std::vector<std::unique_ptr<BlockOperator>> ops_;
|
||||
std::vector<std::unique_ptr<BlockOperator>> blk_Ps_;
|
||||
std::vector<std::unique_ptr<Solver>> smoothers_;
|
||||
OperatorPtr prec_, solver_;
|
||||
|
||||
void SolveParticular(const Vector& rhs, Vector& sol) const;
|
||||
void SolveDivFree(const Vector& rhs, Vector& sol) const;
|
||||
@@ -190,14 +189,11 @@ class DivFreeSolver : public DarcySolver
|
||||
public:
|
||||
DivFreeSolver(const HypreParMatrix& M, const HypreParMatrix &B,
|
||||
const DFSData& data);
|
||||
~DivFreeSolver();
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
void SetOperator(const Operator &op) override { }
|
||||
int GetNumIterations() const override;
|
||||
};
|
||||
|
||||
} // namespace blocksolvers
|
||||
|
||||
} // namespace mfem
|
||||
} // namespace mfem::blocksolvers
|
||||
|
||||
#endif // MFEM_DIVFREE_SOLVER_HPP
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
// (2D random field with anisotropy)
|
||||
// mpirun -np 4 generate_random_field -o 1 -r 3 -rp 3 -nu 4 -l1 0.09 -l2 0.03 -l3 0.05 -s 0.01 -t 0.08 -top 1 -no-rs -m ../../data/ref-square.mesh
|
||||
|
||||
#include <math.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "mfem.hpp"
|
||||
@@ -260,7 +258,8 @@ int main(int argc, char *argv[])
|
||||
// III.3 Solve the SPDE problem
|
||||
spde::SPDESolver solver(nu, bc, &fespace, l1, l2, l3, e1, e2,
|
||||
e3);
|
||||
const int seed = (random_seed) ? 0 : std::numeric_limits<int>::max();
|
||||
const int seed = (random_seed) ? 0 :
|
||||
std::numeric_limits<int>::max() - Mpi::WorldRank();
|
||||
solver.SetupRandomFieldGenerator(seed);
|
||||
solver.GenerateRandomField(u);
|
||||
|
||||
|
||||
@@ -133,8 +133,11 @@ int main(int argc, char *argv[])
|
||||
u.Save(sol_ofs);
|
||||
}
|
||||
|
||||
soutv << "keys '.0" << std::string((int)b, '0') << "'\n" << flush;
|
||||
south << "keys '.0" << std::string((int)a, '0') << "'\n" << flush;
|
||||
if (visualization)
|
||||
{
|
||||
soutv << "keys '.0" << std::string((int)b, '0') << "'\n" << flush;
|
||||
south << "keys '.0" << std::string((int)a, '0') << "'\n" << flush;
|
||||
}
|
||||
|
||||
cout << "Which direction(s) are the two curves spinning in?\n";
|
||||
|
||||
|
||||
+81
-45
@@ -32,6 +32,7 @@ set(UNIT_TESTS_SRCS
|
||||
linalg/test_chebyshev.cpp
|
||||
linalg/test_complex_dense_matrix.cpp
|
||||
linalg/test_complex_operator.cpp
|
||||
linalg/test_complex_vector.cpp
|
||||
linalg/test_constrainedsolver.cpp
|
||||
linalg/test_direct_solvers.cpp
|
||||
linalg/test_hypre_ilu.cpp
|
||||
@@ -179,25 +180,42 @@ if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# SERIAL CUDA TESTS: cunit_tests
|
||||
# SERIAL CUDA TESTS: gpu_unit_tests
|
||||
#-----------------------------------------------------------
|
||||
# Create CUDA 'cunit_tests' executable and test
|
||||
# Create CUDA executable and test
|
||||
if (MFEM_USE_CUDA)
|
||||
set(CUNIT_TESTS_SRCS cunit_test_main.cpp)
|
||||
set_property(SOURCE ${CUNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
|
||||
mfem_add_executable(cunit_tests ${CUNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
|
||||
target_link_libraries(cunit_tests mfem)
|
||||
add_dependencies(cunit_tests copy_data)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} cunit_tests)
|
||||
# gpu_unit_tests
|
||||
set(GPU_UNIT_TESTS_SRCS gpu_unit_test_main.cpp)
|
||||
set_property(SOURCE ${GPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
|
||||
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
|
||||
target_link_libraries(gpu_unit_tests mfem)
|
||||
add_dependencies(gpu_unit_tests copy_data)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME cunit_tests COMMAND cunit_tests)
|
||||
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# SERIAL HIP TESTS: gpu_unit_tests
|
||||
#-----------------------------------------------------------
|
||||
# Create HIP 'gpu_unit_tests' executable and test
|
||||
if (MFEM_USE_HIP)
|
||||
# gpu_unit_tests
|
||||
set(GPU_UNIT_TESTS_SRCS gpu_unit_test_main.cpp)
|
||||
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
|
||||
target_link_libraries(gpu_unit_tests mfem)
|
||||
add_dependencies(gpu_unit_tests copy_data)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# SERIAL SEDOV + TMOP TESTS:
|
||||
# sedov_tests_{cpu,debug,cuda,cuda_uvm}
|
||||
# tmop_pa_tests_{cpu,debug,cuda}
|
||||
# sedov_tests_{cpu,debug,gpu,gpu_uvm}
|
||||
# tmop_pa_tests_{cpu,debug,gpu}
|
||||
#-----------------------------------------------------------
|
||||
# Function to add one device serial test from the tests/unit/miniapp directory.
|
||||
# All device unit tests are built into a separate executable, in order to be
|
||||
@@ -226,27 +244,27 @@ function(add_serial_miniapp_test name test_uvm)
|
||||
add_test(NAME ${name}_tests_debug COMMAND ${name}_tests_debug)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CUDA)
|
||||
mfem_add_executable(${name}_tests_cuda ${${NAME}_TESTS_SRCS})
|
||||
target_compile_definitions(${name}_tests_cuda PUBLIC MFEM_${NAME}_DEVICE="cuda")
|
||||
target_link_libraries(${name}_tests_cuda mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_cuda)
|
||||
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
|
||||
mfem_add_executable(${name}_tests_gpu ${${NAME}_TESTS_SRCS})
|
||||
target_compile_definitions(${name}_tests_gpu PUBLIC MFEM_${NAME}_DEVICE="gpu")
|
||||
target_link_libraries(${name}_tests_gpu mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_gpu)
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME ${name}_tests_cuda COMMAND ${name}_tests_cuda)
|
||||
add_test(NAME ${name}_tests_gpu COMMAND ${name}_tests_gpu)
|
||||
endif()
|
||||
|
||||
if (test_uvm)
|
||||
mfem_add_executable(${name}_tests_cuda_uvm ${${NAME}_TESTS_SRCS})
|
||||
target_compile_definitions(${name}_tests_cuda_uvm PUBLIC
|
||||
MFEM_${NAME}_DEVICE="cuda:uvm")
|
||||
target_link_libraries(${name}_tests_cuda_uvm mfem)
|
||||
mfem_add_executable(${name}_tests_gpu_uvm ${${NAME}_TESTS_SRCS})
|
||||
target_compile_definitions(${name}_tests_gpu_uvm PUBLIC
|
||||
MFEM_${NAME}_DEVICE="gpu:uvm")
|
||||
target_link_libraries(${name}_tests_gpu_uvm mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME}
|
||||
${name}_tests_cuda_uvm)
|
||||
${name}_tests_gpu_uvm)
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME ${name}_tests_cuda_uvm COMMAND ${name}_tests_cuda_uvm)
|
||||
add_test(NAME ${name}_tests_gpu_uvm COMMAND ${name}_tests_gpu_uvm)
|
||||
endif()
|
||||
endif()
|
||||
endif(MFEM_USE_CUDA)
|
||||
endif()
|
||||
endfunction(add_serial_miniapp_test)
|
||||
|
||||
add_serial_miniapp_test(sedov ON) # UVM ON
|
||||
@@ -282,10 +300,11 @@ if (MFEM_USE_CEED)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# PARALLEL CPU AND CUDA TESTS: {p,pc}unit_tests
|
||||
# PARALLEL CPU AND CUDA TESTS: {p,pc}unit_tests and pgpu_unit_tests
|
||||
#-----------------------------------------------------------
|
||||
# Define executables and tests 'punit_tests' and 'pcunit_tests'
|
||||
# Define executables and tests
|
||||
if (MFEM_USE_MPI)
|
||||
# punit_tests
|
||||
if (MFEM_USE_CUDA)
|
||||
set_property(SOURCE punit_test_main.cpp PROPERTY LANGUAGE CUDA)
|
||||
endif()
|
||||
@@ -301,27 +320,44 @@ if (MFEM_USE_MPI)
|
||||
endif()
|
||||
endforeach()
|
||||
if (MFEM_USE_CUDA)
|
||||
set(PCUNIT_TESTS_SRCS pcunit_test_main.cpp)
|
||||
set_property(SOURCE ${PCUNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
|
||||
mfem_add_executable(pcunit_tests ${PCUNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
|
||||
add_dependencies(pcunit_tests copy_data)
|
||||
target_link_libraries(pcunit_tests mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pcunit_tests)
|
||||
foreach(np 1 ${MFEM_MPI_NP})
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME pcunit_tests_np=${np}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${np}
|
||||
${MPIEXEC_PREFLAGS} $<TARGET_FILE:pcunit_tests>
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
# pgpu_unit_tests
|
||||
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
|
||||
set_property(SOURCE ${PGPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
|
||||
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
|
||||
add_dependencies(pgpu_unit_tests copy_data)
|
||||
target_link_libraries(pgpu_unit_tests mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
|
||||
foreach(np 1 ${MFEM_MPI_NP})
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME pgpu_unit_tests_np=${np}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${np}
|
||||
${MPIEXEC_PREFLAGS} $<TARGET_FILE:pgpu_unit_tests>
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if (MFEM_USE_HIP)
|
||||
# pgpu_unit_tests
|
||||
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
|
||||
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
|
||||
add_dependencies(pgpu_unit_tests copy_data)
|
||||
target_link_libraries(pgpu_unit_tests mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
|
||||
foreach(np 1 ${MFEM_MPI_NP})
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME pgpu_unit_tests_np=${np}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${np}
|
||||
${MPIEXEC_PREFLAGS} $<TARGET_FILE:pgpu_unit_tests>
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif(MFEM_USE_MPI)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# PARALLEL SEDOV + TMOP TESTS:
|
||||
# psedov_tests_{cpu,debug,cuda,cuda_uvm}
|
||||
# ptmop_pa_tests_{cpu,cuda}
|
||||
# psedov_tests_{cpu,debug,gpu,gpu_uvm}
|
||||
# ptmop_pa_tests_{cpu,gpu}
|
||||
#-----------------------------------------------------------
|
||||
# Function to add one MPI executable for a test.
|
||||
function(add_mpi_executable_test name dev)
|
||||
@@ -371,10 +407,10 @@ function(add_parallel_miniapp_test name HYPRE_MM)
|
||||
list(APPEND backends debug)
|
||||
endif()
|
||||
endif()
|
||||
if (MFEM_USE_CUDA)
|
||||
list(APPEND backends cuda)
|
||||
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
|
||||
list(APPEND backends gpu)
|
||||
if (HYPRE_MM)
|
||||
list(APPEND backends cuda_uvm)
|
||||
list(APPEND backends gpu_uvm)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ This directory contains MFEM's suite of unit tests, using the
|
||||
MFEM's unit test suite includes a number of executables:
|
||||
|
||||
* `unit_tests`
|
||||
* `cunit_tests` if MFEM is compiled with CUDA support
|
||||
* `sedov_tests_cpu`, `sedov_tests_debug` (and `sedov_tests_cuda` and
|
||||
`sedov_tests_cuda_uvm` if CUDA is enabled), testing a Sedov hydrodynamics case
|
||||
* `tmop_pa_tests_cpu`, `tmop_pa_tests_debug` (and `tmop_pa_tests_cuda` if CUDA
|
||||
* `gpu_unit_tests` if MFEM is compiled with CUDA/HIP support
|
||||
* `sedov_tests_cpu`, `sedov_tests_debug` (and `sedov_tests_gpu` and
|
||||
`sedov_tests_gpu_uvm` if GPU is enabled), testing a Sedov hydrodynamics case
|
||||
* `tmop_pa_tests_cpu`, `tmop_pa_tests_debug` (and `tmop_pa_tests_gpu` if GPU
|
||||
is enabled), testing TMOP with partial assembly
|
||||
|
||||
There are also parallel versions of these executables (prefixed with `p`), which
|
||||
@@ -67,11 +67,11 @@ and those are:
|
||||
serial test executables, and will only be tested with the parallel executable
|
||||
(e.g. `punit_tests`). `punit_tests` will only run tests marked with
|
||||
`[Parallel]`.
|
||||
* `[CUDA]`, which indicates that a test will be tested with the CUDA executables
|
||||
(e.g. `cunit_tests`). These tests will still be run by the standard (CPU)
|
||||
executables. `cunit_tests` will only run tests marked with `[CUDA]`, and its
|
||||
parallel version `pcunit_tests` will only run tests marked with _both_
|
||||
`[CUDA]` and `[Parallel]`.
|
||||
* `[GPU]`, which indicates that a test will be tested with the GPU executables
|
||||
(e.g. `gpu_unit_tests`). These tests will still be run by the standard (CPU)
|
||||
executables. `gpu_unit_tests` will only run tests marked with `[GPU]`, and its
|
||||
parallel version `pgpu_unit_tests` will only run tests marked with _both_
|
||||
`[GPU]` and `[Parallel]`.
|
||||
* `[MFEMData]`, which indicates that a test requires access to a clone of the
|
||||
MFEM data repository (see the `--data` flag below), in order to run tests on
|
||||
some larger mesh files. By default, tests tagged with this tag are skipped,
|
||||
|
||||
@@ -185,7 +185,7 @@ TEST_CASE("Diffusion Diagonal PA", "[PartialAssembly][AssembleDiagonal]")
|
||||
{
|
||||
for (int ne = 1; ne < 3; ++ne)
|
||||
{
|
||||
const int n_elements = pow(ne, dimension);
|
||||
const int n_elements = static_cast<int>(pow(ne, dimension));
|
||||
CAPTURE(dimension, n_elements);
|
||||
|
||||
for (int order = 1; order < 5; ++order)
|
||||
@@ -359,7 +359,7 @@ TEST_CASE("Vector Diffusion Diagonal PA",
|
||||
}
|
||||
|
||||
TEST_CASE("Hcurl/Hdiv diagonal PA",
|
||||
"[CUDA][PartialAssembly][AssembleDiagonal]")
|
||||
"[GPU][PartialAssembly][AssembleDiagonal]")
|
||||
{
|
||||
for (int dimension = 2; dimension < 4; ++dimension)
|
||||
{
|
||||
@@ -404,7 +404,7 @@ TEST_CASE("Hcurl/Hdiv diagonal PA",
|
||||
{
|
||||
for (int ne = 1; ne < 3; ++ne)
|
||||
{
|
||||
const int n_elements = std::pow(ne, dimension);
|
||||
const int n_elements = static_cast<int>(std::pow(ne, dimension));
|
||||
CAPTURE(dimension, spaceType, integrator, coeffType, n_elements);
|
||||
|
||||
int max_order = (dimension == 3) ? 2 : 3;
|
||||
|
||||
@@ -195,7 +195,7 @@ void test_assembly_level(const char *meshname,
|
||||
REQUIRE(y_test.Norml2() < 1.e-12);
|
||||
}
|
||||
|
||||
TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
|
||||
TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [GPU]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
@@ -251,7 +251,7 @@ TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
|
||||
}
|
||||
} // H1 Assembly Levels test case
|
||||
|
||||
TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][CUDA]")
|
||||
TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][GPU]")
|
||||
{
|
||||
const auto fname = GENERATE(
|
||||
"../../data/inline-quad.mesh",
|
||||
@@ -316,7 +316,7 @@ TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][CUDA]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][CUDA]")
|
||||
TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][GPU]")
|
||||
{
|
||||
const auto fname = GENERATE(
|
||||
"../../data/inline-quad.mesh",
|
||||
@@ -387,7 +387,7 @@ TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][CUDA]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("L2 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
|
||||
TEST_CASE("L2 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [GPU]")
|
||||
{
|
||||
const bool dg = true;
|
||||
auto pb = GENERATE(Problem::Mass, Problem::Convection);
|
||||
@@ -454,7 +454,16 @@ void CompareMatricesNonZeros(SparseMatrix &A1, const SparseMatrix &A2,
|
||||
HYPRE_BigInt *cmap1=nullptr,
|
||||
std::unordered_map<HYPRE_BigInt,int> *cmap2inv=nullptr)
|
||||
{
|
||||
REQUIRE(A1.Height() == A2.Height());
|
||||
bool A1_Heigh_equals_A2_Height = A1.Height() == A2.Height();
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Mpi::IsInitialized() && !Mpi::IsFinalized())
|
||||
{
|
||||
const bool in = A1_Heigh_equals_A2_Height;
|
||||
MPI_Allreduce(&in, &A1_Heigh_equals_A2_Height, 1, MPI_C_BOOL, MPI_LAND,
|
||||
MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
REQUIRE(A1_Heigh_equals_A2_Height);
|
||||
int n = A1.Height();
|
||||
|
||||
const int *I1 = A1.HostReadI();
|
||||
@@ -488,6 +497,14 @@ void CompareMatricesNonZeros(SparseMatrix &A1, const SparseMatrix &A2,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Mpi::IsInitialized() && !Mpi::IsFinalized())
|
||||
{
|
||||
const real_t in = error;
|
||||
MPI_Allreduce(&in, &error, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX,
|
||||
MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
REQUIRE(error == MFEM_Approx(0.0, 1e-10));
|
||||
}
|
||||
|
||||
@@ -559,7 +576,7 @@ void TestH1FullAssembly(Mesh &mesh, int order)
|
||||
REQUIRE(B1.Normlinf() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [CUDA]")
|
||||
TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [GPU]")
|
||||
{
|
||||
auto order = GENERATE(1, 2, 3);
|
||||
auto mesh_fname = GENERATE(
|
||||
@@ -570,7 +587,7 @@ TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [CUDA]")
|
||||
TestH1FullAssembly(mesh, order);
|
||||
}
|
||||
|
||||
TEST_CASE("Full Assembly Connectivity", "[AssemblyLevel], [CUDA]")
|
||||
TEST_CASE("Full Assembly Connectivity", "[AssemblyLevel], [GPU]")
|
||||
{
|
||||
const int order = GENERATE(1, 2, 3);
|
||||
const int ne = GENERATE(4, 8, 16, 32);
|
||||
@@ -638,7 +655,7 @@ void TestSameHypreMatrices(OperatorHandle &A1, OperatorHandle &A2)
|
||||
CompareMatricesNonZeros(*M2, *M1);
|
||||
}
|
||||
|
||||
TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
|
||||
TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [GPU]")
|
||||
{
|
||||
auto order = GENERATE(1, 2, 3);
|
||||
auto mesh_fname = GENERATE(
|
||||
@@ -646,6 +663,8 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
|
||||
"../../data/fichera.mesh"
|
||||
);
|
||||
|
||||
// CAPTURE(order, mesh_fname);
|
||||
|
||||
Mesh serial_mesh(mesh_fname);
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
serial_mesh.Clear();
|
||||
@@ -675,17 +694,25 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
|
||||
|
||||
OperatorHandle A_fa, A_legacy;
|
||||
|
||||
// Test that ParallelAssemble gives the same result
|
||||
A_fa.Reset(a_fa.ParallelAssemble());
|
||||
A_legacy.Reset(a_legacy.ParallelAssemble());
|
||||
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
|
||||
<< "]: (1) ParallelAssemble")
|
||||
{
|
||||
// Test that ParallelAssemble gives the same result
|
||||
A_fa.Reset(a_fa.ParallelAssemble());
|
||||
A_legacy.Reset(a_legacy.ParallelAssemble());
|
||||
|
||||
TestSameHypreMatrices(A_fa, A_legacy);
|
||||
TestSameHypreMatrices(A_fa, A_legacy);
|
||||
}
|
||||
|
||||
// Test that FormSystemMatrix gives the same result
|
||||
a_fa.FormSystemMatrix(ess_tdof_list, A_fa);
|
||||
a_legacy.FormSystemMatrix(ess_tdof_list, A_legacy);
|
||||
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
|
||||
<< "]: (2) FormSystemMatrix")
|
||||
{
|
||||
// Test that FormSystemMatrix gives the same result
|
||||
a_fa.FormSystemMatrix(ess_tdof_list, A_fa);
|
||||
a_legacy.FormSystemMatrix(ess_tdof_list, A_legacy);
|
||||
|
||||
TestSameHypreMatrices(A_fa, A_legacy);
|
||||
TestSameHypreMatrices(A_fa, A_legacy);
|
||||
}
|
||||
|
||||
// Test that FormLinearSystem gives the same result
|
||||
ParGridFunction x1(&fespace);
|
||||
@@ -701,13 +728,23 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
|
||||
|
||||
a_fa.Assemble();
|
||||
|
||||
a_fa.FormLinearSystem(ess_tdof_list, x1, b1, A_fa, X1, B1);
|
||||
a_legacy.FormLinearSystem(ess_tdof_list, x2, b2, A_legacy, X2, B2);
|
||||
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
|
||||
<< "]: (3) FormLinearSystem")
|
||||
{
|
||||
a_fa.FormLinearSystem(ess_tdof_list, x1, b1, A_fa, X1, B1);
|
||||
a_legacy.FormLinearSystem(ess_tdof_list, x2, b2, A_legacy, X2, B2);
|
||||
|
||||
TestSameHypreMatrices(A_fa, A_legacy);
|
||||
TestSameHypreMatrices(A_fa, A_legacy);
|
||||
}
|
||||
|
||||
B1 -= B2;
|
||||
REQUIRE(B1.Normlinf() == MFEM_Approx(0.0));
|
||||
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
|
||||
<< "]: (4) FormLinearSystem - RHS")
|
||||
{
|
||||
B1 -= B2;
|
||||
const real_t B_err = GlobalLpNorm(infinity(), B1.Normlinf(),
|
||||
MPI_COMM_WORLD);
|
||||
REQUIRE(B_err == MFEM_Approx(0.0));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -69,7 +69,7 @@ TEST_CASE("Test order of boundary integrators",
|
||||
|
||||
TEST_CASE("FormLinearSystem/SolutionScope",
|
||||
"[BilinearForm]"
|
||||
"[CUDA]")
|
||||
"[GPU]")
|
||||
{
|
||||
// Create a simple mesh and FE space
|
||||
int dim = 2, nx = 2, ny = 2, order = 2;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
TEST_CASE("BlockOperators", "[BlockOperators], [CUDA]")
|
||||
TEST_CASE("BlockOperators", "[BlockOperators], [GPU]")
|
||||
{
|
||||
const int dim = 2, nx = 3, ny = 3, order = 2;
|
||||
Element::Type e_type = Element::QUADRILATERAL;
|
||||
|
||||
@@ -231,7 +231,7 @@ void TestFDCalcCurlShape(FiniteElement* fe, ElementTransformation * T,
|
||||
IntegrationPoint pt = ir->IntPoint(i);
|
||||
fe->CalcCurlShape(pt, dshape);
|
||||
|
||||
CAPTURE(pt.x, pt.y, pt.z);
|
||||
CAPTURE(pt.x, pt.y, dim == 3 ? pt.z : 0_r);
|
||||
|
||||
fdshape = 0.0;
|
||||
for (int d=0; d<dim; d++)
|
||||
|
||||
@@ -94,7 +94,7 @@ void TestCalcDivShape(FiniteElement* fe, ElementTransformation * T, int res)
|
||||
if (fe->GetGeomType() == Geometry::PYRAMID &&
|
||||
(ip.z >= 1.0 || ip.y > 1.0 - ip.z || ip.x > 1.0 - ip.z)) { continue; }
|
||||
|
||||
CAPTURE(ip.x, ip.y, ip.z);
|
||||
CAPTURE(ip.x, ip.y, dim == 3 ? ip.z : 0_r);
|
||||
|
||||
fe->CalcDivShape(ip, weights);
|
||||
|
||||
@@ -215,7 +215,7 @@ void TestFDCalcDivShape(FiniteElement* fe, ElementTransformation * T, int order)
|
||||
IntegrationPoint pt = ir->IntPoint(i);
|
||||
fe->CalcDivShape(pt, dshape);
|
||||
|
||||
CAPTURE(pt.x, pt.y, pt.z);
|
||||
CAPTURE(pt.x, pt.y, dim == 3 ? pt.z : 0_r);
|
||||
|
||||
fdshape = 0.0;
|
||||
for (int d=0; d<dim; d++)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
TEST_CASE("DG Mass Inverse", "[CUDA]")
|
||||
TEST_CASE("DG Mass Inverse", "[GPU]")
|
||||
{
|
||||
auto mesh_filename = GENERATE(
|
||||
"../../data/inline-segment.mesh",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
TEST_CASE("FA Determinism", "[PartialAssembly][CUDA]")
|
||||
TEST_CASE("FA Determinism", "[PartialAssembly][GPU]")
|
||||
{
|
||||
const int order = 3;
|
||||
const char *mesh_filename = "../../data/star-q3.mesh";
|
||||
|
||||
@@ -221,7 +221,8 @@ TEST_CASE("FE Symmetry",
|
||||
const int ne = order; // Num DoFs per edge
|
||||
const int nt = order * (order - 1); // Num DoF per tri face
|
||||
const int nq = 2 * nt; // Num DoF per quad face
|
||||
const int ni = order * pow(order - 1, 2); // Num DoF per interior dir
|
||||
// Num DoF per interior dir
|
||||
const int ni = order * (static_cast<int>(pow(order-1, 2)));
|
||||
const int oq = 8 * ne; // Offset to first quad DoF
|
||||
const int ot = oq + nq; // Offset to first tri DoF
|
||||
const int oi = ot + 4 * nt; // Offset to first interior DoF
|
||||
|
||||
@@ -190,7 +190,7 @@ TEST_CASE("InverseElementTransformation",
|
||||
}
|
||||
|
||||
TEST_CASE("BatchInverseElementTransformation",
|
||||
"[InverseElementTransformation], [CUDA]")
|
||||
"[InverseElementTransformation], [GPU]")
|
||||
{
|
||||
const real_t tol = 4e-13;
|
||||
|
||||
@@ -268,13 +268,14 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,13 +370,14 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,13 +473,14 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -575,13 +578,14 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -675,13 +679,14 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts] -
|
||||
orig_ref_space[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,12 +763,13 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -846,12 +852,13 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -925,12 +932,13 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1010,12 +1018,13 @@ TEST_CASE("BatchInverseElementTransformation",
|
||||
real_t max_err = 0;
|
||||
for (int i = 0; i < npts; ++i)
|
||||
{
|
||||
if (res_type[i] == InverseElementTransformation::Inside)
|
||||
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
|
||||
{
|
||||
++pts_found;
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
|
||||
max_err = fmax(max_err,
|
||||
fabs(AsConst(res_ref_space)[i + d * npts]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ struct LinearFormExtTest
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("Linear Form Extension", "[LinearFormExtension], [CUDA]")
|
||||
TEST_CASE("Linear Form Extension", "[LinearFormExtension], [GPU]")
|
||||
{
|
||||
const bool all = launch_all_non_regression_tests;
|
||||
|
||||
@@ -328,7 +328,7 @@ TEST_CASE("Linear Form Extension", "[LinearFormExtension], [CUDA]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [CUDA]")
|
||||
TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [GPU]")
|
||||
{
|
||||
const bool all = launch_all_non_regression_tests;
|
||||
|
||||
|
||||
@@ -140,17 +140,17 @@ void TestBatchedLOR()
|
||||
TestSameMatrices(A2, A1);
|
||||
}
|
||||
|
||||
TEST_CASE("LOR Batched H1", "[LOR][BatchedLOR][CUDA]")
|
||||
TEST_CASE("LOR Batched H1", "[LOR][BatchedLOR][GPU]")
|
||||
{
|
||||
TestBatchedLOR<H1_FECollection,MassIntegrator,DiffusionIntegrator>();
|
||||
}
|
||||
|
||||
TEST_CASE("LOR Batched ND", "[LOR][BatchedLOR][CUDA]")
|
||||
TEST_CASE("LOR Batched ND", "[LOR][BatchedLOR][GPU]")
|
||||
{
|
||||
TestBatchedLOR<ND_FECollection,VectorFEMassIntegrator,CurlCurlIntegrator>();
|
||||
}
|
||||
|
||||
TEST_CASE("LOR Batched RT", "[LOR][BatchedLOR][CUDA]")
|
||||
TEST_CASE("LOR Batched RT", "[LOR][BatchedLOR][GPU]")
|
||||
{
|
||||
TestBatchedLOR<RT_FECollection,VectorFEMassIntegrator,DivDivIntegrator>();
|
||||
}
|
||||
@@ -192,6 +192,8 @@ void ParTestBatchedLOR()
|
||||
"../../data/fichera-q3.mesh"
|
||||
);
|
||||
|
||||
CAPTURE(order, mesh_fname);
|
||||
|
||||
Mesh serial_mesh = Mesh::LoadFromFile(mesh_fname);
|
||||
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
@@ -226,22 +228,22 @@ void ParTestBatchedLOR()
|
||||
TestSameMatrices(A2, A1);
|
||||
}
|
||||
|
||||
TEST_CASE("Parallel LOR Batched H1", "[LOR][BatchedLOR][Parallel][CUDA]")
|
||||
TEST_CASE("Parallel LOR Batched H1", "[LOR][BatchedLOR][Parallel][GPU]")
|
||||
{
|
||||
ParTestBatchedLOR<H1_FECollection,MassIntegrator,DiffusionIntegrator>();
|
||||
}
|
||||
|
||||
TEST_CASE("Parallel LOR Batched ND", "[LOR][BatchedLOR][Parallel][CUDA]")
|
||||
TEST_CASE("Parallel LOR Batched ND", "[LOR][BatchedLOR][Parallel][GPU]")
|
||||
{
|
||||
ParTestBatchedLOR<ND_FECollection,VectorFEMassIntegrator,CurlCurlIntegrator>();
|
||||
}
|
||||
|
||||
TEST_CASE("Parallel LOR Batched RT", "[LOR][BatchedLOR][Parallel][CUDA]")
|
||||
TEST_CASE("Parallel LOR Batched RT", "[LOR][BatchedLOR][Parallel][GPU]")
|
||||
{
|
||||
ParTestBatchedLOR<RT_FECollection,VectorFEMassIntegrator,DivDivIntegrator>();
|
||||
}
|
||||
|
||||
TEST_CASE("LOR AMS", "[LOR][BatchedLOR][AMS][Parallel][CUDA]")
|
||||
TEST_CASE("LOR AMS", "[LOR][BatchedLOR][AMS][Parallel][GPU]")
|
||||
{
|
||||
enum SpaceType { ND, RT };
|
||||
auto space_type = GENERATE(ND, RT);
|
||||
@@ -313,7 +315,7 @@ TEST_CASE("LOR AMS", "[LOR][BatchedLOR][AMS][Parallel][CUDA]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("LOR ADS", "[LOR][BatchedLOR][ADS][Parallel][CUDA]")
|
||||
TEST_CASE("LOR ADS", "[LOR][BatchedLOR][ADS][Parallel][GPU]")
|
||||
{
|
||||
// Only need to test ADS in 3D
|
||||
auto mesh_fname = GENERATE("../../data/fichera-q3.mesh");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user