Compare commits

..
Author SHA1 Message Date
camierjs 3a319b653a Merge branch 'master' into hpcftools/flowSolver 2025-06-30 13:32:48 -07:00
bslazarov 6507e421e2 ioip 2024-12-16 14:54:11 -08:00
bslazarov 7edb2ed7c4 hlkjlkjl 2024-12-05 15:13:58 -08:00
bslazarov 5d84aa5eb5 Merge branch 'hpcftools/flowSolver' of github.com:mfem/mfem into hpcftools/flowSolver 2024-12-04 14:18:58 -08:00
bslazarov 09988c9da3 ini stokes 2024-12-04 14:18:23 -08:00
Mathias Rainer Schmidt 74e4ad3e2c - updated flow solver
- added comments to Blf and Lf contributions
- split setup and step into vel, auxiliary and pressure part
2024-11-25 11:04:30 -08:00
Mathias Rainer Schmidt cf9fcd8dde Merge remote-tracking branch 'origin/master' into hpcftools/flowSolver 2024-11-14 13:52:27 -08:00
Mathias Rainer Schmidt fa8617ada3 - added partial assembly option 2024-11-01 13:20:29 -07:00
Mathias Rainer Schmidt f7e5db2cea - added ortho solver to phi field 2024-10-24 16:06:37 -07:00
Mathias Rainer Schmidt 60c11776b6 - added executable 2024-10-21 15:57:23 -07:00
Mathias Rainer Schmidt 6238f8ca76 - update solution 2024-10-16 16:00:08 -07:00
Mathias Rainer Schmidt 6a256db9aa - added linear solvers to step 2024-10-16 15:56:46 -07:00
Mathias Rainer Schmidt e0fb9658ca - added linear form integrators 2024-10-15 17:30:09 -07:00
Mathias Rainer Schmidt a1089efac3 - added BilinearForms 2024-10-15 13:46:36 -07:00
Mathias Rainer Schmidt 83f7f769dc - inital flow solver commit 2024-10-15 12:44:25 -07:00
122 changed files with 2596 additions and 7099 deletions
-154
View File
@@ -1,154 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: Sanitizer Config
description: Sets up environment variables for MFEM sanitizer workflow
inputs:
DEBUG:
description: If true, use intermediate caches to speed up the workflow
by reusing previous builds.
default: false
REPOSITORY:
description: Repository to checkout
default: mfem/mfem
BRANCH:
description: Branch to checkout
default: ubsan
CLANG_VER:
description: CLANG version to use
default: 18
# https://github.com/llvm/llvm-project/releases
LLVM_VER:
description: LLVM version to use
default: 19.1.7
# https://github.com/hypre-space/hypre/releases
HYPRE_VER:
description: HYPRE version to use
default: 2.19.0
METIS_VER:
description: METIS version to use
default: 4.0.3
CTEST:
description: CTest command to use
default: ctest -j --test-load $(nproc)
--schedule-random
--stop-on-failure --output-on-failure
--test-dir
# https://clang.llvm.org/docs/AddressSanitizer.html
ASAN_OPTIONS:
default: detect_leaks=1,
strict_init_order=1,
strict_string_checks=1,
check_initialization_order=1,
detect_stack_use_after_return=1
ASAN_CXXFLAGS:
default: -fsanitize=address
-fsanitize-address-use-after-scope
ASAN_LDFLAGS:
default: -fsanitize=address
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
UBSAN_OPTIONS:
default: halt_on_error=1, print_stacktrace=1
UBSAN_CXXFLAGS:
default: -fsanitize=undefined
UBSAN_LDFLAGS:
default: -fsanitize=undefined
# https://clang.llvm.org/docs/MemorySanitizer.html
MSAN_OPTIONS:
default: "poison_in_dtor=1"
MSAN_CXXFLAGS:
default: -fsanitize=memory
-fsanitize-memory-track-origins
-fsanitize-memory-use-after-dtor
MSAN_LDFLAGS:
default: -fsanitize=memory
LSAN_DIR:
description: LSAN suppression directory
default: lsan
LSAN_FILE:
description: LSAN suppression file
default: lsan.supp
NO_FLAGS:
description: If true, do not set any CXXFLAGS or LDFLAGS.
default: false
runs:
using: 'composite'
steps:
- name: Env (Inputs)
run: |
echo DEBUG=${{inputs.DEBUG}} >> $GITHUB_ENV
echo REPOSITORY=${{inputs.REPOSITORY}} >> $GITHUB_ENV
echo BRANCH=${{inputs.BRANCH}} >> $GITHUB_ENV
echo CLANG_VER=${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo LLVM_VER=${{inputs.LLVM_VER}} >> $GITHUB_ENV
echo HYPRE_VER=${{inputs.HYPRE_VER}} >> $GITHUB_ENV
echo METIS_VER=${{inputs.METIS_VER}} >> $GITHUB_ENV
echo CTEST=${{inputs.CTEST}} >> $GITHUB_ENV
echo ASAN_OPTIONS=${{inputs.ASAN_OPTIONS}} >> $GITHUB_ENV
echo UBSAN_OPTIONS=${{inputs.UBSAN_OPTIONS}} >> $GITHUB_ENV
echo MSAN_OPTIONS=${{inputs.MSAN_OPTIONS}} >> $GITHUB_ENV
echo LSAN_DIR=${{inputs.LSAN_DIR}} >> $GITHUB_ENV
echo LSAN_FILE=${{inputs.LSAN_FILE}} >> $GITHUB_ENV
echo ASAN_CXXFLAGS=${{inputs.ASAN_CXXFLAGS}} >> $GITHUB_ENV
echo ASAN_LDFLAGS=${{inputs.ASAN_LDFLAGS}} >> $GITHUB_ENV
echo UBSAN_CXXFLAGS=${{inputs.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Env (dir)
run: |
echo LLVM_DIR=${{github.workspace}}/llvm >> $GITHUB_ENV
echo HYPRE_DIR=hypre-${{inputs.HYPRE_VER}} >> $GITHUB_ENV
echo METIS_DIR=metis-${{inputs.METIS_VER}} >> $GITHUB_ENV
shell: bash
- name: Env (bis)
run: |
echo CC=clang-${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo CXX=clang++-${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo LLVM_INC=${{env.LLVM_DIR}}/include/c++/v1 >> $GITHUB_ENV
echo LLVM_LIB=${{env.LLVM_DIR}}/lib >> $GITHUB_ENV
echo HYPRE_TGZ=v${{inputs.HYPRE_VER}}.tar.gz >> $GITHUB_ENV
echo METIS_TGZ=metis-${{inputs.METIS_VER}}.tar.gz >> $GITHUB_ENV
LSAN_SUPPRESSIONS="${{github.workspace}}/${{inputs.LSAN_DIR}}/${{inputs.LSAN_FILE}}"
echo "LSAN_OPTIONS=suppressions=$LSAN_SUPPRESSIONS" >> $GITHUB_ENV
shell: bash
- name: Env (ter)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo LLVM_CXXFLAGS=-stdlib=libc++ -I${{env.LLVM_INC}} -Isystem${{env.LLVM_INC}} >> $GITHUB_ENV
echo LLVM_LDFLAGS=-L${{env.LLVM_LIB}} -lc++abi -Wl,-rpath,${{env.LLVM_LIB}} >> $GITHUB_ENV
shell: bash
- name: Env (quater)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo CXXFLAGS=${{env.LLVM_CXXFLAGS}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LLVM_LDFLAGS}} >> $GITHUB_ENV
shell: bash
-91
View File
@@ -1,91 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'MFEM Compilation'
description: 'MFEM Compilation'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
runs:
using: 'composite'
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
if: ${{env.DEBUG == 'true'}}
id: debug
with:
path: mfem/build
key: build-${{inputs.par}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/setup
if: ${{steps.debug.outputs.cache-hit != 'true'}}
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
- name: Build with ASAN
if: inputs.sanitizer == 'asan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.ASAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Build with MSAN
if: inputs.sanitizer == 'msan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Build with UBSAN
if: inputs.sanitizer == 'ubsan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- uses: mfem/github-actions/build-mfem@v2.5
if: ${{steps.debug.outputs.cache-hit != 'true'}}
env:
CXXFLAGS: ${{env.CXXFLAGS}}
LDFLAGS: ${{env.LDFLAGS}}
with:
mpi: ${{inputs.par == 'false' && 'seq' || 'par'}}
mfem-dir: mfem
os: ${{runner.os}}
library-only: true
build-system: cmake
hypre-dir: ${{env.HYPRE_DIR}}
metis-dir: ${{env.METIS_DIR}}
config-options: >-
-GNinja
-DMPICXX=${{env.CXX}}
-DCMAKE_CXX_STANDARD=17
-DMFEM_USE_MEMALLOC=OFF
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_VERBOSE_MAKEFILE=ON
-DCMAKE_CXX_COMPILER=${{env.CXX}}
-DCMAKE_CXX_FLAGS_RELEASE='-g -O1 -fno-omit-frame-pointer'
- name: Delete object files
if: ${{steps.debug.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: find . -type f -name '*.o' -delete
shell: bash
- uses: actions/upload-artifact@v4
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
if-no-files-found: error
retention-days: 1
overwrite: false
-33
View File
@@ -1,33 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'Install MPI'
description: 'Installs MPI and set up its environment variables'
runs:
using: 'composite'
steps:
- name: Install
run: sudo apt-get install openmpi-bin libopenmpi-dev
shell: bash
- name: Env
run: |
echo PRTE_MCA_rmaps_default_mapping_policy=:oversubscribe >> $GITHUB_ENV
echo MPI_INC=$(mpicxx --showme:compile) >> $GITHUB_ENV
echo MPI_LIB=$(mpicxx --showme:link) >> $GITHUB_ENV
shell: bash
- name: Env (bis)
run: |
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
shell: bash
@@ -1,71 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'Restore state'
description: 'Restore state to be able to run checks, tests'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
cache-path:
description: 'path to what needs to be restored'
default: none
cache-skip:
description: 'Skip cache restoration'
default: false
outputs:
cache-hit:
description: 'Output from a specific step'
value: ${{steps.debug.outputs.cache-hit}}
runs:
using: 'composite'
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
id: debug
with:
path: ${{inputs.cache-path}}
key: ${{github.job}}-${{inputs.par}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/setup
if: ${{steps.debug.outputs.cache-hit != 'true'}}
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
- uses: actions/download-artifact@v4
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
- name: Ninja Patch
working-directory: mfem/build
run: |
sed -i -e 's/CXX_STATIC_LIBRARY_LINKER__mfem_Release.*/CUSTOM_COMMAND/' build.ninja
sed -i -e '/build tests\/unit\/all:/ s/tests\/unit\/[^ ]*unit_tests[^ ]*//g' build.ninja
sed -i -e '/^add_test(\[=\[\(unit_tests\|punit_tests\)\]=\]/ s/)/ "--input-file .\/list-test-names-${{matrix.tag}}" "--min-duration 1")/' tests/unit/CTestTestfile.cmake
shell: bash
- name: Copy Data
if: ${{steps.debug.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
ninja cmake_object_order_depends_target_unit_tests
cp -pR ../tests/unit/data tests/unit
shell: bash
-64
View File
@@ -1,64 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: 'Setup state'
description: 'Sets up the state to be able to run build & run'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
runs:
using: 'composite'
steps:
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
with:
path: ${{env.LLVM_DIR}}
fail-on-cache-miss: true
key: build-libcxx-${{env.LLVM_VER}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/mpi
if: ${{inputs.par == 'true'}}
- uses: actions/cache/restore@v4 # Cache for Hypre
if: ${{inputs.par == 'true'}}
with:
path: ${{env.HYPRE_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
- uses: actions/cache/restore@v4 # Cache for Metis
if: ${{inputs.par == 'true'}}
with:
path: ${{env.METIS_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
- name: Hypre/Metis links
if: ${{inputs.par == 'true'}}
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
shell: bash
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
with:
path: ${{env.LSAN_DIR}}
fail-on-cache-miss: true
key: build-lsan-suppression-file
- uses: actions/checkout@v4 # Checkout the repository
with:
path: mfem
# ref: ${{env.BRANCH}}
# repository: ${{env.REPOSITORY}}
+7 -26
View File
@@ -7,17 +7,18 @@
https://mfem.org
This directory contains the GitHub CI scripts for MFEM.
Note that some of these scripts use the shared MFEM GitHub Actions from the external mfem/github-actions repository:
<https://github.com/mfem/github-actions>
https://github.com/mfem/github-actions
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch in the above from which the action is taken.
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.1`, the `v2.1` suffix denotes the branch in the above from which the action is taken.
The current CI workflows are:
## `repo-check.yml`
### `repo-check.yml`
Runs a number of static repository-level sanity checks.
@@ -29,39 +30,19 @@ Runs a number of static repository-level sanity checks.
- `branch-history` guards against accidental commits of large files using the `--history` option of the `config/githooks/pre-push` script.
## `mfem-analysis.yml` (`build-analysis`)
### `mfem-analysis.yml` (`build-analysis`)
Checks if the code builds and satisfies minimal requirements.
- `gitignore` builds hypre, METIS, and MFEM using `mfem/github-actions/build-hypre`, `mfem/github-actions/build-metis`, and `mfem/github-actions/build-mfem` and checks for correct `.gitignore` settings by running the `tests/scripts/gitignore` script.
## `builds-and-tests.yml`
### `builds-and-tests.yml`
Runs a matrix of builds and tests runs with different compilers, OS, mfem/hypre settings, etc. Also processes and upload Codecov reports.
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
Uses the following GitHub Actions from https://github.com/mfem/github-actions:
- `mfem/github-actions/build-hypre`
- `mfem/github-actions/build-metis`
- `mfem/github-actions/build-mfem`
- `mfem/github-actions/upload-coverage`
## Sanitizer Workflow for MFEM Verification
This workflow validates MFEM unit tests, examples, and miniapps using sanitizer tools.
- `sanitizers.yml` orchestrates:
- Building and caching dependencies: HYPRE, METIS, LSAN suppression file, and LLVM libcxx.
- Launching fine-grained jobs for serial (ASAN, MSAN, UBSAN) and parallel (ASAN, UBSAN) sanitizers.
- `sanitize-tests.yml` is a reusable workflow accepting `par` mode (`true` for parallel) and `sanitizer` (ASAN, MSAN, or UBSAN) as inputs. It executes the following jobs:
- **Build**: Compiles the MFEM library with specified parallel and sanitizer settings.
- **Check**: Runs verification checks.
- Parallel jobs to test the following: **Examples**, **Miniapps** and **Unit tests**
The workflow leverages composite actions in `.github/actions/sanitize/`:
- `config`: Centralizes settings for the sanitizer workflow.
- `mfem`: Manages the MFEM library build process.
- `mpi`: Installs MPI and applies additional compilation flags.
- `restore`: Restores the testing environment state.
- `setup`: Builds or restores cached dependencies.
+69
View File
@@ -0,0 +1,69 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
name: "Sanitizer"
permissions:
actions: write
on:
push:
branches:
- master
- next
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Serial:
runs-on: ubuntu-24.04
steps:
- name: MFEM Checkout
uses: actions/checkout@v4
with:
path: mfem
- name: MFEM Build
uses: mfem/github-actions/build-mfem@v2.5
with:
os: ${{ runner.os }}
target: opt
mpi: seq
hypre-dir: unused-hypre-dir
metis-dir: unused-metis-dir
mfem-dir: mfem
build-system: make
library-only: false
config-options:
CXX="clang++-18"
CXXFLAGS="-g -O1 -std=c++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
@@ -1,39 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-hypre
on:
workflow_call:
jobs:
build-hypre:
runs-on: ubuntu-latest
name: 2.19.0
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.HYPRE_DIR}}
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.5
with:
archive: ${{env.HYPRE_TGZ}}
dir: ${{env.HYPRE_DIR}}
target: int32
precision: fp64
build-system: make
@@ -1,76 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-libcxx
on:
workflow_call:
jobs:
build-llvm-libcxx:
runs-on: ubuntu-latest
strategy:
matrix:
sanitizer: [asan, msan, ubsan]
include:
- sanitizer: asan
llvm_use_sanitizer: "Address"
- sanitizer: msan
llvm_use_sanitizer: "MemoryWithOrigins"
- sanitizer: ubsan
llvm_use_sanitizer: "Undefined"
name: ${{matrix.sanitizer}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.LLVM_DIR}}
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
- name: Clone
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: >
git clone --filter=blob:none --depth=1
--branch llvmorg-${{env.LLVM_VER}}
--no-checkout https://github.com/llvm/llvm-project.git llvm-project
- name: Checkout
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: llvm-project
run: |
git sparse-checkout set --cone
git checkout llvmorg-${{env.LLVM_VER}}
git sparse-checkout set cmake llvm/cmake runtimes libcxx libcxxabi
- name: Mkdir
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: mkdir ${{env.LLVM_DIR}}
- name: CMake
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: ${{env.LLVM_DIR}}
run: >
VERBOSE=1
cmake -GNinja ../llvm-project/runtimes/
-DCMAKE_C_COMPILER=${{env.CC}}
-DCMAKE_CXX_COMPILER=${{env.CXX}}
-DCMAKE_BUILD_TYPE=RelWithDebInfo
-DCMAKE_INSTALL_PREFIX=/usr
-DLLVM_USE_SANITIZER=${{matrix.llvm_use_sanitizer}}
-DLLVM_BUILD_32_BITS=OFF
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF
-DLLVM_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_BENCHMARKS=OFF
-DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi'
- name: Build
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: ${{env.LLVM_DIR}}
run: cmake --build . -- cxx cxxabi
-38
View File
@@ -1,38 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-file-lsan
on:
workflow_call:
jobs:
build-file-lsan:
runs-on: ubuntu-latest
name: lsan.supp
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.LSAN_DIR}}
key: build-lsan-suppression-file
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
run: |
mkdir -p ${{env.LSAN_DIR}}
cat << EOF > ${{env.LSAN_DIR}}/${{env.LSAN_FILE}}
leak:libevent_core-2.1.so
leak:ompi_mpi_finalize
leak:ompi_mpi_init
leak:PMPI_Init
leak:strdup
EOF
@@ -1,36 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: build-metis
on:
workflow_call:
jobs:
build-metis:
runs-on: ubuntu-latest
name: 4.0.3
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.METIS_DIR}}
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
with:
archive: ${{env.METIS_TGZ}}
dir: ${{env.METIS_DIR}}
-197
View File
@@ -1,197 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: Sanitize
on:
workflow_call:
inputs:
par:
description: 'Whether to build for parallel (true/false)'
required: false
default: false
type: boolean
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
required: true
default: asan
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/mfem
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
check:
needs: [build]
runs-on: ubuntu-latest
env:
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/examples/${{env.ex}}
- name: MFEM Check
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v check
examples:
needs: [check]
runs-on: ubuntu-latest
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/examples/ex1
- name: Build Examples
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v examples
- name: Test Examples
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} examples ${{env.exclude}} --show-only
${{env.CTEST}} examples ${{env.exclude}}
miniapps:
needs: [check]
runs-on: ubuntu-latest
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/miniapps/meshing/minimal-surface
- name: Build Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v miniapps
- name: Test Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} miniapps ${{env.exclude}} --show-only
${{env.CTEST}} miniapps ${{env.exclude}}
tests-miniapps:
needs: [check]
runs-on: ubuntu-latest
env:
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/sedov_tests_cpu
- name: Build Tests Unit Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v tests/unit/all
- name: Run Tests Unit Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}} --show-only
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}}
tests-unit-build:
needs: [check]
runs-on: ubuntu-latest
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
- name: Build Unit Tests
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v ${{env.unit_tests}}
- name: Delete object files
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: find . -type f -name '*.o' -delete
- uses: actions/upload-artifact@v4
with:
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build/tests/unit/${{env.unit_tests}}
if-no-files-found: error
retention-days: 1
overwrite: false
tests-unit-run:
needs: [tests-unit-build]
runs-on: ubuntu-latest
strategy:
matrix:
tag: [0, 1, 2, 3]
name: tests-unit-run-${{matrix.tag}}
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
np: ${{inputs.par && '_np=2' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
- uses: actions/download-artifact@v4
if: ${{steps.restore.outputs.cache-hit != 'true'}}
with:
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build/tests/unit
- name: Split Unit Tests
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: |
chmod 755 ${{env.unit_tests}}
./${{env.unit_tests}} --list-test-names-only | tail -n +2 > list-test-names
shuf list-test-names -o list-test-names
split --verbose -n l/4 -d -a 1 list-test-names list-test-names-
- name: Cat Unit Tests ${{matrix.tag}}
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: cat list-test-names-${{matrix.tag}}
- name: Run Unit Tests ${{matrix.tag}}
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}" --show-only
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}"
-73
View File
@@ -1,73 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
---
name: Sanitizers
permissions:
actions: write
on:
push:
branches: ["master", "next"]
pull_request:
workflow_dispatch:
concurrency:
group: ${{github.workflow}}-${{github.ref}}
cancel-in-progress: true
jobs:
# Build steps for dependencies
build-hypre:
uses: ./.github/workflows/sanitize-build-hypre.yml
build-metis:
uses: ./.github/workflows/sanitize-build-metis.yml
build-lsan:
uses: ./.github/workflows/sanitize-build-lsan.yml
build-libcxx:
uses: ./.github/workflows/sanitize-build-libcxx.yml
# Serial sanitizers: asan, msan, ubsan
seq-asan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: asan
seq-msan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: msan
seq-ubsan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: ubsan
# Parallel sanitizers: asan, ubsan
par-asan:
needs: [build-libcxx, build-hypre, build-metis]
uses: ./.github/workflows/sanitize-tests.yml
with:
par: true
sanitizer: asan
par-ubsan:
needs: [build-libcxx, build-hypre, build-metis]
uses: ./.github/workflows/sanitize-tests.yml
with:
par: true
sanitizer: ubsan
+2 -4
View File
@@ -300,7 +300,6 @@ miniapps/nurbs/nurbs_solenoidal
miniapps/nurbs/nurbs_printfunc
miniapps/nurbs/nurbs_patch_ex1
miniapps/nurbs/nurbs_curveint
miniapps/nurbs/nurbs_surface
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol_?.gf
@@ -319,7 +318,6 @@ miniapps/nurbs/nurbs_naca_cmesh
miniapps/nurbs/naca-cmesh.mesh
miniapps/nurbs/glvis_naca-cmesh.mesh
miniapps/nurbs/Naca_cmesh
miniapps/nurbs/*-Surface.mesh
miniapps/performance/ex1
miniapps/performance/ex1p
@@ -415,8 +413,8 @@ miniapps/diag-smoothers/mg-abs-l1-jacobi
tests/unit/output_meshes
tests/unit/unit_tests
tests/unit/punit_tests
tests/unit/gpu_unit_tests
tests/unit/pgpu_unit_tests
tests/unit/cunit_tests
tests/unit/pcunit_tests
tests/unit/sedov_tests_*
tests/unit/psedov_tests_*
tests/unit/tmop_pa_tests_*
+1 -21
View File
@@ -29,14 +29,9 @@ 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
@@ -44,8 +39,6 @@ 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
-------------------------------------
@@ -55,26 +48,13 @@ 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
====================================
+77 -36
View File
@@ -62,9 +62,14 @@ 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 &);
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 &);
bool check_for_inline_mesh(const char * mesh_file);
@@ -210,48 +215,54 @@ int main(int argc, char *argv[])
ComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
ComplexFunctionCoefficient u0(u0_exact);
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
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);
ComplexConstantCoefficient oneCoef(1.0);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
switch (prob)
{
case 0:
if (exact_sol)
{
u.ProjectBdrCoefficient(u0, ess_bdr);
u_exact->ProjectCoefficient(u0);
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
u_exact->ProjectCoefficient(u0_r, u0_i);
}
else
{
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
}
break;
case 1:
if (exact_sol)
{
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
u_exact->ProjectCoefficient(u1);
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
u_exact->ProjectCoefficient(u1_r, u1_i);
}
else
{
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
}
break;
case 2:
if (exact_sol)
{
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
u_exact->ProjectCoefficient(u2);
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
u_exact->ProjectCoefficient(u2_r, u2_i);
}
else
{
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
}
break;
default: break; // This should be unreachable
@@ -289,24 +300,27 @@ 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<DiffusionIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
default: break; // This should be unreachable
}
@@ -422,24 +436,29 @@ int main(int argc, char *argv[])
if (exact_sol)
{
real_t err_u = -1.0;
real_t err_r = -1.0;
real_t err_i = -1.0;
switch (prob)
{
case 0:
err_u = u.ComputeL2Error(u0);
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_u = u.ComputeL2Error(u1);
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_u = u.ComputeL2Error(u2);
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
default: break; // This should be unreachable
}
cout << endl;
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
@@ -545,14 +564,36 @@ complex<real_t> u0_exact(const Vector &x)
return std::exp(-i * kappa * x[dim - 1]);
}
void u1_exact(const Vector &x, ComplexVector &v)
real_t u0_real_exact(const Vector &x)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
return u0_exact(x).real();
}
void u2_exact(const Vector &x, ComplexVector &v)
real_t u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &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);
}
+33 -50
View File
@@ -62,10 +62,6 @@ 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 &);
@@ -248,22 +244,13 @@ 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);
@@ -271,40 +258,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, ess_bdr);
u_exact->ProjectCoefficient(u0);
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
u_exact->ProjectCoefficient(u0_r, u0_i);
}
else
{
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
}
break;
case 1:
if (exact_sol)
{
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
u_exact->ProjectCoefficient(u1);
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
u_exact->ProjectCoefficient(u1_r, u1_i);
}
else
{
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
}
break;
case 2:
if (exact_sol)
{
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
u_exact->ProjectCoefficient(u2);
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
u_exact->ProjectCoefficient(u2_r, u2_i);
}
else
{
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
}
break;
default: break; // This should be unreachable
@@ -344,24 +331,27 @@ 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<DiffusionIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
default: break; // This should be unreachable
}
@@ -485,18 +475,22 @@ int main(int argc, char *argv[])
if (exact_sol)
{
real_t err_u = -1.0;
real_t err_r = -1.0;
real_t err_i = -1.0;
switch (prob)
{
case 0:
err_u = u.ComputeL2Error(u0);
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_u = u.ComputeL2Error(u1);
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_u = u.ComputeL2Error(u2);
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
default: break; // This should be unreachable
}
@@ -504,7 +498,8 @@ int main(int argc, char *argv[])
if ( myid == 0 )
{
cout << endl;
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
}
@@ -632,12 +627,6 @@ 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();
@@ -650,12 +639,6 @@ 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();
-3
View File
@@ -59,7 +59,6 @@ set(SRCS
integ/nonlininteg_vecconvection_pa.cpp
integ/nonlininteg_vecconvection_mf.cpp
coefficient.cpp
complex_coefficient.cpp
complex_fem.cpp
convergence.cpp
datacollection.cpp
@@ -177,7 +176,6 @@ set(HDRS
integ/bilininteg_hcurlhdiv_kernels.hpp
integ/bilininteg_mass_kernels.hpp
coefficient.hpp
complex_coefficient.hpp
complex_fem.hpp
convergence.hpp
datacollection.hpp
@@ -249,7 +247,6 @@ set(HDRS
nonlinearform_ext.hpp
nonlininteg.hpp
qfunction.hpp
qinterp/det.hpp
qinterp/eval.hpp
qinterp/eval_hdiv.hpp
qinterp/grad.hpp
-1
View File
@@ -515,7 +515,6 @@ struct InvTNewtonSolver<Geometry::SEGMENT, SDim, SType, max_team_x>
phys_tol += pptr[idx + d * npts] * pptr[idx + d * npts];
}
phys_tol = fmax(phys_rtol * phys_rtol, phys_tol * phys_rtol * phys_rtol);
hit_bdr[0] = prev_hit_bdr[0] = false;
}
// for each iteration
while (true)
-217
View File
@@ -1,217 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "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
-523
View File
@@ -1,523 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_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
-240
View File
@@ -96,23 +96,6 @@ 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)
@@ -125,23 +108,6 @@ 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,
@@ -155,26 +121,6 @@ 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,
@@ -188,28 +134,6 @@ 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,
@@ -225,80 +149,6 @@ 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)
@@ -881,17 +731,6 @@ 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)
@@ -904,17 +743,6 @@ 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,
@@ -928,19 +756,6 @@ 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,
@@ -956,21 +771,6 @@ 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,
@@ -986,21 +786,6 @@ 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)
{
@@ -1040,31 +825,6 @@ 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
+21 -1307
View File
File diff suppressed because it is too large Load Diff
+10 -12
View File
@@ -764,9 +764,9 @@ ParaViewDataCollectionBase::ParaViewDataCollectionBase(
{
cycle = 0;
#ifdef MFEM_USE_ZLIB
// If we have zlib, enable compression. Otherwise, compression is disabled in
// the DataCollection base class constructor.
compression = true;
compression = true; // if we have zlib, enable compression
#else
compression = false; // otherwise, disable compression
#endif
}
@@ -784,8 +784,13 @@ 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
@@ -1169,14 +1174,7 @@ 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()
{
+7 -6
View File
@@ -537,6 +537,13 @@ 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).
///
@@ -626,12 +633,6 @@ 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;
-1
View File
@@ -241,7 +241,6 @@ 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);
+1 -2
View File
@@ -101,11 +101,10 @@ public:
// Setup DofToQuad information
dtq.nqpt = (int)floor(std::pow(ir.GetNPoints(), 1.0 / mesh.Dimension()) + 0.5);
dtq.ndof = dtq.nqpt;
dtq.mode = used_in_tensor_product ? DofToQuad::TENSOR : DofToQuad::FULL;
// Calculate sizes
const int num_qp = used_in_tensor_product ?
static_cast<int>(std::pow(dtq.nqpt, mesh.Dimension())) :
std::pow(dtq.nqpt, mesh.Dimension()) :
ir.GetNPoints();
tsize = vdim * num_qp * mesh.GetNE();
+8 -9
View File
@@ -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->AddMultTranspose(x, y);
R->MultTranspose(x, y);
};
return std::make_tuple(RT, R->Height());
}
@@ -1702,13 +1702,12 @@ std::array<DofToQuadMap, N> load_dtq_mem(
std::array<DofToQuadMap, N> f;
for (std::size_t i = 0; i < N; i++)
{
const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
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);
if (dtq[i].which_input != -1)
{
const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
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)
@@ -2159,7 +2158,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...>)
{
@@ -2244,7 +2243,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
View File
@@ -4334,7 +4334,7 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
u.GetSubVector(udofs, ul);
utrans.InvTransformPrimal(ul);
Transf = ufes->GetElementTransformation(ielem);
const auto *dummy = ufes->GetFE(ielem);
FiniteElement *dummy = nullptr;
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
*dummy, fl, with_coeff, ir);
+25 -26
View File
@@ -1009,7 +1009,6 @@ 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;
@@ -1039,11 +1038,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_DIRECT(dz,z,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
}
@@ -1051,9 +1050,9 @@ inline void SmemPADiffusionApply3D(const int NE,
}
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
B[qx][dy] = b(qx,dy);
G[qx][dy] = g(qx,dy);
@@ -1061,11 +1060,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0, v = 0.0;
MFEM_UNROLL(MD1)
@@ -1081,11 +1080,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MD1)
@@ -1102,11 +1101,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MD1)
@@ -1137,9 +1136,9 @@ inline void SmemPADiffusionApply3D(const int NE,
MFEM_SYNC_THREAD;
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
Bt[dy][qx] = b(qx,dy);
Gt[dy][qx] = g(qx,dy);
@@ -1147,11 +1146,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
@@ -1168,11 +1167,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(Q1D)
@@ -1189,11 +1188,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
+1 -1
View File
@@ -62,7 +62,7 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const int NE = ne;
const int Q1D = quad1D;
const int NQ = static_cast<int>(std::pow(Q1D, dim));
const int NQ = 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
View File
@@ -673,7 +673,7 @@ public:
int myid;
MPI_Comm_rank(comm, &myid);
int seed = (seed_ > 0) ? seed_ + myid : time(nullptr) + myid;
int seed = (seed_ > 0) ? seed_ + myid : (int)time(0) + myid;
SetSeed(seed);
}
#else
+2 -2
View File
@@ -5259,7 +5259,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
gc.GetNeighborLTDofTable(nbr_ltdof);
const int nb_connections = nbr_ltdof.Size_of_connections();
shr_ltdof.SetSize(nb_connections);
if (nb_connections > 0) { shr_ltdof.CopyFrom(nbr_ltdof.GetJ()); }
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);
if (nb_connections > 0) { ext_ldof.CopyFrom(nbr_ldof.GetJ()); }
ext_ldof.CopyFrom(nbr_ldof.GetJ());
ext_ldof.GetMemory().UseDevice(true);
ext_buf.SetSize(nb_connections);
ext_buf.UseDevice(true);
+280 -3
View File
@@ -9,16 +9,278 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "det.hpp"
#include "../quadinterpolator.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../fem/kernels.hpp"
#include "../../linalg/kernels.hpp"
using namespace mfem;
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;
@@ -40,12 +302,27 @@ void InitDetKernels()
}
} // namespace quadrature_interpolator
} // namespace internal
/// @cond Suppress_Doxygen_warnings
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Fallback(
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(
int DIM, int SDIM, int D1D, int Q1D)
{
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
-304
View File
@@ -1,304 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_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
+27 -9
View File
@@ -5122,32 +5122,33 @@ real_t TMOP_Integrator::GetSurfaceFittingWeight()
void TMOP_Integrator::EnableNormalization(const GridFunction &x)
{
ComputeNormalizationEnergies(x, metric_normal, lim_normal);
ComputeNormalizationEnergies(x, metric_normal, lim_normal, surf_fit_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[2];
ComputeNormalizationEnergies(x, loc[0], loc[1]);
real_t rdc[2];
MPI_Allreduce(loc, rdc, 2, MPITypeMap<real_t>::mpi_type, MPI_SUM,
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,
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 &lim_energy,
real_t &surf_fit_gf_energy)
{
metric_energy = 0.0;
lim_energy = 0.0;
if (PA.enabled)
{
MFEM_VERIFY(PA.E.Size() > 0, "Must be called after AssemblePA!");
@@ -5190,6 +5191,9 @@ 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);
@@ -5221,7 +5225,21 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
lim_energy += weight;
}
// TODO: Normalization of the surface fitting term.
// 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);
}
}
}
}
// Cases when integration is not over the target element, or when the
+2 -1
View File
@@ -2038,7 +2038,8 @@ protected:
} PA;
void ComputeNormalizationEnergies(const GridFunction &x,
real_t &metric_energy, real_t &lim_energy);
real_t &metric_energy, real_t &lim_energy,
real_t &surf_fit_gf_energy);
void AssembleElementVectorExact(const FiniteElement &el,
ElementTransformation &T,
-1
View File
@@ -39,7 +39,6 @@ list(APPEND HDRS
arrays_by_name.hpp
backends.hpp
binaryio.hpp
complex_type.hpp
cuda.hpp
device.hpp
error.hpp
+1 -5
View File
@@ -326,11 +326,7 @@ public:
the Size to match this Capacity after this.*/
template <typename U>
inline void CopyFrom(const U *src)
{
if (!begin() || size == 0) { return; }
MFEM_ASSERT(begin() && src, "Error in Array::CopyFrom");
std::memcpy(begin(), src, MemoryUsage());
}
{ std::memcpy(begin(), src, MemoryUsage()); }
/// STL-like begin. Returns pointer to the first element of the array.
inline T* begin() { return data; }
-1
View File
@@ -62,7 +62,6 @@
#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
-125
View File
@@ -1,125 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_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
-1
View File
@@ -47,7 +47,6 @@
#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
-36
View File
@@ -16,7 +16,6 @@
#include "../fem/ceed/interface/util.hpp"
#endif
#ifdef MFEM_USE_MPI
#include "communication.hpp"
#include "../linalg/hypre.hpp"
#endif
@@ -146,11 +145,6 @@ Device::Device()
Configure(device);
device_env = true;
}
if (GetEnv("MFEM_GPU_AWARE_MPI"))
{
SetGPUAwareMPI(true);
}
}
Device::~Device()
@@ -202,29 +196,6 @@ 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)
@@ -342,13 +313,6 @@ 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;
}
-4
View File
@@ -198,10 +198,6 @@ 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'
+1 -3
View File
@@ -47,9 +47,7 @@
#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)
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) \
if(const int i=hipThreadIdx_ ##k; i<N)
for(int i=hipThreadIdx_ ##k; i<N; i+=hipBlockDim_ ##k)
#endif
namespace mfem
-2
View File
@@ -21,7 +21,6 @@ list(APPEND SRCS
blockvector.cpp
complex_densemat.cpp
complex_operator.cpp
complex_vector.cpp
constraints.cpp
densemat.cpp
symmat.cpp
@@ -48,7 +47,6 @@ list(APPEND HDRS
blockvector.hpp
complex_densemat.hpp
complex_operator.hpp
complex_vector.hpp
constraints.hpp
densemat.hpp
dinvariants.hpp
-302
View File
@@ -9,7 +9,6 @@
// 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>
@@ -17,8 +16,6 @@
namespace mfem
{
using namespace std;
DenseMatrix & ComplexDenseMatrix::real()
{
MFEM_ASSERT(Op_Real_, "ComplexDenseMatrix has no real part!");
@@ -1020,303 +1017,4 @@ 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
-215
View File
@@ -13,7 +13,6 @@
#define MFEM_COMPLEX_DENSEMAT
#include "complex_operator.hpp"
#include "../general/complex_type.hpp"
#include <complex>
namespace mfem
@@ -242,220 +241,6 @@ 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
-424
View File
@@ -1,424 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../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;
}
}
-479
View File
@@ -1,479 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_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
-28
View File
@@ -4405,32 +4405,4 @@ 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
-8
View File
@@ -24,7 +24,6 @@ class DenseMatrix : public Matrix
{
friend class DenseTensor;
friend class DenseMatrixInverse;
friend class ComplexTypeDenseMatrix;
private:
Memory<real_t> data;
@@ -1330,13 +1329,6 @@ 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)
-12
View File
@@ -2574,18 +2574,6 @@ 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,
-7
View File
@@ -42,13 +42,6 @@ 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,
-2
View File
@@ -80,8 +80,6 @@ inline real_t rand_real()
/// Vector data type.
class Vector
{
friend class ComplexVector;
protected:
Memory<real_t> data;
+27 -35
View File
@@ -26,12 +26,6 @@
}\
}
#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
{
@@ -141,18 +135,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(const std::string &fpath, int flags = NC_CLOBBER);
void PrintExodusII(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, const std::string &fpath,
static void PrintExodusII(Mesh & mesh, std::string fpath,
int flags = NC_CLOBBER);
protected:
/// @brief Closes any open file and creates a NetCDF file using selected flags.
void OpenExodusII(const std::string &fpath, int flags);
void OpenExodusII(std::string fpath, int flags);
/// @brief Closes any open file.
void CloseExodusII();
@@ -173,9 +167,9 @@ protected:
std::unordered_set<int> GenerateUniqueNodeIDs();
/// @brief Populates vectors with x, y, z coordinates from mesh.
void ExtractVertexCoordinates(std::vector<real_t> &coordx,
std::vector<real_t> &coordy,
std::vector<real_t> &coordz);
void ExtractVertexCoordinates(std::vector<double> & coordx,
std::vector<double> & coordy,
std::vector<double> & coordz);
/// @brief Writes node connectivity for a particular block.
/// @param block_id The block to write to the file.
@@ -193,7 +187,7 @@ protected:
/// @brief Writes the number of elements in the mesh.
void WriteNumOfElements();
/// @brief Writes the floating-point word size (sizeof(real_t)).
/// @brief Writes the floating-point word size (4 == float; 8 == double).
void WriteFloatingPointWordSize();
/// @brief Writes the API version.
@@ -297,7 +291,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);
}
@@ -368,7 +362,7 @@ void ExodusIIWriter::WriteExodusIIMeshInformation()
WriteNodeSets();
}
void ExodusIIWriter::PrintExodusII(const std::string &fpath, int flags)
void ExodusIIWriter::PrintExodusII(std::string fpath, int flags)
{
OpenExodusII(fpath, flags);
@@ -380,7 +374,7 @@ void ExodusIIWriter::PrintExodusII(const std::string &fpath, int flags)
mfem::out << "Mesh successfully written to Exodus II file" << std::endl;
}
void ExodusIIWriter::PrintExodusII(Mesh &mesh, const std::string &fpath,
void ExodusIIWriter::PrintExodusII(Mesh & mesh, std::string fpath,
int flags)
{
ExodusIIWriter writer(mesh);
@@ -388,7 +382,7 @@ void ExodusIIWriter::PrintExodusII(Mesh &mesh, const std::string &fpath,
writer.PrintExodusII(fpath, flags);
}
void ExodusIIWriter::OpenExodusII(const std::string &fpath, int flags)
void ExodusIIWriter::OpenExodusII(std::string fpath, int flags)
{
CloseExodusII(); // Close any open files.
@@ -428,7 +422,7 @@ void ExodusIIWriter::WriteNumOfElements()
void ExodusIIWriter::WriteFloatingPointWordSize()
{
const int word_size = sizeof(real_t);
const int word_size = 8;
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_FLOATING_POINT_WORD_SIZE_LABEL,
NC_INT, 1,
&word_size);
@@ -436,15 +430,13 @@ void ExodusIIWriter::WriteFloatingPointWordSize()
void ExodusIIWriter::WriteAPIVersion()
{
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_API_VERSION_LABEL, MFEM_NETCDF_REAL_T,
1,
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_API_VERSION_LABEL, NC_FLOAT, 1,
&ExodusIILabels::EXODUS_API_VERSION);
}
void ExodusIIWriter::WriteDatabaseVersion()
{
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_DATABASE_VERSION_LABEL,
MFEM_NETCDF_REAL_T, 1,
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_DATABASE_VERSION_LABEL, NC_FLOAT, 1,
&ExodusIILabels::EXODUS_DATABASE_VERSION);
}
@@ -615,25 +607,25 @@ void ExodusIIWriter::WriteNodalCoordinates()
DefineDimension("num_nodes", num_nodes, &num_nodes_id);
// 3. Extract the nodal coordinates.
// NB: writes in format real_t (double or float); ndims = 1 (vector).
// NB: assume doubles (could be floats!); ndims = 1 (vector).
// https://docs.unidata.ucar.edu/netcdf-c/current/group__variables.html#gac7e8662c51f3bb07d1fc6d6c6d9052c8
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);
std::vector<double> coordx(num_nodes);
std::vector<double> coordy(num_nodes);
std::vector<double> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
ExtractVertexCoordinates(coordx, coordy, coordz);
// 4. Define and put the nodal coordinates.
DefineAndPutVar(ExodusIILabels::EXODUS_COORDX_LABEL, MFEM_NETCDF_REAL_T, 1,
DefineAndPutVar(ExodusIILabels::EXODUS_COORDX_LABEL, NC_DOUBLE, 1,
&num_nodes_id,
coordx.data());
DefineAndPutVar(ExodusIILabels::EXODUS_COORDY_LABEL, MFEM_NETCDF_REAL_T, 1,
DefineAndPutVar(ExodusIILabels::EXODUS_COORDY_LABEL, NC_DOUBLE, 1,
&num_nodes_id,
coordy.data());
if (mesh.Dimension() == 3)
{
DefineAndPutVar(ExodusIILabels::EXODUS_COORDZ_LABEL, MFEM_NETCDF_REAL_T, 1,
DefineAndPutVar(ExodusIILabels::EXODUS_COORDZ_LABEL, NC_DOUBLE, 1,
&num_nodes_id,
coordz.data());
}
@@ -778,9 +770,9 @@ void ExodusIIWriter::WriteNodeConnectivityForBlock(const int block_id)
}
void ExodusIIWriter::ExtractVertexCoordinates(std::vector<real_t> & coordx,
std::vector<real_t> & coordy,
std::vector<real_t> & coordz)
void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
std::vector<double> & coordy,
std::vector<double> & coordz)
{
if (mesh.GetNodes()) // Higher-order.
{
@@ -790,7 +782,7 @@ void ExodusIIWriter::ExtractVertexCoordinates(std::vector<real_t> & coordx,
sorted_node_ids.assign(unordered_node_ids.begin(), unordered_node_ids.end());
std::sort(sorted_node_ids.begin(), sorted_node_ids.end());
real_t coordinates[3];
double coordinates[3];
for (size_t i = 0; i < sorted_node_ids.size(); i++)
{
int node_id = sorted_node_ids[i];
@@ -810,7 +802,7 @@ void ExodusIIWriter::ExtractVertexCoordinates(std::vector<real_t> & coordx,
{
for (int ivertex = 0; ivertex < mesh.GetNV(); ivertex++)
{
real_t *coordinates = mesh.GetVertex(ivertex);
double * coordinates = mesh.GetVertex(ivertex);
coordx[ivertex] = coordinates[0];
coordy[ivertex] = coordinates[1];
@@ -1088,4 +1080,4 @@ void ExodusIIWriter::CheckNodalFESpaceIsSecondOrderH1() const
#endif
}
}
-1
View File
@@ -2994,7 +2994,6 @@ 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
+8 -11
View File
@@ -588,10 +588,9 @@ protected:
void Loader(std::istream &input, int generate_edges = 0,
std::string parse_tag = "");
/** @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 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,
@@ -2483,12 +2482,10 @@ public:
/// Print the mesh to the given stream using Netgen/Truegrid format.
virtual void PrintXG(std::ostream &os = mfem::out) const;
/** @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 '#'. */
/// 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); }
@@ -2540,7 +2537,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
+13 -3
View File
@@ -802,11 +802,21 @@ struct BufferReader : BufferReaderBase
{
// Each "data block" is preceded by a header that is either UInt32 or
// UInt64. The rest of the data follows.
MFEM_VERIFY(sizeof(F)*n == ReadHeaderEntry(header_buf),
"AppendedData: wrong data size");
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");
}
if (std::is_same_v<T, F>)
if (std::is_same<T, F>::value)
{
// Special case: no type conversions necessary, so can just memcpy
memcpy(dest, buf, sizeof(T)*n);
+27 -99
View File
@@ -9,13 +9,8 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "nurbs.hpp"
#include "point.hpp"
#include "segment.hpp"
#include "quadrilateral.hpp"
#include "hexahedron.hpp"
#include "../fem/gridfunc.hpp"
#include "mesh_headers.hpp"
#include "../fem/fem.hpp"
#include "../general/text.hpp"
#include <fstream>
@@ -38,7 +33,6 @@ KnotVector::KnotVector(istream &input)
knot.Load(input, NumOfControlPoints + Order + 1);
GetElements();
coarse = false;
}
KnotVector::KnotVector(int order, int NCP)
@@ -47,13 +41,12 @@ 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.
@@ -93,7 +86,6 @@ KnotVector::KnotVector(int order, const Vector& intervals,
++NumOfElements;
}
}
coarse = false;
}
KnotVector &KnotVector::operator=(const KnotVector &kv)
@@ -151,7 +143,7 @@ void KnotVector::UniformRefinement(Vector &newknots, int rf) const
{
for (int m = 1; m < rf; ++m)
{
newknots(j) = ((1.0 - (m * h)) * knot(i)) + (m * h * knot(i+1));
newknots(j) = m * h * (knot(i) + knot(i+1));
j++;
}
}
@@ -340,7 +332,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
{
@@ -367,7 +359,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
{
@@ -425,7 +417,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
{
@@ -545,11 +537,11 @@ void KnotVector::FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const
int i = j - d;
if (isElement(i))
{
arg1 = std::numeric_limits<real_t>::epsilon() / 2_r;
arg1 = 1e-16;
CalcShape(shape, i, arg1);
max1 = shape[d];
arg2 = 1_r - arg1;
arg2 = 1-(1e-16);
CalcShape(shape, i, arg2);
max2 = shape[d];
@@ -587,9 +579,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, bool reuse_inverse)
void KnotVector::FindInterpolant(Array<Vector*> &x)
{
int order = GetOrder();
int ncp = GetNCP();
@@ -597,93 +589,29 @@ void KnotVector::FindInterpolant(Array<Vector*> &x, bool reuse_inverse)
// 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
#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
Vector shape(order+1);
DenseMatrix A(ncp,ncp);
A = 0.0;
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 col = i_args[i] + p;
if (col < i)
{
KL = std::max(KL, i - col);
}
else if (i < col)
{
KU = std::max(KU, col - i);
}
A(i,i_args[i] + p) = shape[p];
}
}
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(); }
// Solve problems
A.Invert();
Vector tmp;
for (int i = 0; i < x.Size(); i++)
for (int i= 0; i < x.Size(); i++)
{
tmp = *x[i];
A_coll_inv.Mult(tmp, *x[i]);
A.Mult(tmp,*x[i]);
}
#endif
}
int KnotVector::findKnotSpan(real_t u) const
@@ -1485,7 +1413,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)
@@ -1503,8 +1431,8 @@ void NURBSPatch::DegreeElevate(int dir, int t)
KnotVector &oldkv = *kv[dir];
oldkv.GetElements();
auto *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
oldkv.GetNCP() + oldkv.GetNE()*t);
NURBSPatch *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
oldkv.GetNCP() + oldkv.GetNE()*t);
NURBSPatch &newp = *newpatch;
KnotVector &newkv = *newp.GetKV(dir);
@@ -2449,7 +2377,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");
@@ -4659,7 +4587,7 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
// Flip vector
int size = pkvc[d]->Size();
int ns = static_cast<int>(ceil(size/2.0));
int ns = ceil(size/2.0);
for (int j = 0; j < ns; j++)
{
real_t tmp = apb - pkvc[d]->Elem(j);
@@ -4719,7 +4647,7 @@ void NURBSExtension::KnotRemove(Array<Vector *> &kv, real_t tol)
// Flip vector
int size = pkvc[d]->Size();
int ns = static_cast<int>(ceil(size/2.0));
int ns = ceil(size/2.0);
for (int j = 0; j < ns; j++)
{
real_t tmp = apb - pkvc[d]->Elem(j);
+7 -20
View File
@@ -22,6 +22,7 @@
#include "../general/communication.hpp"
#endif
#include <iostream>
#include <set>
namespace mfem
{
@@ -54,7 +55,7 @@ protected:
public:
/// Create an empty KnotVector.
KnotVector() = default;
KnotVector() { }
/** @brief Create a KnotVector by reading data from stream @a input. Two
integers are read, for order and number of control points. */
@@ -73,7 +74,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; }
@@ -143,13 +144,8 @@ 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.
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);
curve are returned in @a x in the same form. */
void FindInterpolant(Array<Vector*> &x);
/** 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
@@ -207,14 +203,6 @@ 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
};
@@ -298,7 +286,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.
@@ -713,8 +701,7 @@ public:
NURBSExtension(Mesh *mesh_array[], int num_pieces);
NURBSExtension(const Mesh *patch_topology,
const Array<const NURBSPatch*> &patches_);
NURBSExtension(const Mesh *patch_topology, const Array<const NURBSPatch*> p);
/// Copy assignment not supported.
NURBSExtension& operator=(const NURBSExtension&) = delete;
+1 -2
View File
@@ -3132,12 +3132,11 @@ 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) = AsConst(pNodes->FaceNbrData())(vdofs[n*k+j]);
pointmat(k,j) = (pNodes->FaceNbrData())(vdofs[n*k+j]);
}
}
+9 -1
View File
@@ -257,7 +257,15 @@ template <typename SubMeshT>
void AddBoundaryElements(SubMeshT &mesh,
const std::unordered_map<int,int> &lface_to_boundary_attribute)
{
const int num_codim_1 = mesh.GetNumFaces();
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; }
}();
if (mesh.Dimension() == 3)
{
+42 -44
View File
@@ -84,7 +84,7 @@ void VTKHDF::EnsureSteps()
}
hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
Dims &dims)
int ndims)
{
const char *name_c = name.c_str();
@@ -94,23 +94,20 @@ hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
if (status == 0)
{
// Dataset does not exist, create it.
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 dims(ndims);
Dims maxdims(ndims, H5S_UNLIMITED);
const hid_t fspace = H5Screate_simple(ndims, dims, maxdims);
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] = dims[i];
chunk_size_bytes /= dims[i];
chunk[i] = 16;
chunk_size_bytes /= 16;
}
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)
@@ -127,19 +124,7 @@ hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
else if (status > 0)
{
// Dataset exists, open it.
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;
return H5Dopen2(f, name_c, H5P_DEFAULT);
}
else
{
@@ -175,13 +160,27 @@ 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;
Dims dims = globsize;
const hid_t d = EnsureDataset(f, name, GetTypeID<T>(), dims);
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);
}
// Write the new entry.
const hid_t dspace = H5Dget_space(d);
Dims start(ndims);
start[0] = dims[0] - globsize[0] + offset;
start[0] = old_size + offset;
Dims count(ndims);
count[0] = locsize;
for (int i = 1; i < ndims; ++i) { count[i] = globsize[i]; }
@@ -335,14 +334,14 @@ void VTKHDF::Truncate(const real_t t)
}
// Index of found time index (may be 'one-past-the-end' if not found)
const ptrdiff_t i = std::distance(tvals.begin(), it);
const int 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_ulong(vtk, "Steps", "NSteps", &nsteps, 1);
H5LTset_attribute_int(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
@@ -510,7 +509,7 @@ void VTKHDF::UpdateSteps(real_t t)
// Set the NSteps attribute
++nsteps;
H5LTset_attribute_ulong(steps, ".", "NSteps", &nsteps, 1);
H5LTset_attribute_int(steps, ".", "NSteps", &nsteps, 1);
AppendValue(steps, "Values", t);
AppendValue(steps, "PartOffsets", part_offset);
@@ -619,16 +618,16 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
for (int i = 0; i < pmat.Width(); i++)
{
points.push_back(FP_T(pmat(0,i)));
if (pmat.Height() > 1) { points.push_back(FP_T(pmat(1,i))); }
points.push_back(pmat(0,i));
if (pmat.Height() > 1) { points.push_back(pmat(1,i)); }
else { points.push_back(0.0); }
if (pmat.Height() > 2) { points.push_back(FP_T(pmat(2,i))); }
if (pmat.Height() > 2) { points.push_back(pmat(2,i)); }
else { points.push_back(0.0); }
}
}
}
const int ne_0 = mesh.GetNE();
const hsize_t ne_0 = mesh.GetNE();
const hsize_t ne = high_order ? ne_0 : ne_ref;
AppendParData(vtk, "NumberOfPoints", 1, mpi_rank, mpi_dims, &np);
@@ -658,7 +657,7 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
if (high_order)
{
Array<int> local_connectivity;
for (int e = 0; e < int(ne); ++e)
for (size_t e = 0; e < ne; ++e)
{
offsets[e] = off;
const Geometry::Type geom = mesh.GetElementGeometry(e);
@@ -676,7 +675,7 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
{
int off_0 = 0;
int e_ref = 0;
for (int e = 0; e < ne_0; ++e)
for (hsize_t e = 0; e < ne_0; ++e)
{
const Geometry::Type geom = mesh.GetElementGeometry(e);
const int nv = get_nv(e);
@@ -715,13 +714,12 @@ 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 (int e = 0; e < ne_0; ++e)
for (hsize_t e = 0; e < ne_0; ++e)
{
const int ne_ref_e = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref_e; ++i, ++e_ref)
const int ne_ref = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref; ++i, ++e_ref)
{
cell_types[e_ref] = static_cast<unsigned char>(
vtk_geom_map[mesh.GetElementGeometry(e)]);
cell_types[e_ref] = vtk_geom_map[mesh.GetElementGeometry(e)];
}
}
AppendParData(vtk, "Types", ne, e_offset, Dims({ne_total}),
@@ -734,11 +732,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 (int e = 0; e < ne_0; ++e)
for (hsize_t e = 0; e < ne_0; ++e)
{
const int attr = mesh.GetAttribute(e);
const int ne_ref_e = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref_e; ++i, ++e_ref)
const int ne_ref = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref; ++i, ++e_ref)
{
attributes[e_ref] = attr;
}
@@ -774,7 +772,7 @@ void VTKHDF::SaveGridFunction(const GridFunction &gf, const std::string &name)
{
for (int vd = 0; vd < vdim; ++vd)
{
point_values[off] = FP_T(vec_val(vd, i));
point_values[off] = vec_val(vd, i);
++off;
}
}
+7 -9
View File
@@ -76,14 +76,14 @@ private:
/// Wrapper for storing dataset dimensions (max ndims is 2D in VTKHDF).
struct Dims
{
static constexpr size_t MAX_NDIMS = 2;
static constexpr int 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(int(data_.size()))
Dims(std::initializer_list<T> data_) : Dims(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.
unsigned long nsteps = 0;
int 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;
long sequence = -1;
long nodes_sequence = -1;
int sequence = -1;
int nodes_sequence = -1;
bool high_order = true;
int ref = -1;
public:
@@ -187,10 +187,8 @@ private:
/// The rank (number of dimensions) of the dataset is given by @a ndims and
/// its data type is given by @a type.
///
/// 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);
/// 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);
/// @brief Ensure the named group is open, creating it if needed. Set @a
/// group to the ID.
-1
View File
@@ -224,6 +224,5 @@ int main (int argc, char *argv[])
}
delete metric;
delete fec_mesh;
return 0;
}
+12 -2
View File
@@ -11,9 +11,14 @@
if (MFEM_USE_MPI)
list(APPEND NAVIER_COMMON_SOURCES
navier_solver.cpp)
navier_solver.cpp
incompressible_navier_solver.cpp
stokes_solver.cpp)
list(APPEND NAVIER_COMMON_HEADERS
navier_solver.hpp)
navier_solver.hpp
incompressible_navier_solver.hpp
stokes_solver.hpp)
convert_filenames_to_full_paths(NAVIER_COMMON_SOURCES)
convert_filenames_to_full_paths(NAVIER_COMMON_HEADERS)
@@ -52,6 +57,11 @@ if (MFEM_USE_MPI)
${NAVIER_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(incompNS_2Dtest
MAIN incompNS_2Dtest.cpp
${NAVIER_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(navier_turbchan
MAIN navier_turbchan.cpp
${NAVIER_COMMON_FILES}
+134
View File
@@ -0,0 +1,134 @@
// 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"
#include <fstream>
using namespace mfem;
using namespace incompressible_navier;
void vel(const Vector &x, real_t t, Vector &u)
{
real_t xi = x(0);
real_t 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);
}
}
int main(int argc, char *argv[])
{
Mpi::Init(argc, argv);
Hypre::Init();
int serial_refinements = 1;
int vOrder = 2;
int pOrder = 1;
int tOrder = 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;
//Mesh *mesh = new Mesh("box-cylinder.mesh");
Mesh mesh = Mesh::MakeCartesian2D(90, 30, mfem::Element::QUADRILATERAL, true, 3, 1);
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, vOrder, pOrder, tOrder, kin_vis);
flowsolver.EnablePA(false);
// // 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);
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 (t + dt >= t_final - dt / 2)
{
last_step = true;
}
flowsolver.Step(t, dt, step);
if (step % 1 == 0)
{
pvdc.SetCycle(step);
pvdc.SetTime(t);
pvdc.Save();
}
if (Mpi::Root())
{
printf("%11s %11s\n", "Time", "dt");
printf("%.5E %.5E\n", t, dt);
fflush(stdout);
}
}
// flowsolver.PrintTimingData();
delete pmesh;
return 0;
}
@@ -0,0 +1,515 @@
// 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"
#include "../../general/forall.hpp"
#include <fstream>
#include <iomanip>
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), velGF(torder_+1,nullptr), pGF(torder_+1,nullptr)
{
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);
// 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;
}
int vfes_truevsize = vfes->GetTrueVSize();
int pfes_truevsize = pfes->GetTrueVSize();
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->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->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->SetMaxIter(1000);
}
void IncompressibleNavierSolver::UpdateTimestepHistory(real_t dt)
{
}
void IncompressibleNavierSolver::Step(real_t &time, real_t dt, int current_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];
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;
}
@@ -0,0 +1,362 @@
// 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.
#ifndef MFEM_INCOMP_NAVIER_SOLVER_HPP
#define MFEM_INCOMP_NAVIER_SOLVER_HPP
#define INCOMP_NAVIER_VERSION 0.1
#include "mfem.hpp"
namespace mfem
{
namespace 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)
{
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);
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.
/**
* The summary shows the timing in seconds in the first row of
*
*/
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.
int velorder;
int porder;
int torder;
/// Kinematic viscosity (dimensionless).
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;
ParBilinearForm *psiBForm = nullptr;
ParBilinearForm *pBForm = nullptr;
ParLinearForm *velLForm = nullptr;
ParLinearForm *psiLForm = nullptr;
ParLinearForm *pLForm = nullptr;
std::vector<ParGridFunction*> velGF;
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-10;
real_t rtol_psisolve = 1e-10;
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!"
real_t rtol_psolve = 1e-12;
real_t rtol_psisolve = 1e-6;
real_t rtol_velsolve = 1e-8;
#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 incompressible_navier
} // namespace mfem
#endif
+119
View File
@@ -0,0 +1,119 @@
#include "stokes_solver.hpp"
namespace mfem {
StokesOperator::StokesOperator(ParFiniteElementSpace &vel_fes,
ParFiniteElementSpace &pres_fes):
Operator(vel_fes.GetTrueVSize()+pres_fes.GetTrueVSize()),
vfes(vel_fes),
pfes(pres_fes),
offsets({0, vel_fes.GetTrueVSize(), pres_fes.GetTrueVSize()}),
intrules(0, Quadrature1D::GaussLobatto),
zero_coeff(0.0)
{
if (vel_fes.GetParMesh()->bdr_attributes.Size() > 0)
{
vel_ess_bdr.SetSize(vel_fes.GetParMesh()->bdr_attributes.Max());
vel_ess_bdr = 0.0;
pres_ess_bdr.SetSize(vel_fes.GetParMesh()->bdr_attributes.Max());
pres_ess_bdr = 0.0;
}
vfes.GetEssentialTrueDofs(vel_ess_bdr, vel_ess_tdofs);
pfes.GetEssentialTrueDofs(pres_ess_bdr, pres_ess_tdofs);
offsets.PartialSum();
vel_bc_gf.reset(new ParGridFunction(&vfes));
*vel_bc_gf = 0.0; //set the velocity grid function to zero
pres_bc_gf.reset(new ParGridFunction(&pfes));
*pres_bc_gf = 0.0; //set the pressure grid function to zero
// The nonlinear convective integrators use over-integration (dealiasing) as
// a stabilization mechanism.
ir_nl = intrules.Get(vfes.GetFE(0)->GetGeomType(),
(int)(ceil(1.5 * 2*(vel_fes.GetOrder(0)+1) - 3)));
ir = intrules.Get(vfes.GetFE(0)->GetGeomType(),
(int)(2*(vel_fes.GetOrder(0)+1) - 3));
ir_face = intrules.Get(vfes.GetFaceElement(0)->GetGeomType(),
(int)(2*(vel_fes.GetOrder(0)+1) - 3));
b11_form=nullptr;
b22_form=nullptr;
b12_form=nullptr;
b21_form=nullptr;
}
void StokesOperator::SetVelBC(std::vector<VelDirichletBC>& vvbc)
{
for(auto vbc=vvbc.begin();vbc!=vvbc.end();vbc++)
{
for (int i = 0; i < vbc->second->Size(); i++)
{
if (*(vbc->second)[i] == 1)
{
vel_ess_bdr[i] = 1;
}
}
}
vfes.GetEssentialTrueDofs(vel_ess_bdr, vel_ess_tdofs);
}
void StokesOperator::SetPressBC(std::vector<PresDirichletBC>& vpbc)
{
for(auto pbc=vpbc.begin();pbc!=vpbc.end();pbc++)
{
for(int i=0;i<pbc->second->Size();i++){
if (*(pbc->second)[i] == 1)
{
vel_ess_bdr[i] = 1;
}
}
}
pfes.GetEssentialTrueDofs(pres_ess_bdr, pres_ess_tdofs);
}
void StokesOperator::Mult(const Vector &x, Vector &y) const
{
}
void StokesOperator::Setup()
{
BilinearFormIntegrator *integrator;
delete b11_form;
b11_form=new ParBilinearForm(&vfes);
integrator=new ElasticityIntegrator(zero_coeff,*viscosity);
integrator->SetIntRule(&ir);
b11_form->AddDomainIntegrator(integrator);
delete b12_form;
b12_form=new ParMixedBilinearForm(&pfes,&vfes);
integrator=new VectorDivergenceIntegrator();
integrator->SetIntRule(&ir);
b12_form->AddDomainIntegrator(integrator);
delete b21_form;
b21_form=new ParMixedBilinearForm(&vfes,&pfes);
integrator=new GradientIntegrator();
integrator->SetIntRule(&ir);
b21_form->AddDomainIntegrator(integrator);
if (matrix_free)
{
b11_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
b12_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
b21_form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
}
}
+76
View File
@@ -0,0 +1,76 @@
#ifndef STOKESSOLVER_H
#define STOKESSOLVER_H
#define STOKES_VERSION 0.1
#include "mfem.hpp"
namespace mfem {
using VelDirichletBC = std::pair<VectorCoefficient *, Array<int> *>;
using PresDirichletBC = std::pair<Coefficient *, Array<int> *>;
class StokesOperator:public Operator
{
public:
StokesOperator(ParFiniteElementSpace &vel_fes,
ParFiniteElementSpace &pres_fes);
void SetVelBC(std::vector<VelDirichletBC>& vvbc);
void SetPressBC(std::vector<PresDirichletBC>& vpbc);
virtual
void Mult(const Vector &x, Vector &y) const override;
const Array<int>& GetOffsets() const
{
return offsets;
}
void Setup();
void Assemble();
private:
ParFiniteElementSpace &vfes;
ParFiniteElementSpace &pfes;
// ParGridFunction &kinematic_viscosity;
std::unique_ptr<ParGridFunction> vel_bc_gf;
std::unique_ptr<ParGridFunction> pres_bc_gf;
Array<int> vel_ess_bdr;
Array<int> pres_ess_bdr;
Array<int> vel_ess_tdofs;
Array<int> pres_ess_tdofs;
bool matrix_free;
Array<int> offsets;
IntegrationRules intrules;
IntegrationRule ir; //general integraion rule
IntegrationRule ir_nl; //non-linear integration rule
IntegrationRule ir_face; //face integration rule
ConstantCoefficient zero_coeff;
std::unique_ptr<Coefficient> viscosity;
ParBilinearForm *b11_form; //velocity
ParBilinearForm *b22_form; //pressure
ParMixedBilinearForm *b12_form; //mixed (velocity,pressure)
ParMixedBilinearForm *b21_form; //mized (pressure,velocity)
BlockOperator* A;
};
}
#endif // STOKESSOLVER_H
-12
View File
@@ -80,10 +80,6 @@ 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
@@ -251,14 +247,6 @@ 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)
+2 -9
View File
@@ -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_surface
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
@@ -158,13 +158,6 @@ 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
@@ -199,6 +192,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 *-Surface.mesh
@rm -f naca-cmesh.mesh sol_?.gf
@rm -rf Example1* Example3* Example5* Solenoidal_* ParaView
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh solution.dat
-655
View File
@@ -1,655 +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.
//
// --------------------------------------------------------
// 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;
}
}
+42 -45
View File
@@ -52,13 +52,12 @@
// (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;
@@ -84,54 +83,48 @@ real_t natural_bc(const Vector & x);
D: subset of the boundary where natural boundary condition is imposed. */
class DarcyProblem
{
OperatorPtr M_, B_;
Vector rhs_, ess_data_;
ParGridFunction u_, p_;
OperatorPtr M_;
OperatorPtr B_;
Vector rhs_;
Vector ess_data_;
ParGridFunction u_;
ParGridFunction p_;
ParMesh mesh_;
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_;
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);
const HypreParMatrix& GetM() const { return *M_.As<HypreParMatrix>(); }
const HypreParMatrix& GetB() const { return *B_.As<HypreParMatrix>(); }
HypreParMatrix& GetM() { return *M_.As<HypreParMatrix>(); }
HypreParMatrix& GetB() { 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() { return mVarf_; }
ParMixedBilinearForm& GetBform() { return bVarf_; }
ParBilinearForm* GetMform() const { return mVarf_; }
ParMixedBilinearForm* GetBform() const { 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),
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),
: mesh_(MPI_COMM_WORLD, mesh), ucoeff_(mesh.Dimension(), u_exact),
pcoeff_(p_exact), dfs_spaces_(order, num_refs, &mesh_, ess_bdr, dfs_param),
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, ""))
@@ -160,20 +153,24 @@ DarcyProblem::DarcyProblem(Mesh &mesh, int num_refs, int order,
gform.AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
gform.Assemble();
mVarf_.AddDomainIntegrator(new VectorFEMassIntegrator(mass_coeff));
mVarf_.ComputeElementMatrices();
mVarf_.Assemble();
mVarf_.EliminateEssentialBC(ess_bdr, u_, fform);
mVarf_ = new ParBilinearForm(dfs_spaces_.GetHdivFES());
bVarf_ = new ParMixedBilinearForm(dfs_spaces_.GetHdivFES(),
dfs_spaces_.GetL2FES());
mVarf_.Finalize();
M_.Reset(mVarf_.ParallelAssemble());
mVarf_->AddDomainIntegrator(new VectorFEMassIntegrator(mass_coeff));
mVarf_->ComputeElementMatrices();
mVarf_->Assemble();
mVarf_->EliminateEssentialBC(ess_bdr, u_, fform);
bVarf_.AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf_.Assemble();
bVarf_.SpMat() *= -1.0;
bVarf_.EliminateTrialEssentialBC(ess_bdr, u_, gform);
bVarf_.Finalize();
B_.Reset(bVarf_.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());
rhs_.SetSize(M_->NumRows() + B_->NumRows());
Vector rhs_block0(rhs_.GetData(), M_->NumRows());
@@ -344,8 +341,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);
const HypreParMatrix &M = darcy.GetM();
const HypreParMatrix &B = darcy.GetB();
HypreParMatrix& M = darcy.GetM();
HypreParMatrix& B = darcy.GetB();
const DFSData& DFS_data = darcy.GetDFSData();
delete mesh;
+15 -12
View File
@@ -14,27 +14,29 @@
namespace mfem
{
BlockFESpaceOperator::BlockFESpaceOperator(const FESVector &fespaces):
BlockFESpaceOperator::BlockFESpaceOperator(const
std::vector<const FiniteElementSpace*> &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.
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));
prolongation.SetDiagonalBlock(i,
const_cast<Operator *>(fespaces[i]->GetProlongationMatrix()));
restriction.SetDiagonalBlock(i,
const_cast<Operator *>(fespaces[i]->GetRestrictionOperator()));
}
}
int BlockFESpaceOperator::GetHeight(const FESVector &fespaces)
int BlockFESpaceOperator::GetHeight(const std::vector<const FiniteElementSpace*>
&fespaces)
{
int height = 0;
for (size_t i = 0; i < fespaces.size(); i++)
@@ -44,7 +46,8 @@ int BlockFESpaceOperator::GetHeight(const FESVector &fespaces)
return height;
}
Array<int> BlockFESpaceOperator::GetBlockOffsets(const FESVector &fespaces)
Array<int> BlockFESpaceOperator::GetBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces)
{
Array<int> offsets(fespaces.size()+1);
offsets[0] = 0;
@@ -57,8 +60,8 @@ Array<int> BlockFESpaceOperator::GetBlockOffsets(const FESVector &fespaces)
return offsets;
}
Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const FESVector
&fespaces)
Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces)
{
Array<int> offsets(fespaces.size()+1);
offsets[0] = 0;
@@ -80,8 +83,8 @@ Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const FESVector
return offsets;
}
Array<int> BlockFESpaceOperator::GetResRowBlockOffsets(const FESVector
&fespaces)
Array<int> BlockFESpaceOperator::GetResRowBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces)
{
Array<int> offsets(fespaces.size()+1);
std::cout << "fespaces.size() = " << fespaces.size() << std::endl;
+15 -10
View File
@@ -25,8 +25,7 @@ namespace mfem
/// L-Vectors. For example, a block may be a BilinearForm.
class BlockFESpaceOperator : public Operator
{
using FESVector = std::vector<const FiniteElementSpace*>;
private:
/// Offsets for the square "A" operator.
Array<int> offsets;
/// Column offsets for the prolongation operator.
@@ -40,27 +39,33 @@ class BlockFESpaceOperator : public Operator
/// Maps true dofs of each block to local dofs.
BlockOperator restriction;
/// Computes height for parent operator.
static int GetHeight(const FESVector &fespaces);
static int GetHeight(const std::vector<const FiniteElementSpace*>
&fespaces);
/// Computes offsets for A BlockOperator.
static Array<int> GetBlockOffsets(const FESVector &fespaces);
static Array<int> GetBlockOffsets(const std::vector<const FiniteElementSpace*>
&fespaces);
/// Computes col_offsets for prolongation operator.
static Array<int> GetProColBlockOffsets(const FESVector &fespaces);
static Array<int> GetProColBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces);
/// Computes row_offsets for restriction operator.
static Array<int> GetResRowBlockOffsets(const FESVector &fespaces);
static Array<int> GetResRowBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces);
public:
/// @brief Constructor for BlockFESpaceOperator.
/// @param[in] fespaces Finite element spaces for diagonal blocks. Spaces are not owned.
BlockFESpaceOperator(const FESVector &fespaces);
BlockFESpaceOperator(const std::vector<const FiniteElementSpace*> &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 // MFEM_BLOCK_FESPACE_OPERATOR
#endif
+49 -41
View File
@@ -9,65 +9,70 @@
// 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::blocksolvers
namespace mfem
{
namespace blocksolvers
{
/// Bramble-Pasciak Solver
BramblePasciakSolver::BramblePasciakSolver(ParBilinearForm &mVarf,
ParMixedBilinearForm &bVarf,
const BPSParameters &param)
: DarcySolver(mVarf.ParFESpace()->GetTrueVSize(),
bVarf.TestFESpace()->GetTrueVSize())
BramblePasciakSolver::BramblePasciakSolver(
ParBilinearForm *mVarf,
ParMixedBilinearForm *bVarf,
const BPSParameters &param)
: 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);
std::unique_ptr<HypreParMatrix> invDBt(B_->Transpose());
auto BT = B_->Transpose();
auto invDBt = new HypreParMatrix(*BT);
invDBt->InvScaleRows(diagM);
S_.reset(ParMult(B_.get(), invDBt.get(), true));
auto S = ParMult(B_.get(), invDBt);
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 &param)
BramblePasciakSolver::BramblePasciakSolver(
HypreParMatrix &M, HypreParMatrix &B, HypreParMatrix &Q,
Solver &M0, Solver &M1,
const BPSParameters &param)
: 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 &param)
void BramblePasciakSolver::Init(
HypreParMatrix &M, HypreParMatrix &B, HypreParMatrix &Q,
Solver &M0, Solver &M1,
const BPSParameters &param)
{
Bt_ = std::make_unique<TransposeOperator>(&B);
auto Bt = new TransposeOperator(&B);
auto invQ = new HypreDiagScale(Q);
use_bpcg = param.use_bpcg;
if (use_bpcg)
{
oop_ = std::make_unique<BlockOperator>(offsets_);
oop_ = new BlockOperator(offsets_);
oop_->owns_blocks = false;
oop_->SetBlock(0, 0, &M);
oop_->SetBlock(0, 1, Bt_.get());
oop_->SetBlock(0, 1, Bt);
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
@@ -76,48 +81,51 @@ void BramblePasciakSolver::Init(HypreParMatrix &M,
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_ = std::make_unique<ProductOperator>(temp_cpc, temp_tri, true, true);
ppc_ = new ProductOperator(temp_cpc, temp_tri, true, true);
ipc_ = std::make_unique<BlockOperator>(offsets_);
ipc_ = new BlockOperator(offsets_);
ipc_->owns_blocks = false;
ipc_->SetDiagonalBlock(0, invQ);
ipc_->owns_blocks = 1;
// bpcg
solver_ = std::make_unique<BPCGSolver>(M.GetComm(), ipc_.get(), ppc_.get());
solver_.reset(new BPCGSolver(M.GetComm(), *ipc_, *ppc_));
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_.get());
temp_oop->SetBlock(0, 1, Bt);
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_ = std::make_unique<SumOperator>(temp_AN, 1.0, id, -1.0, true, true);
mop_ = std::make_unique<ProductOperator>(map_.get(), temp_oop, false, false);
map_ = new SumOperator(temp_AN, 1.0, id, -1.0, true, true);
cpc_ = std::make_unique<BlockDiagonalPreconditioner>(offsets_);
mop_ = new ProductOperator(map_, temp_oop, false, true);
cpc_ = new BlockDiagonalPreconditioner(offsets_);
cpc_->owns_blocks = true;
cpc_->SetDiagonalBlock(0, &M0);
cpc_->SetDiagonalBlock(1, &M1);
// (P)CG
solver_ = std::make_unique<CGSolver>(M.GetComm());
solver_.reset(new CGSolver(M.GetComm()));
solver_->SetOperator(*mop_);
solver_->SetPreconditioner(*cpc_);
}
@@ -125,7 +133,7 @@ void BramblePasciakSolver::Init(HypreParMatrix &M,
}
HypreParMatrix *BramblePasciakSolver::ConstructMassPreconditioner(
const ParBilinearForm &mVarf, real_t q_scaling)
ParBilinearForm &mVarf, real_t q_scaling)
{
MFEM_ASSERT((q_scaling > 0.0) && (q_scaling < 1.0),
"Invalid Q-scaling factor: q_scaling = " << q_scaling );
@@ -159,7 +167,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(static_cast<int>(696383552LL+779345LL*i));
x.Randomize(696383552+779345*i);
#if defined(MFEM_USE_DOUBLE)
const real_t rel_tol = 1e-12;
#elif defined(MFEM_USE_SINGLE)
@@ -392,5 +400,5 @@ void BPCGSolver::Mult(const Vector &b, Vector &x) const
final_norm = sqrt(delta);
Monitor(final_iter, final_norm, r, x, true);
}
} // namespace mfem::blocksolvers
} // namespace blocksolvers
} // namespace mfem
+27 -19
View File
@@ -49,7 +49,9 @@
#include "darcy_solver.hpp"
#include <memory>
namespace mfem::blocksolvers
namespace mfem
{
namespace blocksolvers
{
/// Parameters for the BramblePasciakSolver method
@@ -68,11 +70,11 @@ protected:
void UpdateVectors();
public:
BPCGSolver(const Operator *ipc, const Operator *ppc): iprec(ipc), pprec(ppc) {}
BPCGSolver(const Operator &ipc, const Operator &ppc) { pprec = &ppc; iprec = &ipc; }
#ifdef MFEM_USE_MPI
BPCGSolver(MPI_Comm comm_, const Operator *ipc, const Operator *ppc)
: IterativeSolver(comm_), iprec(ipc), pprec(ppc) { }
BPCGSolver(MPI_Comm comm_, const Operator &ipc, const Operator &ppc)
: IterativeSolver(comm_) { pprec = &ppc; iprec = &ipc; }
#endif
void SetOperator(const Operator &op) override
@@ -81,9 +83,11 @@ 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;
};
@@ -112,20 +116,23 @@ 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_;
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_;
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_;
Array<int> ess_zero_dofs_;
void Init(HypreParMatrix &M, HypreParMatrix &B,
@@ -135,8 +142,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 &param);
/// System and mass preconditioner are user-provided
@@ -151,8 +158,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(const ParBilinearForm &mVarf,
const real_t alpha = 0.5);
static HypreParMatrix *ConstructMassPreconditioner(ParBilinearForm &mVarf,
real_t alpha = 0.5);
void Mult(const Vector &x, Vector &y) const override;
void SetOperator(const Operator &op) override { }
@@ -160,6 +167,7 @@ public:
int GetNumIterations() const override { return solver_->GetNumIterations(); }
};
} // namespace mfem::blocksolvers
} // namespace blocksolvers
} // namespace mfem
#endif // MFEM_BP_SOLVER_HPP
+6 -5
View File
@@ -13,9 +13,10 @@
using namespace std;
namespace mfem::blocksolvers
namespace mfem
{
namespace blocksolvers
{
void SetOptions(IterativeSolver& solver, const IterSolveParameters& param)
{
solver.SetPrintLevel(param.print_level);
@@ -48,7 +49,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 = 1;
prec_.owns_blocks = true;
SetOptions(solver_, param);
solver_.SetOperator(op_);
@@ -60,5 +61,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 mfem::blocksolvers
} // namespace blocksolvers
} // namespace mfem
+9 -4
View File
@@ -13,10 +13,13 @@
#define MFEM_DARCY_SOLVER_HPP
#include "mfem.hpp"
#include <memory>
#include <vector>
namespace mfem::blocksolvers
namespace mfem
{
namespace blocksolvers
{
struct IterSolveParameters
{
int print_level = 0;
@@ -29,6 +32,8 @@ 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
};
@@ -63,7 +68,7 @@ public:
void SetEssZeroDofs(const Array<int>& dofs) { dofs.Copy(ess_zero_dofs_); }
int GetNumIterations() const override { return solver_.GetNumIterations(); }
};
} // namespace mfem::blocksolvers
} // namespace blocksolvers
} // namespace mfem
#endif // MFEM_DARCY_SOLVER_HPP
+107 -106
View File
@@ -13,16 +13,16 @@
using namespace std;
namespace mfem::blocksolvers
namespace mfem
{
static HypreParMatrix* TwoStepsRAP(const HypreParMatrix *Rt,
const HypreParMatrix *A,
const HypreParMatrix *P)
namespace blocksolvers
{
OperatorPtr R(Rt->Transpose());
OperatorPtr RA(ParMult(R.As<HypreParMatrix>(), A));
return ParMult(RA.As<HypreParMatrix>(), P, true);
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);
}
void GetRowColumnsRef(const SparseMatrix& A, int row, Array<int>& cols)
@@ -59,36 +59,34 @@ DFSSpaces::DFSSpaces(int order, int num_refine, ParMesh *mesh,
if (mesh->Dimension() == 3)
{
hcurl_fec_ = std::make_unique<ND_FECollection>(order+1, mesh->Dimension());
hcurl_fec_.reset(new ND_FECollection(order+1, mesh->Dimension()));
}
else
{
hcurl_fec_ = std::make_unique<H1_FECollection>(order+1, mesh->Dimension());
hcurl_fec_.reset(new H1_FECollection(order+1, mesh->Dimension()));
}
all_bdr_attr_.SetSize(ess_attr.Size(), 1);
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_);
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_));
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);
data_.P_l2.resize(num_refine);
data_.P_hdiv.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
data_.P_l2.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
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_ = std::make_unique<ParFiniteElementSpace>(mesh, hcurl_fec_.get());
coarse_hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(*hcurl_fes_);
data_.P_hcurl.resize(num_refine);
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));
}
SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
@@ -106,8 +104,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);
@@ -121,7 +119,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
J[counter++] = tdof_agg.GetRowColumns(i)[0];
}
auto *D = new real_t[I[tdof_agg.NumRows()]];
real_t * 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());
@@ -148,21 +146,20 @@ void DFSSpaces::MakeDofRelationTables(int level)
void DFSSpaces::CollectDFSData()
{
auto GetP = [&](std::unique_ptr<OperatorPtr> &P,
std::unique_ptr<ParFiniteElementSpace> &cfes,
ParFiniteElementSpace& fes, const bool remove_zero)
auto GetP = [this](OperatorPtr& P, unique_ptr<ParFiniteElementSpace>& cfes,
ParFiniteElementSpace& fes, bool remove_zero)
{
fes.Update();
auto T = new OperatorHandle(Operator::Hypre_ParCSR);
fes.GetTrueTransferOperator(*cfes, *T);
P.reset(T);
if (remove_zero) { P->As<HypreParMatrix>()->DropSmallEntries(1e-16); }
fes.GetTrueTransferOperator(*cfes, P);
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);
@@ -174,9 +171,7 @@ 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_.Ae[level_+1].reset(
data_.C[level_+1].As<HypreParMatrix>()
->EliminateCols(ess_hcurl_tdof));
data_.C[level_+1].As<HypreParMatrix>()->EliminateCols(ess_hcurl_tdof);
++level_;
@@ -194,7 +189,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);
@@ -250,7 +245,7 @@ SaddleSchwarzSmoother::SaddleSchwarzSmoother(const HypreParMatrix& M,
const SparseMatrix& agg_hdivdof,
const SparseMatrix& agg_l2dof,
const HypreParMatrix& P_l2,
const ProductOperator& Q_l2)
const HypreParMatrix& Q_l2)
: Solver(M.NumRows() + B.NumRows()), agg_hdivdof_(agg_hdivdof),
agg_l2dof_(agg_l2dof), solvers_loc_(agg_l2dof.NumRows())
{
@@ -317,27 +312,23 @@ 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_.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());
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());
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)
{
@@ -352,112 +343,123 @@ DivFreeSolver::DivFreeSolver(const HypreParMatrix &M,
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].reset(coarse_solver);
smoothers_[l] = coarse_solver;
continue;
}
auto P_hdiv_l = data.P_hdiv[l-1]->As<HypreParMatrix>();
auto P_l2_l = data.P_l2[l-1]->As<HypreParMatrix>();
HypreParMatrix& P_hdiv_l = *data.P_hdiv[l-1].As<HypreParMatrix>();
HypreParMatrix& 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>();
ProductOperator& Q_l2_l = *data.Q_l2[l-1].As<ProductOperator>();
auto* C_l = data.C[l].As<HypreParMatrix>();
HypreParMatrix& Q_l2_l = *data.Q_l2[l-1].As<HypreParMatrix>();
HypreParMatrix* 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 = 1;
smoothers_[l] =
std::make_unique<ProductSolver>(ops_[l].get(), S0, S1, false, true, true);
S1->owns_blocks = true;
smoothers_[l] = new ProductSolver(ops_[l], S0, S1, false, true, true);
}
else
{
smoothers_[l].reset(S0);
smoothers_[l] = 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] =
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);
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);
ops_[l-1] =
std::make_unique<BlockOperator>(ops_offsets_[l-1]);
ops_[l-1] = new 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 = 1;
ops_[l-1]->owns_blocks = true;
}
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_.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));
solver_.As<GMRESSolver>()->SetOperator(*(ops_.Last()));
prec_.Reset(new Multigrid(ops_, smoothers_, blk_Ps_,
own_ops, own_smoothers, own_Ps));
}
else
{
Array<HypreParMatrix*> ops(data_.P_hcurl.size()+1);
Array<Solver*> smoothers(ops.Size());
Array<HypreParMatrix*> Ps(data_.P_hcurl.size());
auto C_finest = data.C.back().As<HypreParMatrix>();
ops.Last() = TwoStepsRAP(C_finest, &M, C_finest);
own_Ps = false;
HypreParMatrix& 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);
}
own_ops = true, own_smoothers = true;
prec_.Reset(new Multigrid(ops, smoothers, Ps,
own_ops, own_smoothers, own_blk_Ps));
prec_.Reset(new Multigrid(ops, smoothers, Ps, own_ops, own_smoothers, own_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()), sols(smoothers_.size());
std::vector<Vector> rhss(smoothers_.Size());
std::vector<Vector> 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());
@@ -468,12 +470,12 @@ void DivFreeSolver::SolveParticular(const Vector& rhs, Vector& sol) const
blk_Ps_[l]->MultTranspose(rhss[l+1], rhss[l]);
}
for (size_t l = 0; l < smoothers_.size(); ++l)
for (int l = 0; l < smoothers_.Size(); ++l)
{
smoothers_[l]->Mult(rhss[l], sols[l]);
}
for (size_t l = 0; l < blk_Ps_.size(); ++l)
for (int l = 0; l < blk_Ps_.Size(); ++l)
{
Vector P_sol(blk_Ps_[l]->NumRows());
blk_Ps_[l]->Mult(sols[l], P_sol);
@@ -505,12 +507,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_.back()->Mult(y, resid);
ops_.Last()->Mult(y, resid);
add(1.0, x, -1.0, resid, resid);
BlockVector correction(offsets_);
@@ -537,7 +539,7 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
ch.Clear();
ch.Start();
ops_.back()->Mult(y, resid);
ops_.Last()->Mult(y, resid);
add(1.0, x, -1.0, resid, resid);
SolveDivFree(resid.GetBlock(0), correction.GetBlock(0));
@@ -551,7 +553,7 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
ch.Clear();
ch.Start();
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.back()->GetBlock(0, 0));
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.Last()->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);
@@ -565,12 +567,11 @@ 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_.at(0).get())->GetNumIterations();
return static_cast<BDPMinresSolver*>(smoothers_[0])->GetNumIterations();
}
return solver_.As<IterativeSolver>()->GetNumIterations();
}
} // namespace mfem::blocksolvers
} // namespace blocksolvers
} // namespace mfem
+28 -24
View File
@@ -13,11 +13,11 @@
#define MFEM_DIVFREE_SOLVER_HPP
#include "darcy_solver.hpp"
#include <memory>
namespace mfem::blocksolvers
namespace mfem
{
namespace blocksolvers
{
/// Parameters for the divergence free solver
struct DFSParameters : IterSolveParameters
{
@@ -35,18 +35,14 @@ struct DFSParameters : IterSolveParameters
/// Data for the divergence free solver
struct DFSData
{
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;
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)
DFSParameters param;
};
@@ -96,7 +92,8 @@ public:
/// Compute the product B * B^T and solve it with CG preconditioned by BoomerAMG
class BBTSolver : public Solver
{
OperatorPtr BBT_, BBT_prec_;
OperatorPtr BBT_;
OperatorPtr BBT_prec_;
CGSolver BBT_solver_;
public:
BBTSolver(const HypreParMatrix &B, IterSolveParameters param);
@@ -118,11 +115,14 @@ public:
/// [ B 0 ]
class SaddleSchwarzSmoother : public Solver
{
const SparseMatrix &agg_hdivdof_, &agg_l2dof_;
const SparseMatrix& agg_hdivdof_;
const SparseMatrix& agg_l2dof_;
OperatorPtr coarse_l2_projector_;
Array<int> offsets_;
mutable Array<int> offsets_loc_, hdivdofs_loc_, l2dofs_loc_;
mutable Array<int> offsets_loc_;
mutable Array<int> hdivdofs_loc_;
mutable Array<int> 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 ProductOperator& Q_l2);
const HypreParMatrix& 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,10 +178,11 @@ class DivFreeSolver : public DarcySolver
OperatorPtr BT_;
BBTSolver BBT_solver_;
std::vector<Array<int>> ops_offsets_;
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_;
Array<BlockOperator*> ops_;
Array<BlockOperator*> blk_Ps_;
Array<Solver*> smoothers_;
OperatorPtr prec_;
OperatorPtr solver_;
void SolveParticular(const Vector& rhs, Vector& sol) const;
void SolveDivFree(const Vector& rhs, Vector& sol) const;
@@ -189,11 +190,14 @@ 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 mfem::blocksolvers
} // namespace blocksolvers
} // namespace mfem
#endif // MFEM_DIVFREE_SOLVER_HPP
+3 -2
View File
@@ -33,6 +33,8 @@
// (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"
@@ -258,8 +260,7 @@ 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() - Mpi::WorldRank();
const int seed = (random_seed) ? 0 : std::numeric_limits<int>::max();
solver.SetupRandomFieldGenerator(seed);
solver.GenerateRandomField(u);
+2 -5
View File
@@ -133,11 +133,8 @@ int main(int argc, char *argv[])
u.Save(sol_ofs);
}
if (visualization)
{
soutv << "keys '.0" << std::string((int)b, '0') << "'\n" << flush;
south << "keys '.0" << std::string((int)a, '0') << "'\n" << flush;
}
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";
+45 -81
View File
@@ -32,7 +32,6 @@ 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
@@ -180,42 +179,25 @@ if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
endif()
#-----------------------------------------------------------
# SERIAL CUDA TESTS: gpu_unit_tests
# SERIAL CUDA TESTS: cunit_tests
#-----------------------------------------------------------
# Create CUDA executable and test
# Create CUDA 'cunit_tests' executable and test
if (MFEM_USE_CUDA)
# 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)
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)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
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)
add_test(NAME cunit_tests COMMAND cunit_tests)
endif()
endif()
#-----------------------------------------------------------
# SERIAL SEDOV + TMOP TESTS:
# sedov_tests_{cpu,debug,gpu,gpu_uvm}
# tmop_pa_tests_{cpu,debug,gpu}
# sedov_tests_{cpu,debug,cuda,cuda_uvm}
# tmop_pa_tests_{cpu,debug,cuda}
#-----------------------------------------------------------
# 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
@@ -244,27 +226,27 @@ function(add_serial_miniapp_test name test_uvm)
add_test(NAME ${name}_tests_debug COMMAND ${name}_tests_debug)
endif()
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_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_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME ${name}_tests_gpu COMMAND ${name}_tests_gpu)
add_test(NAME ${name}_tests_cuda COMMAND ${name}_tests_cuda)
endif()
if (test_uvm)
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)
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)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME}
${name}_tests_gpu_uvm)
${name}_tests_cuda_uvm)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME ${name}_tests_gpu_uvm COMMAND ${name}_tests_gpu_uvm)
add_test(NAME ${name}_tests_cuda_uvm COMMAND ${name}_tests_cuda_uvm)
endif()
endif()
endif()
endif(MFEM_USE_CUDA)
endfunction(add_serial_miniapp_test)
add_serial_miniapp_test(sedov ON) # UVM ON
@@ -300,11 +282,10 @@ if (MFEM_USE_CEED)
endif()
#-----------------------------------------------------------
# PARALLEL CPU AND CUDA TESTS: {p,pc}unit_tests and pgpu_unit_tests
# PARALLEL CPU AND CUDA TESTS: {p,pc}unit_tests
#-----------------------------------------------------------
# Define executables and tests
# Define executables and tests 'punit_tests' and 'pcunit_tests'
if (MFEM_USE_MPI)
# punit_tests
if (MFEM_USE_CUDA)
set_property(SOURCE punit_test_main.cpp PROPERTY LANGUAGE CUDA)
endif()
@@ -320,44 +301,27 @@ if (MFEM_USE_MPI)
endif()
endforeach()
if (MFEM_USE_CUDA)
# 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()
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()
endif()
endif(MFEM_USE_MPI)
#-----------------------------------------------------------
# PARALLEL SEDOV + TMOP TESTS:
# psedov_tests_{cpu,debug,gpu,gpu_uvm}
# ptmop_pa_tests_{cpu,gpu}
# psedov_tests_{cpu,debug,cuda,cuda_uvm}
# ptmop_pa_tests_{cpu,cuda}
#-----------------------------------------------------------
# Function to add one MPI executable for a test.
function(add_mpi_executable_test name dev)
@@ -407,10 +371,10 @@ function(add_parallel_miniapp_test name HYPRE_MM)
list(APPEND backends debug)
endif()
endif()
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
list(APPEND backends gpu)
if (MFEM_USE_CUDA)
list(APPEND backends cuda)
if (HYPRE_MM)
list(APPEND backends gpu_uvm)
list(APPEND backends cuda_uvm)
endif()
endif()
+9 -9
View File
@@ -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`
* `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
* `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
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]`.
* `[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]`.
* `[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]`.
* `[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,
@@ -16,13 +16,13 @@
int main(int argc, char *argv[])
{
#ifdef MFEM_USE_SINGLE
std::cout << "\nThe serial GPU unit tests are not supported in single"
std::cout << "\nThe serial CUDA unit tests are not supported in single"
" precision.\n\n";
return MFEM_SKIP_RETURN_VALUE;
#endif
mfem::Device device("gpu");
mfem::Device device("cuda");
// Include only tests labeled with GPU. Exclude parallel tests.
return RunCatchSession(argc, argv, {"[GPU]", "~[Parallel]"});
// Include only tests labeled with CUDA. Exclude parallel tests.
return RunCatchSession(argc, argv, {"[CUDA]", "~[Parallel]"});
}
+3 -3
View File
@@ -185,7 +185,7 @@ TEST_CASE("Diffusion Diagonal PA", "[PartialAssembly][AssembleDiagonal]")
{
for (int ne = 1; ne < 3; ++ne)
{
const int n_elements = static_cast<int>(pow(ne, dimension));
const int n_elements = 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",
"[GPU][PartialAssembly][AssembleDiagonal]")
"[CUDA][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 = static_cast<int>(std::pow(ne, dimension));
const int n_elements = std::pow(ne, dimension);
CAPTURE(dimension, spaceType, integrator, coeffType, n_elements);
int max_order = (dimension == 3) ? 2 : 3;
+21 -58
View File
@@ -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], [GPU]")
TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
{
const bool all_tests = launch_all_non_regression_tests;
@@ -251,7 +251,7 @@ TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [GPU]")
}
} // H1 Assembly Levels test case
TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][GPU]")
TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][CUDA]")
{
const auto fname = GENERATE(
"../../data/inline-quad.mesh",
@@ -316,7 +316,7 @@ TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][GPU]")
}
}
TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][GPU]")
TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][CUDA]")
{
const auto fname = GENERATE(
"../../data/inline-quad.mesh",
@@ -387,7 +387,7 @@ TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][GPU]")
}
}
TEST_CASE("L2 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [GPU]")
TEST_CASE("L2 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
{
const bool dg = true;
auto pb = GENERATE(Problem::Mass, Problem::Convection);
@@ -454,16 +454,7 @@ void CompareMatricesNonZeros(SparseMatrix &A1, const SparseMatrix &A2,
HYPRE_BigInt *cmap1=nullptr,
std::unordered_map<HYPRE_BigInt,int> *cmap2inv=nullptr)
{
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);
REQUIRE(A1.Height() == A2.Height());
int n = A1.Height();
const int *I1 = A1.HostReadI();
@@ -497,14 +488,6 @@ 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));
}
@@ -576,7 +559,7 @@ void TestH1FullAssembly(Mesh &mesh, int order)
REQUIRE(B1.Normlinf() == MFEM_Approx(0.0));
}
TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [GPU]")
TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [CUDA]")
{
auto order = GENERATE(1, 2, 3);
auto mesh_fname = GENERATE(
@@ -587,7 +570,7 @@ TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [GPU]")
TestH1FullAssembly(mesh, order);
}
TEST_CASE("Full Assembly Connectivity", "[AssemblyLevel], [GPU]")
TEST_CASE("Full Assembly Connectivity", "[AssemblyLevel], [CUDA]")
{
const int order = GENERATE(1, 2, 3);
const int ne = GENERATE(4, 8, 16, 32);
@@ -655,7 +638,7 @@ void TestSameHypreMatrices(OperatorHandle &A1, OperatorHandle &A2)
CompareMatricesNonZeros(*M2, *M1);
}
TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [GPU]")
TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
{
auto order = GENERATE(1, 2, 3);
auto mesh_fname = GENERATE(
@@ -663,8 +646,6 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [GPU]")
"../../data/fichera.mesh"
);
// CAPTURE(order, mesh_fname);
Mesh serial_mesh(mesh_fname);
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
serial_mesh.Clear();
@@ -694,25 +675,17 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [GPU]")
OperatorHandle A_fa, A_legacy;
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());
// 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);
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);
// 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);
@@ -728,23 +701,13 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [GPU]")
a_fa.Assemble();
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);
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);
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));
}
B1 -= B2;
REQUIRE(B1.Normlinf() == MFEM_Approx(0.0));
}
#endif
+1 -1
View File
@@ -69,7 +69,7 @@ TEST_CASE("Test order of boundary integrators",
TEST_CASE("FormLinearSystem/SolutionScope",
"[BilinearForm]"
"[GPU]")
"[CUDA]")
{
// Create a simple mesh and FE space
int dim = 2, nx = 2, ny = 2, order = 2;
+1 -1
View File
@@ -14,7 +14,7 @@
using namespace mfem;
TEST_CASE("BlockOperators", "[BlockOperators], [GPU]")
TEST_CASE("BlockOperators", "[BlockOperators], [CUDA]")
{
const int dim = 2, nx = 3, ny = 3, order = 2;
Element::Type e_type = Element::QUADRILATERAL;
+1 -1
View File
@@ -231,7 +231,7 @@ void TestFDCalcCurlShape(FiniteElement* fe, ElementTransformation * T,
IntegrationPoint pt = ir->IntPoint(i);
fe->CalcCurlShape(pt, dshape);
CAPTURE(pt.x, pt.y, dim == 3 ? pt.z : 0_r);
CAPTURE(pt.x, pt.y, pt.z);
fdshape = 0.0;
for (int d=0; d<dim; d++)
+2 -2
View File
@@ -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, dim == 3 ? ip.z : 0_r);
CAPTURE(ip.x, ip.y, ip.z);
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, dim == 3 ? pt.z : 0_r);
CAPTURE(pt.x, pt.y, pt.z);
fdshape = 0.0;
for (int d=0; d<dim; d++)
+1 -1
View File
@@ -14,7 +14,7 @@
using namespace mfem;
TEST_CASE("DG Mass Inverse", "[GPU]")
TEST_CASE("DG Mass Inverse", "[CUDA]")
{
auto mesh_filename = GENERATE(
"../../data/inline-segment.mesh",
+1 -1
View File
@@ -14,7 +14,7 @@
using namespace mfem;
TEST_CASE("FA Determinism", "[PartialAssembly][GPU]")
TEST_CASE("FA Determinism", "[PartialAssembly][CUDA]")
{
const int order = 3;
const char *mesh_filename = "../../data/star-q3.mesh";
+1 -2
View File
@@ -221,8 +221,7 @@ 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
// Num DoF per interior dir
const int ni = order * (static_cast<int>(pow(order-1, 2)));
const int ni = order * pow(order - 1, 2); // Num DoF per interior dir
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
+24 -33
View File
@@ -190,7 +190,7 @@ TEST_CASE("InverseElementTransformation",
}
TEST_CASE("BatchInverseElementTransformation",
"[InverseElementTransformation], [GPU]")
"[InverseElementTransformation], [CUDA]")
{
const real_t tol = 4e-13;
@@ -268,14 +268,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -370,14 +369,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -473,14 +471,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -578,14 +575,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -679,14 +675,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -763,13 +758,12 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
}
}
}
@@ -852,13 +846,12 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
}
}
}
@@ -932,13 +925,12 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
}
}
}
@@ -1018,13 +1010,12 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
if (res_type[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
}
}
}
+2 -2
View File
@@ -201,7 +201,7 @@ struct LinearFormExtTest
}
};
TEST_CASE("Linear Form Extension", "[LinearFormExtension], [GPU]")
TEST_CASE("Linear Form Extension", "[LinearFormExtension], [CUDA]")
{
const bool all = launch_all_non_regression_tests;
@@ -328,7 +328,7 @@ TEST_CASE("Linear Form Extension", "[LinearFormExtension], [GPU]")
}
}
TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [GPU]")
TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [CUDA]")
{
const bool all = launch_all_non_regression_tests;

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