Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4667cf9d39 | ||
|
|
1e6c8ecf56 | ||
|
|
95c844f519 | ||
|
|
b5b0f2382f | ||
|
|
bbdc39d605 | ||
|
|
7fed49c38b |
@@ -94,16 +94,6 @@ inputs:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
# Unfortunately, "uses:" fields cannot have references to variables like
|
||||
# ${{env.MFEM_ACTIONS_VERSION}}, so the branch/tag name has to be hard coded.
|
||||
# Therefore, in the future, when updating the version of the
|
||||
# mfem/github-actions to use, we'll have to replace:
|
||||
# - all definitions of MFEM_ACTIONS_VERSION and
|
||||
# - all "uses:" fields that refer to mfem/github-actions.
|
||||
MFEM_ACTIONS_VERSION:
|
||||
description: Version (branch or tag) of the mfem/github-actions to use.
|
||||
default: v2.7
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -128,7 +118,6 @@ runs:
|
||||
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
|
||||
echo MFEM_ACTIONS_VERSION=${{inputs.MFEM_ACTIONS_VERSION}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Env (dir)
|
||||
|
||||
@@ -25,7 +25,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
@@ -53,7 +53,7 @@ runs:
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- uses: mfem/github-actions/build-mfem@v2.7
|
||||
- uses: mfem/github-actions/build-mfem@v2.5
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
env:
|
||||
CXXFLAGS: ${{env.CXXFLAGS}}
|
||||
@@ -82,7 +82,7 @@ runs:
|
||||
run: find . -type f -name '*.o' -delete
|
||||
shell: bash
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
name: 'Install MPI'
|
||||
description: 'Installs MPI and set up its environment variables'
|
||||
|
||||
inputs:
|
||||
NO_FLAGS:
|
||||
description: If true, do not set any CXXFLAGS or LDFLAGS.
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
@@ -32,7 +27,6 @@ runs:
|
||||
shell: bash
|
||||
|
||||
- name: Env (bis)
|
||||
if: ${{ inputs.NO_FLAGS != 'true' }}
|
||||
run: |
|
||||
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
|
||||
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
|
||||
|
||||
@@ -36,7 +36,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
@@ -49,7 +49,7 @@ runs:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
@@ -23,7 +23,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache/restore@v5 # Cache for LLVM libcxx
|
||||
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
@@ -32,32 +32,32 @@ runs:
|
||||
- uses: ./.github/actions/sanitize/mpi
|
||||
if: ${{inputs.par == 'true'}}
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Hypre
|
||||
- uses: actions/cache/restore@v4 # Cache for Hypre
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{env.MFEM_ACTIONS_VERSION}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Metis
|
||||
- uses: actions/cache/restore@v4 # Cache for Metis
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
|
||||
- name: Hypre/Metis links
|
||||
if: ${{inputs.par == 'true'}}
|
||||
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for LSAN suppression file
|
||||
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
- uses: actions/checkout@v6 # Checkout the repository
|
||||
- uses: actions/checkout@v4 # Checkout the repository
|
||||
with:
|
||||
path: mfem
|
||||
# ref: ${{env.BRANCH}}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
# MFEM Pull Request Review Agent Guide
|
||||
|
||||
## Purpose and scope
|
||||
Review MFEM PRs for correctness, maintainability, performance, portability, test coverage, and MFEM consistency. Use the diff and PR context; reference source files, tests, and CI results when available. Follow `CONTRIBUTING.md`, especially Developer Guidelines, PR rules, checklist, and testing.
|
||||
|
||||
## Critical review pillars
|
||||
- Correctness and numerical behavior
|
||||
- API and user-facing impact
|
||||
- Performance implications
|
||||
- Maintainability and portability
|
||||
|
||||
## Review workflow
|
||||
1. Read the PR description, linked issues, and intended behavior.
|
||||
2. Inspect the diff before commenting.
|
||||
3. Identify affected MFEM components, examples, tests, build or docs changes, and downstream APIs.
|
||||
4. Analyze the code against the critical review pillars.
|
||||
5. Compare the change against nearby code and MFEM patterns; flag unmotivated deviations.
|
||||
6. Check whether tests and documentation were updated appropriately.
|
||||
7. Review CI results and suggest actions.
|
||||
8. Produce a structured review with prioritized findings.
|
||||
9. Always limit conclusions to available evidence.
|
||||
|
||||
## MFEM-specific review checklist
|
||||
- Component-aware scope: identify the touched subsystem (FEM, solvers, preconditioners, linear algebra, mesh, examples, miniapps, build, or docs) and assess its impact against the review pillars.
|
||||
- Numerical and algorithmic behavior: assess issues in convergence, stability, tolerances, precision, iteration limits, and failure handling. If clear opportunities exist to improve the algorithmic approach, call them out with expected impact.
|
||||
- API and user-facing impact: assess backward compatibility, user-visible behavior and default changes, migration impact, deprecations, and whether documentation clearly explains user-facing API changes.
|
||||
- Data structure and memory semantics: assess ownership, lifetime, aliasing, container behavior, and device-host synchronization.
|
||||
- Parallel and serial behavior: assess whether the change preserves equivalent semantics in serial and parallel modes where applicable; if logic is currently mode-specific, check whether extension to the other mode is straightforward (clear abstractions, no hard-wired assumptions), document constraints, and call out expected behavior differences explicitly.
|
||||
- Backend and portability impact: assess likely cross-backend risks in CPU, CUDA, HIP, OCCA, RAJA, partial assembly, fallback paths, compiler compatibility, and platform assumptions.
|
||||
- Build, dependency, and configuration impact: assess CMake or make changes, optional dependency behavior, and feature-flag interactions.
|
||||
- Tests and docs alignment: check available regression or unit coverage evidence for changed behavior, and ensure docs are updated for new flags, APIs, options, or behavior changes.
|
||||
- MFEM developer-guideline fit: keep code lean, simple, general, logically separated, and portable; suggest C++17 improvements when they clearly improve safety, clarity, or maintainability.
|
||||
- New source files, examples, or miniapps: if a PR adds source/header files, verify they are properly wired into the relevant `makefile` and `CMakeLists.txt`, referenced in docs where applicable (including `doc/CodeDocumentation.dox`), and added to top-level `.gitignore` only when generated artifacts require it.
|
||||
- Changelog: verify `CHANGELOG` is updated if the PR introduces significant new features or user-facing changes.
|
||||
- MFEM conventions: use `real_t`; use `mfem::out`/`mfem::err` instead of `std::cout`/`std::cerr` in library code; flag large/binary files; if AI assistance is apparent but undisclosed, suggest following `CONTRIBUTING.md`.
|
||||
- Edge cases: if the PR touches complex or error-prone areas, suggest additional tests for edge cases, failure modes, and parallel behavior.
|
||||
|
||||
## Commenting guidelines
|
||||
- Keep comments concise, actionable, and grounded in the diff.
|
||||
- Focus on correctness, behavior changes, and user impact over style nits.
|
||||
- Be professional, concise, collaborative, technically precise, and avoid unsupported assumptions.
|
||||
|
||||
@@ -13,7 +13,7 @@ Note that some of these scripts use the shared MFEM GitHub Actions from the exte
|
||||
|
||||
<https://github.com/mfem/github-actions>
|
||||
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch (or tag) in the above from which the action is taken.
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch in the above from which the action is taken.
|
||||
|
||||
The current CI workflows are:
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
remove-docker-images: 'true'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# It's easier to reference named variables than indexes of the matrix
|
||||
- name: Set Environment
|
||||
|
||||
@@ -40,7 +40,6 @@ env:
|
||||
METIS_ARCHIVE_MAC: metis-4.0.3-mac.tgz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
MFEM_TOP_DIR: mfem
|
||||
MFEM_ACTIONS_VERSION: v2.7
|
||||
|
||||
# Note for future improvements:
|
||||
#
|
||||
@@ -154,7 +153,7 @@ jobs:
|
||||
# /home/runner/work/mfem/mfem/mfem
|
||||
# Note: Done now to access "install-hypre" and "install-metis" actions.
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.MFEM_TOP_DIR }}
|
||||
# Fetch the complete history for codecov to access commits ID
|
||||
@@ -171,6 +170,20 @@ jobs:
|
||||
env
|
||||
shell: bash
|
||||
|
||||
# For info on Xcode see:
|
||||
# - https://github.com/actions/runner-images/issues/12541
|
||||
# - https://github.com/actions/runner-images/blob/releases/macos-15-arm64/20250811/images/macos/macos-15-arm64-Readme.md#xcode
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
XCODE_PATH="/Applications/Xcode_16.4.app"
|
||||
echo "> sudo xcode-select -s ${XCODE_PATH}"
|
||||
sudo xcode-select -s ${XCODE_PATH}
|
||||
echo "> g++ -v"
|
||||
g++ -v
|
||||
echo "> clang++ -v"
|
||||
clang++ -v
|
||||
|
||||
# Only get MPI if defined for the job.
|
||||
# TODO: It would be nice to have only one step, e.g. with a dedicated
|
||||
# action, but I (@adrienbernede) don't see how at the moment.
|
||||
@@ -212,14 +225,14 @@ jobs:
|
||||
- name: cache hypre
|
||||
id: hypre-cache
|
||||
if: matrix.mpi == 'par'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -229,7 +242,7 @@ jobs:
|
||||
|
||||
- name: get hypre (Windows)
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -242,14 +255,14 @@ jobs:
|
||||
- name: cache metis
|
||||
id: metis-cache
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
- name: install metis
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.7
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
with:
|
||||
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
@@ -257,7 +270,7 @@ jobs:
|
||||
- name: cache vcpkg (Windows)
|
||||
id: vcpkg-cache
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vcpkg_cache
|
||||
key: ${{ runner.os }}-${{ matrix.mpi }}-vcpkg-v1
|
||||
@@ -282,8 +295,7 @@ jobs:
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew update
|
||||
brew install enzyme
|
||||
ENZYME_LLVM=$(brew info enzyme | sed -n 's/^Required.*:.*\(llvm[^ ]*\).*/\1/p')
|
||||
echo "ENZYME_LLVM=$ENZYME_LLVM"
|
||||
ENZYME_LLVM=$(brew info enzyme | sed -n 's/^Required:.*\(llvm[^ ]*\).*/\1/p')
|
||||
LLVM_PREFIX=$(brew --prefix $ENZYME_LLVM)
|
||||
echo "LLVM_PREFIX=$LLVM_PREFIX" >> $GITHUB_ENV
|
||||
echo "OMPI_CC=$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV
|
||||
@@ -291,7 +303,7 @@ jobs:
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.7
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
env:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
|
||||
with:
|
||||
@@ -362,10 +374,8 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v2.7
|
||||
uses: mfem/github-actions/upload-coverage@v2.5
|
||||
with:
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
directories: "fem general linalg mesh"
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -40,11 +40,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -70,4 +70,4 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -32,7 +32,6 @@ env:
|
||||
METIS_ARCHIVE: metis-4.0.3.tar.gz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
COVERAGE_ENV: mfem-coverage
|
||||
MFEM_ACTIONS_VERSION: v2.7
|
||||
|
||||
jobs:
|
||||
gitignore:
|
||||
@@ -40,7 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout MFEM
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
@@ -51,37 +50,36 @@ jobs:
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-int32-fp64-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: int32
|
||||
precision: fp64
|
||||
|
||||
- name: Cache Metis Install
|
||||
id: metis-cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.7
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
with:
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build-mfem
|
||||
uses: mfem/github-actions/build-mfem@v2.7
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: copyright check
|
||||
id: copyright
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get astyle
|
||||
run: |
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get doxygen and graphviz
|
||||
run: |
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -17,24 +17,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 2.19.0
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{ env.MFEM_ACTIONS_VERSION }}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.7
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
with:
|
||||
archive: ${{env.HYPRE_TGZ}}
|
||||
dir: ${{env.HYPRE_DIR}}
|
||||
|
||||
@@ -27,13 +27,13 @@ jobs:
|
||||
llvm_use_sanitizer: "Undefined"
|
||||
name: ${{matrix.sanitizer}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: lsan.supp
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
@@ -17,24 +17,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 4.0.3
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
- name: Setup
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.7
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
with:
|
||||
archive: ${{env.METIS_TGZ}}
|
||||
dir: ${{env.METIS_DIR}}
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/mfem
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
env:
|
||||
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
|
||||
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
env:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build/tests/unit
|
||||
run: find . -type f -name '*.o' -delete
|
||||
- uses: actions/upload-artifact@v7
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
@@ -165,14 +165,14 @@ jobs:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
np: ${{inputs.par && '_np=2' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
- uses: actions/download-artifact@v8
|
||||
- uses: actions/download-artifact@v4
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
|
||||
@@ -443,10 +443,6 @@ miniapps/diag-smoothers/mg-abs-l1-jacobi
|
||||
miniapps/contact/contact
|
||||
miniapps/contact/ParaView
|
||||
|
||||
miniapps/plasma/pic/electrostatic-*
|
||||
!miniapps/plasma/pic/electrostatic-*.cpp
|
||||
miniapps/plasma/pic/*.csv
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
|
||||
@@ -85,8 +85,3 @@ opt_par_gcc_10_pumi:
|
||||
extends: .mfem_job_on_dane
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +pumi"
|
||||
|
||||
opt_par_gcc_10_gslib:
|
||||
extends: .mfem_job_on_dane
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +gslib"
|
||||
|
||||
@@ -63,8 +63,3 @@ opt_mpi_cuda_hypre_cuda_gcc:
|
||||
extends: .mfem_job_on_matrix
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90 ^hypre+cuda"
|
||||
|
||||
opt_mpi_cuda_gcc_gslib:
|
||||
extends: .mfem_job_on_matrix
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +mpi +cuda +gslib cuda_arch=90 ^hypre+cuda"
|
||||
|
||||
@@ -32,9 +32,9 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "dane" ]]; then
|
||||
srun --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
salloc --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
srun --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
else
|
||||
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
|
||||
exit 1
|
||||
|
||||
@@ -11,84 +11,24 @@
|
||||
Version 4.9.1 (development)
|
||||
===========================
|
||||
|
||||
- Added policy for AI-assisted contribution to CONTRIBUTING.md.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added GPU-enabled partial assembly for simplicial Bernstein H1 basis based on
|
||||
ragged tensor algorithms (see DOI: 10.1137/11082539X) for mass and diffusion
|
||||
integrators.
|
||||
|
||||
- Replaced legacy simplex quadrature rules with symmetric positive weight rules
|
||||
for triangles (orders 0-25) and tetrahedra (orders 0-20). These rules
|
||||
guarantee all-positive weights and interior quadrature points, improving
|
||||
numerical stability. Higher orders fall back to Grundmann-Moller.
|
||||
* Triangle rules: Witherden and Vincent, DOI: 10.1016/j.camwa.2015.03.017
|
||||
* Tet rules (d=1-13): Witherden and Vincent (same as above)
|
||||
* Tet rules (d=14-20): Chuluunbaatar et al., DOI: 10.1016/j.camwa.2022.08.016
|
||||
|
||||
- Added support for general 1D Gauss-Jacobi quadrature rules and Stroud conical
|
||||
quadrature rules on triangles and tetrahedra.
|
||||
|
||||
- Improved the GridFunction projection routines. Projections work for Scalar,
|
||||
- Improved the gridfunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behavior has not changed.
|
||||
projections can be selected, default behaviour has not changed.
|
||||
|
||||
- Added GridFunction projection methods for trace spaces, i.e., project
|
||||
coefficients on the mesh skeleton.
|
||||
|
||||
- Added methods to estimate function extremum using piecewise linear bounds plus
|
||||
- Added methods to estimate function extremum using piecewise linear bounds +
|
||||
recursive subdivision.
|
||||
|
||||
- Extend FindPointsGSLIB to support surface meshes.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added option to guarantee mesh validity during TMOP-based r-adaptivity, using
|
||||
bounds on the determinant of the mesh transformation Jacobian.
|
||||
|
||||
- Added PA support for TMOP's adaptive limiting functionality. Multiple
|
||||
GridFunctions and Coefficients can be combined to form a composite term.
|
||||
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
- Added the option to include material interfaces (faces separating elements
|
||||
with different element attributes) as additional boundary elements, for
|
||||
parallel visualization, e.g. with GLVis. This is supported by both the Print
|
||||
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added support for trace spaces in PRefinementTransferOperator. This is used in
|
||||
PRefinement multigrid methods for problems posed on trace spaces (see e.g. the
|
||||
DPG miniapps).
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added device assembly support for 3D H(curl) VectorFEDomainLFIntegrator.
|
||||
|
||||
- Added NVIDIA cuDSS library interface. Implementation examples have been
|
||||
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
|
||||
details. Supported versions >= 0.6.0.
|
||||
|
||||
- Allow specifying GPU kernel launch bounds for native and RAJA GPU backends.
|
||||
- Added support for FiniteElement::MapType::INTEGRAL spaces to
|
||||
QuadratureInterpolator.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- The Lorentz miniapp (in miniapps/electromagnetics) has been updated to
|
||||
leverage the ParticleSet capability.
|
||||
|
||||
- Added (Complex)PRefinementMultigrid solver option in the DPG miniapps.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Fixed signed DOF handling in ParGridFunction reading (read constructor) and
|
||||
saving via SaveAsOne(). Simplified the process of applying the DOF signs by
|
||||
using the new method ApplyDofSigns() in class ParFiniteElementSpace: the
|
||||
method will return immediately if no sign flips are needed.
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
|
||||
+1
-10
@@ -433,15 +433,6 @@ if (MFEM_USE_STRUMPACK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# cuDSS can only be enabled in CUDA
|
||||
if (MFEM_USE_CUDSS)
|
||||
if (MFEM_USE_CUDA)
|
||||
find_package(CUDSS REQUIRED)
|
||||
else()
|
||||
message(FATAL_ERROR " *** cuDSS requires that CUDA be enabled.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GnuTLS
|
||||
if (MFEM_USE_GNUTLS)
|
||||
find_package(_GnuTLS REQUIRED)
|
||||
@@ -640,7 +631,7 @@ find_package(Threads REQUIRED)
|
||||
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
|
||||
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CUDSS CALIPER CODIPACK
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
ALGOIM ENZYME CUDA::cudart)
|
||||
|
||||
|
||||
+65
-73
@@ -3,13 +3,12 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml?query=branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -25,14 +24,6 @@ must be made under this license.
|
||||
Note also that MFEM has a [Code of Conduct](CODE_OF_CONDUCT.md). By participating
|
||||
in the MFEM community, you agree to abide by its rules.
|
||||
|
||||
## AI Policy
|
||||
- Use of AI code generation in MFEM is allowed but must be disclosed, e.g. by
|
||||
selecting the `AI-assisted` label on the PR.
|
||||
- By submitting a PR, the author acknowledges that they have reviewed and
|
||||
understand the changes they are proposing.
|
||||
- PR authors are still responsible for correctness, licensing, and attribution
|
||||
of all changes.
|
||||
|
||||
If you plan on contributing to MFEM, consider reviewing the
|
||||
[issue tracker](https://github.com/mfem/mfem/issues) first to check if a thread
|
||||
already exists for your desired feature or the bug you ran into. Use a pull
|
||||
@@ -85,7 +76,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
follow the [MFEM PR Rules](#mfem-pr-rules).
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning two
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning two
|
||||
reviewers to evaluate the changes.
|
||||
- The reviewers have 3 weeks to evaluate the PR and work with the author to
|
||||
fix issues and implement improvements.
|
||||
@@ -126,7 +117,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── petsc
|
||||
│ ├── pumi
|
||||
│ ├── sundials
|
||||
│ └── superlu
|
||||
| └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── dfem
|
||||
@@ -138,6 +129,10 @@ The MFEM source code has the following structure:
|
||||
│ ├── moonolith
|
||||
│ ├── qinterp
|
||||
│ └── tmop
|
||||
│ | ├── assemble
|
||||
│ | ├── metrics
|
||||
│ | ├── mult
|
||||
│ | └── tools
|
||||
├── general
|
||||
├── linalg
|
||||
│ ├── batched
|
||||
@@ -150,10 +145,11 @@ The MFEM source code has the following structure:
|
||||
│ ├── common
|
||||
│ ├── contact
|
||||
│ ├── dfem
|
||||
│ ├── diag-smoothers
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── fluids
|
||||
│ │ ├── navier
|
||||
│ │ └── schrodinger-flow
|
||||
│ ├── gslib
|
||||
│ ├── hdiv-linear-solver
|
||||
│ ├── hooke
|
||||
@@ -163,7 +159,6 @@ The MFEM source code has the following structure:
|
||||
│ ├── nurbs
|
||||
│ ├── parelag
|
||||
│ ├── performance
|
||||
│ ├── plasma
|
||||
│ ├── shifted
|
||||
│ ├── solvers
|
||||
│ ├── spde
|
||||
@@ -194,15 +189,15 @@ respectively.
|
||||
|
||||
- The main finite element classes are:
|
||||
+ [`FiniteElement`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElementCollection.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1FiniteElementSpace.html)
|
||||
+ [`GridFunction`](https://docs.mfem.org/html/classmfem_1_1GridFunction.html)
|
||||
+ [`BilinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1BilinearFormIntegrator.html) and [`LinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
|
||||
- The main linear algebra classes and sources are
|
||||
+ [`Operator`](https://docs.mfem.org/html/classmfem_1_1Operator.html) and [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1Vector.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`DenseMatrix`](https://docs.mfem.org/html/classmfem_1_1DenseMatrix.html) and [`SparseMatrix`](https://docs.mfem.org/html/classmfem_1_1SparseMatrix.html)
|
||||
+ Sparse [smoothers](https://docs.mfem.org/html/sparsesmoothers_8hpp.html) and linear [solvers](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
|
||||
@@ -214,8 +209,8 @@ shared geometric entities between different tasks. The parallel source files
|
||||
have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
|
||||
- The main parallel classes are
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParNCMesh.html)
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParFiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1ParFiniteElementSpace.html)
|
||||
+ [`ParGridFunction`](https://docs.mfem.org/html/classmfem_1_1ParGridFunction.html)
|
||||
+ [`ParBilinearForm`](https://docs.mfem.org/html/classmfem_1_1ParBilinearForm.html) and [`ParLinearForm`](https://docs.mfem.org/html/classmfem_1_1ParLinearForm.html)
|
||||
@@ -225,14 +220,14 @@ have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
#### GPU and general device support
|
||||
|
||||
GPU and multi-core CPU support is based on device kernels supporting different
|
||||
backends (CUDA, HIP, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
backends (CUDA, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
device/host memory manager.
|
||||
|
||||
- The main device-relevant classes and sources are:
|
||||
+ [`Device`](https://docs.mfem.org/html/device_8hpp.html)
|
||||
+ [`MemoryManager`](https://docs.mfem.org/html/mem_manager_8hpp.html)
|
||||
+ the [`mfem::forall`](https://docs.mfem.org/html/forall_8hpp.html) function
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html), [`hip.hpp`](https://docs.mfem.org/html/hip_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
|
||||
#### Utilities, building and documentation
|
||||
- The `general/` directory contains C++ classes that serve as utilities for
|
||||
@@ -246,8 +241,8 @@ device/host memory manager.
|
||||
- `examples` and `miniapps` respectively gather simple and more fully-featured
|
||||
demonstrations of the usage on MFEM. They both rely on `data/` for the
|
||||
collection of meshes.
|
||||
- The `tests/` directory contains a unit test suite, additional tests, and
|
||||
benchmarks.
|
||||
- The `tests/` directory contains a unit test suite and will later contain more
|
||||
tests that run example codes.
|
||||
|
||||
See also the [code overview](https://mfem.org/code-overview/) section on the MFEM
|
||||
website.
|
||||
@@ -281,8 +276,8 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
the top of https://github.com/mfem.
|
||||
- Consider making your membership public by going to https://github.com/orgs/mfem/people
|
||||
and clicking on the organization visibility drop box next to your name.
|
||||
- Project discussions and announcements will be posted at https://github.com/orgs/mfem/discussions,
|
||||
tagging the `@mfem/everyone` team when appropriate.
|
||||
- Project discussions and announcements will be posted at
|
||||
https://github.com/orgs/mfem/teams/everyone.
|
||||
|
||||
#### Structure
|
||||
- The MFEM source code is in the [mfem](https://github.com/mfem/mfem)
|
||||
@@ -342,12 +337,11 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Well-designed simple code is frequently more general and powerful.
|
||||
- Lean code base is easier to understand by new collaborators.
|
||||
- New features should be added only if they are necessary or generally useful.
|
||||
- Introduction of language constructs not currently used in MFEM should be
|
||||
- Introduction of language constructions not currently used in MFEM should be
|
||||
justified and generally avoided (to maintain portability to various systems
|
||||
and compilers, including early access hardware).
|
||||
- We prefer basic C++. Use C++17 features judiciously, prioritizing readability,
|
||||
consistency with existing MFEM code, and portability to different systems,
|
||||
compilers and device backends.
|
||||
- We prefer basic C++ and the C++03 standard, to keep the code readable by
|
||||
a large audience and to make sure it compiles anywhere.
|
||||
|
||||
- *Keep the code general and reasonably efficient*
|
||||
- The main goal is fast prototyping for research and application development.
|
||||
@@ -390,7 +384,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- When your branch is ready for other developers to review / comment on
|
||||
the code, create a pull request towards `mfem:master`.
|
||||
|
||||
- Pull requests typically have titles like:
|
||||
- Pull request typically have titles like:
|
||||
|
||||
`Description [new-feature-dev]`
|
||||
|
||||
@@ -411,12 +405,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Add a description, appropriate labels and assign yourself to the PR. The MFEM
|
||||
team will add reviewers as appropriate.
|
||||
|
||||
- List outstanding TODO items in the description.
|
||||
- List outstanding TODO items in the description, see PR #222 for an example.
|
||||
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
or request the `ready-for-review` label.
|
||||
the `ready-for-review` label.
|
||||
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning
|
||||
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
|
||||
the PR and work with the author to implement improvements and fix issues.
|
||||
|
||||
@@ -442,7 +436,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
checks in GitHub Actions enforce MFEM-specific rules which are explained in
|
||||
the error messages and the `tests/scripts` directory.
|
||||
|
||||
- Also note that the tests `branch-history` and `repo-check` found in GitHub
|
||||
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
|
||||
Actions can be triggered automatically before each push using git hooks. See
|
||||
the [git hooks README](config/githooks/README.md) for a detailed explanation.
|
||||
|
||||
@@ -499,15 +493,15 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a
|
||||
|
||||
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
|
||||
|
||||
4. To seek help from the editors in case of difficulties.
|
||||
3. To seek help from the editors in case of difficulties.
|
||||
|
||||
5. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
4. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
|
||||
6. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
7. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
|
||||
8. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
|
||||
#### Responsibilities of Authors
|
||||
|
||||
@@ -533,30 +527,30 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Code builds.
|
||||
- [ ] Code passes `make style`.
|
||||
- [ ] Update `CHANGELOG`:
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Update `INSTALL`:
|
||||
- [ ] Has a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Had a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update `.gitignore`:
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] New examples:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] Add the example code to the appropriate `SEQ_EXAMPLES` and `PAR_EXAMPLES` variables.
|
||||
- [ ] Add any files generated by it to the `clean` target.
|
||||
- [ ] Add the example binary and any files generated by it to the top-level `.gitignore` file.
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Add the example code to the `ALL_EXE_SRCS` variable.
|
||||
- [ ] Make sure `THIS_TEST_OPTIONS` is set correctly for the new example.
|
||||
- [ ] List the new example in `doc/CodeDocumentation.dox`.
|
||||
- [ ] If new examples directory (e.g. `examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new examples directory (e.g.`examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add example-specific documentation, see e.g. the `src/examples.md`.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
@@ -573,13 +567,13 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Add/update the `CMakeLists.txt` file in the new miniapp directory.
|
||||
- [ ] Consider adding a new test for the new miniapp.
|
||||
- [ ] List the new miniapp in `doc/CodeDocumentation.dox`
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New capability:
|
||||
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
|
||||
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
|
||||
@@ -681,7 +675,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- [ ] Update URL shortlinks:
|
||||
- [ ] Create a shortlink at [http://bit.ly/](http://bit.ly/) for the release tarball, e.g. https://mfem.github.io/releases/mfem-3.1.tgz.
|
||||
- [ ] (LLNL only) Add and commit the new shortlink in the `links` and `links-mfem` files of the internal `mfem/downloads` repo.
|
||||
- [ ] Add the new shortlinks to the MFEM package in `spack`.
|
||||
- [ ] Add the new shortlinks to the MFEM packages in `spack`, `homebrew/science`, `VisIt`, etc.
|
||||
- [ ] Update website in `mfem/web` repo:
|
||||
- Update version and shortlinks in `src/index.md` and `src/download.md`.
|
||||
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
|
||||
@@ -733,24 +727,22 @@ commit or push, see the [README](config/githooks/README.md) in the `config/githo
|
||||
directory.
|
||||
|
||||
|
||||
### GitHub Actions smoke tests
|
||||
|
||||
### Linux and Mac smoke tests
|
||||
We use GitHub Actions to drive the default tests on the `master` and `next`
|
||||
branches. See the `.github/workflows` files and the logs at
|
||||
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
|
||||
|
||||
GitHub Actions testing should be kept lightweight, as there is a time
|
||||
constraint on jobs. The current workflows cover Linux, macOS, and Windows
|
||||
configurations.
|
||||
Testing using GitHub Actions should be kept lightweight, as there is a time
|
||||
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
|
||||
|
||||
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
|
||||
- Tests on the `next` branch are currently scheduled to run each night.
|
||||
|
||||
### Additional Windows smoke test
|
||||
|
||||
We also use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor.yml` file
|
||||
and the build logs at
|
||||
### Windows smoke test
|
||||
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor` file and the
|
||||
build logs at
|
||||
[https://ci.appveyor.com/project/mfem/mfem](https://ci.appveyor.com/project/mfem/mfem).
|
||||
|
||||
CMake is used to generate the MSVC Project files and drive the build. A release
|
||||
|
||||
@@ -38,13 +38,14 @@ the option MFEM_USE_METIS.
|
||||
MFEM also includes support for devices such as GPUs, and programming models such
|
||||
as CUDA, HIP, OCCA, OpenMP and RAJA.
|
||||
|
||||
- Starting with version 4.9, MFEM requires a C++17 compiler.
|
||||
- Starting with version 4.0, MFEM requires a C++11 compiler. We recommend using
|
||||
a newer compiler, e.g. GCC version 4.9 or higher.
|
||||
|
||||
- CUDA support requires an NVIDIA GPU and an installation of the CUDA Toolkit
|
||||
https://developer.nvidia.com/cuda-toolkit
|
||||
|
||||
- HIP support requires an AMD GPU and an installation of the ROCm software stack
|
||||
https://rocm.docs.amd.com
|
||||
https://rocmdocs.amd.com
|
||||
|
||||
- OCCA support requires the OCCA library
|
||||
https://libocca.org
|
||||
@@ -82,9 +83,9 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
make parallel -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
CUDA build:
|
||||
make cuda -j 4
|
||||
@@ -114,14 +115,14 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
|
||||
make -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
Parallel build with fetching of hypre and METIS:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DMFEM_FETCH_TPLS=YES
|
||||
make -j 4
|
||||
|
||||
@@ -133,8 +134,7 @@ CUDA build:
|
||||
|
||||
HIP build:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 \
|
||||
-DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
make -j 4
|
||||
|
||||
Example codes (serial/parallel, depending on the build):
|
||||
@@ -269,7 +269,6 @@ Compilers:
|
||||
CXX - C++ compiler, serial build
|
||||
MPICXX - MPI C++ compiler, parallel build
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
|
||||
HIP_CXX - The HIP compiler, e.g. 'hipcc'
|
||||
|
||||
Compiler options:
|
||||
OPTIM_FLAGS - Options for optimized build
|
||||
@@ -396,11 +395,6 @@ MFEM_USE_STRUMPACK = YES/NO
|
||||
classes. When enabled, this option uses the STRUMPACK_* library options, see
|
||||
below.
|
||||
|
||||
MFEM_USE_CUDSS = YES/NO
|
||||
Enable MFEM functionality based on the cuDSS library. When using cuDSS, CUDA
|
||||
support must be also enabled in MFEM, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
When enabled, this option uses the CUDSS_* library options, see below.
|
||||
|
||||
MFEM_USE_GINKGO = YES/NO
|
||||
Enable MFEM functionality based on the Ginkgo library, which provides
|
||||
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
|
||||
@@ -560,13 +554,13 @@ MFEM_USE_RAJA = YES/NO
|
||||
MFEM_USE_OCCA = YES/NO
|
||||
Enables support for the OCCA library in MFEM. OCCA is an open-source library
|
||||
which aims to make it easy to program different types of devices (e.g. CPU,
|
||||
GPU, FPGA) by providing a unified API for interacting with JIT-compiled
|
||||
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
|
||||
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
|
||||
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
|
||||
MFEM_USE_GSLIB = YES/NO
|
||||
Enables MFEM functionality based on the GSLIB library, and specifically its
|
||||
FindPoints component, which provides robust algorithms to evaluate finite
|
||||
FindPoints component, which provides a robust algorithms to evaluate finite
|
||||
element functions in a collection of points in physical space. When enabled,
|
||||
the user can use the GSLIB-FindPoints methods as shown in miniapps/gslib.
|
||||
|
||||
@@ -725,18 +719,9 @@ The specific libraries and their options are:
|
||||
Options: STRUMPACK_OPT, STRUMPACK_LIB.
|
||||
Versions: STRUMPACK >= 3.0.0.
|
||||
|
||||
- CUDSS (optional), used when MFEM_USE_CUDSS = YES. Note that CUDSS requires
|
||||
CUDA 12.x toolkit and the cuDSS libraries. The supported communication backend
|
||||
is OpenMPI 4.x (default), and OpenMPI 4.x or a later version must be pre-built.
|
||||
The source files in the cuDSS tarball provide guidance for developing custom
|
||||
MPI implementations.
|
||||
URL: https://developer.nvidia.com/cudss
|
||||
https://docs.nvidia.com/cuda/cudss/advanced_features.html#communication-layer-library-in-cudss
|
||||
Options: CUDSS_OPT, CUDSS_LIB.
|
||||
Versions: cuDSS >= 0.6.0.
|
||||
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Ginkgo may have additional
|
||||
requirements and module-specific dependencies; see the webpage below.
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
|
||||
C++ compiler that supports the C++-17 standard. For additional requirements
|
||||
and dependencies of specific modules, see the Ginkgo webpage below.
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
|
||||
Debug).
|
||||
@@ -808,7 +793,7 @@ The specific libraries and their options are:
|
||||
Options: CONDUIT_OPT, CONDUIT_LIB.
|
||||
Versions: Conduit >= 0.3.1.
|
||||
|
||||
- ADIOS2 (optional), used when MFEM_USE_ADIOS2 = YES.
|
||||
- ADIOS2 (optional) used when MFEM_USE_ADIOS2 = YES.
|
||||
URL: https://adios2.readthedocs.io/
|
||||
Versions: ADIOS >= 2.5.0.
|
||||
|
||||
@@ -884,7 +869,7 @@ The specific libraries and their options are:
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 2022.10.3.
|
||||
|
||||
- Moonolith (optional), used when MFEM_USE_MOONOLITH = YES.
|
||||
- Moonolith (optional), use when MFEM_USE_MOONOLITH = YES.
|
||||
URL: https://bitbucket.org/zulianp/par_moonolith
|
||||
Options: MOONOLITH_DIR
|
||||
Versions: MOONOLITH >= 1.1.0.
|
||||
@@ -972,7 +957,7 @@ CMAKE_BUILD_TYPE which can be set to standard values like "Debug", and "Release"
|
||||
To use a specific generator use the "-G <generator>" option of cmake:
|
||||
|
||||
cmake <mfem-source-dir> -G "Xcode"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 17 2022"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 12 2013"
|
||||
cmake <mfem-source-dir> -G "MinGW Makefiles"
|
||||
|
||||
With CMake it is possible to build MFEM as a shared library using the standard
|
||||
@@ -1217,7 +1202,7 @@ larger problems, there are two options:
|
||||
Specific options for HIP
|
||||
========================
|
||||
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
|
||||
ROCm install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
|
||||
Specific options for RAJA+HIP+MPI
|
||||
=================================
|
||||
|
||||
@@ -28,7 +28,6 @@ license files. These software products and their licenses are as follows:
|
||||
* AmgXWrapper (linalg/amgxsolver.{hpp,cpp}) -- MIT license
|
||||
* Catch++ (tests/unit/catch.hpp) -- Boost 1.0 license
|
||||
* Gecko (general/gecko.{cpp,hpp}) -- BSD 3-clause license
|
||||
* gslib (fem/gslib.{cpp,hpp}, mesh/bb_grid_map.{cpp,hpp}) -- BSD 3-clause license
|
||||
* Picojson (fem/picojson.h) -- Custom 2-clause license
|
||||
* TinyXML2 (general/tinyxml2.{cpp,h}) -- zlib license
|
||||
* Zstr (general/zstr.hpp) -- MIT license
|
||||
|
||||
@@ -35,7 +35,6 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
|
||||
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
|
||||
set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
|
||||
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
|
||||
set(MFEM_USE_CUDSS @MFEM_USE_CUDSS@)
|
||||
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
|
||||
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
|
||||
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
|
||||
@@ -110,14 +109,6 @@ if (MFEM_USE_RAJA)
|
||||
find_dependency(RAJA)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CUDSS)
|
||||
find_dependency(cudss)
|
||||
endif (MFEM_USE_CUDSS)
|
||||
|
||||
if (MFEM_USE_UMPIRE)
|
||||
find_dependency(umpire)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET mfem)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/MFEMTargets.cmake)
|
||||
endif (NOT TARGET mfem)
|
||||
|
||||
@@ -108,15 +108,6 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
#cmakedefine MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
#cmakedefine MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
#cmakedefine MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
#cmakedefine MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable functionality based on the Ginkgo library.
|
||||
#cmakedefine MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
if (NOT cudss_DIR AND CUDSS_DIR)
|
||||
set(cudss_DIR ${CUDSS_DIR}/lib/cmake/cudss)
|
||||
endif()
|
||||
message(STATUS "Looking for CUDSS ...")
|
||||
message(STATUS " in CUDSS_DIR = ${CUDSS_DIR}")
|
||||
message(STATUS " cudss_DIR = ${cudss_DIR}")
|
||||
find_package(cudss)
|
||||
set(CUDSS_FOUND ${cudss_FOUND})
|
||||
set(CUDSS_LIBRARIES "cudss")
|
||||
if (CUDSS_FOUND)
|
||||
message(STATUS
|
||||
"Found CUDSS target: ${CUDSS_LIBRARIES} (version: ${cudss_VERSION})")
|
||||
else()
|
||||
set(msg STATUS)
|
||||
if (CUDSS_FIND_REQUIRED)
|
||||
set(msg FATAL_ERROR)
|
||||
endif()
|
||||
message(${msg}
|
||||
"CUDSS not found. Please set CUDSS_DIR to the install prefix.")
|
||||
endif()
|
||||
|
||||
if(CUDSS_FOUND AND TARGET cudss)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION)
|
||||
if(NOT CUDSS_LIBRARY_LOCATION)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
if(CUDSS_LIBRARY_LOCATION)
|
||||
get_filename_component(CUDSS_LIBRARY_DIR "${CUDSS_LIBRARY_LOCATION}" DIRECTORY)
|
||||
else()
|
||||
message(WARNING "Could not determine the location of the cuDSS library.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "cuDSS target not available; cannot determine library directory.")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS threading library if OpenMP is enabled.
|
||||
# The threading layer library (libcudss_mtlayer_gomp.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
if (MFEM_USE_OPENMP)
|
||||
find_file(
|
||||
CUDSS_THREADING_LIB
|
||||
NAMES libcudss_mtlayer_gomp.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_THREADING_LIB AND CUDSS_THREADING_LIB)
|
||||
set(MFEM_CUDSS_THREADING_LIB "${CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS threading layer library: ${MFEM_CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS communication library if MFEM use OpenMPI.
|
||||
# The communication layer library (libcudss_commlayer_mpi.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
# The communication layer library is used pre-built communication layers for OpenMPI
|
||||
# by default.
|
||||
if (MFEM_USE_MPI)
|
||||
find_file(
|
||||
CUDSS_COMM_LIB
|
||||
NAMES libcudss_commlayer_openmpi.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_COMM_LIB AND CUDSS_COMM_LIB)
|
||||
set(MFEM_CUDSS_COMM_LIB "${CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS communication layer library: ${MFEM_CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
@@ -18,17 +18,19 @@
|
||||
|
||||
if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
|
||||
enable_language(C)
|
||||
set(GSLIB_FETCH_VERSION 1.0.9)
|
||||
add_library(GSLIB STATIC IMPORTED)
|
||||
# set options (technically flags because GSLIB does not use cmake)
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(GSLIB_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(GSLIB_FLAGS "${GSLIB_FLAGS} -fPIC")
|
||||
set(GSLIB_FETCH_VERSION 1.0.9)
|
||||
set(GSLIB_C_FLAGS ${CMAKE_C_FLAGS_${BUILD_TYPE}})
|
||||
if (CMAKE_C_FLAGS)
|
||||
set(GSLIB_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
endif()
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(GSLIB_C_FLAGS "${GSLIB_C_FLAGS} -fPIC")
|
||||
endif()
|
||||
add_library(GSLIB STATIC IMPORTED)
|
||||
# define external project and create future include directory so it is present
|
||||
# to pass CMake checks at end of MFEM configuration step
|
||||
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_FLAGS}")
|
||||
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_C_FLAGS}")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/gslib)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(gslib
|
||||
@@ -38,7 +40,7 @@ if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS=${GSLIB_FLAGS}"
|
||||
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS= ${GSLIB_C_FLAGS}"
|
||||
INSTALL_COMMAND "")
|
||||
file(MAKE_DIRECTORY ${PREFIX}/include)
|
||||
# set imported library target properties
|
||||
|
||||
@@ -44,9 +44,6 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
|
||||
# set options and associated dependencies
|
||||
set(HYPRE_CMAKE_OPTIONS "")
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (BUILD_SHARED_LIBS)
|
||||
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON)
|
||||
endif()
|
||||
# collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
|
||||
get_cmake_property(all_vars VARIABLES)
|
||||
foreach(var ${all_vars})
|
||||
@@ -98,6 +95,7 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
SOURCE_SUBDIR src
|
||||
PREFIX ${HYPRE_INSTALL}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . -- -j${CMAKE_BUILD_PARALLEL_LEVEL}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${HYPRE_INSTALL} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${HYPRE_CMAKE_OPTIONS})
|
||||
file(MAKE_DIRECTORY ${HYPRE_INSTALL}/include)
|
||||
# set imported library target properties
|
||||
|
||||
@@ -19,18 +19,10 @@
|
||||
# - METIS_VERSION_5 (cache variable)
|
||||
|
||||
if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
|
||||
enable_language(C)
|
||||
set(METIS_FETCH_VERSION 4.0.3)
|
||||
add_library(METIS STATIC IMPORTED)
|
||||
# set options (technically flags because METIS does not use cmake)
|
||||
set(METIS_FLAGS "-Wno-implicit-int -Wno-incompatible-pointer-types")
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
|
||||
set(METIS_FLAGS "${METIS_FLAGS} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(METIS_FLAGS "${METIS_FLAGS} -fPIC")
|
||||
endif()
|
||||
# define external project
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with ${METIS_FLAGS}")
|
||||
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with default options")
|
||||
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/metis)
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(metis
|
||||
@@ -40,7 +32,7 @@ if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
|
||||
UPDATE_DISCONNECTED TRUE
|
||||
PREFIX ${PREFIX}
|
||||
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
|
||||
BUILD_COMMAND $(MAKE) clean && $(MAKE) "OPTFLAGS=${METIS_FLAGS}"
|
||||
BUILD_COMMAND $(MAKE) COPTIONS=-Wno-incompatible-pointer-types
|
||||
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
|
||||
# set imported library target properties
|
||||
add_dependencies(METIS metis)
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
# - UMPIRE_LIBRARIES
|
||||
# - UMPIRE_INCLUDE_DIRS
|
||||
|
||||
if (NOT umpire_ROOT AND UMPIRE_DIR)
|
||||
set(umpire_ROOT ${UMPIRE_DIR})
|
||||
if (NOT umpire_DIR AND UMPIRE_DIR)
|
||||
set(umpire_DIR ${UMPIRE_DIR}/lib/cmake/umpire)
|
||||
endif()
|
||||
message(STATUS "Looking for UMPIRE ...")
|
||||
message(STATUS " in UMPIRE_DIR = ${UMPIRE_DIR}")
|
||||
message(STATUS " umpire_ROOT = ${umpire_ROOT}")
|
||||
message(STATUS " umpire_DIR = ${umpire_DIR}")
|
||||
find_package(umpire CONFIG)
|
||||
set(UMPIRE_FOUND ${umpire_FOUND})
|
||||
set(UMPIRE_LIBRARIES "umpire")
|
||||
|
||||
@@ -157,10 +157,4 @@ constexpr real_t operator""_r(unsigned long long v)
|
||||
#endif
|
||||
#endif // MFEM_USE_MPI not defined
|
||||
|
||||
#ifndef MFEM_USE_CUDA
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
#error Building with cuDSS (MFEM_USE_CUDSS=YES) requires CUDA (MFEM_USE_CUDA=YES)
|
||||
#endif
|
||||
#endif // MFEM_USE_CUDSS not defined
|
||||
|
||||
#endif // MFEM_CONFIG_HPP
|
||||
|
||||
@@ -108,15 +108,6 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
// #define MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
// #define MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
// #define MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
// #define MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable MFEM features based on the Ginkgo library.
|
||||
// #define MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -36,9 +36,6 @@ MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
|
||||
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
|
||||
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
|
||||
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
|
||||
MFEM_USE_CUDSS = @MFEM_USE_CUDSS@
|
||||
MFEM_CUDSS_COMM_LIB = @MFEM_CUDSS_COMM_LIB@
|
||||
MFEM_CUDSS_THREADING_LIB = @MFEM_CUDSS_THREADING_LIB@
|
||||
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
|
||||
|
||||
@@ -38,7 +38,6 @@ option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
|
||||
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
|
||||
option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
|
||||
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
|
||||
option(MFEM_USE_CUDSS "Enable cuDSS usage" OFF)
|
||||
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
|
||||
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
|
||||
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
|
||||
|
||||
+1
-15
@@ -153,7 +153,6 @@ MFEM_USE_SUPERLU = NO
|
||||
MFEM_USE_SUPERLU5 = NO
|
||||
MFEM_USE_MUMPS = NO
|
||||
MFEM_USE_STRUMPACK = NO
|
||||
MFEM_USE_CUDSS = NO
|
||||
MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_MAGMA = NO
|
||||
@@ -369,19 +368,6 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
|
||||
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
|
||||
$(SCOTCH_LIB) $(SCALAPACK_LIB)
|
||||
|
||||
# CUDSS library configuration
|
||||
CUDSS_DIR = @MFEM_DIR@/../cudss
|
||||
CUDSS_INCLUDE_DIR = $(CUDSS_DIR)/include
|
||||
CUDSS_LIBRARY_DIR = $(CUDSS_DIR)/lib
|
||||
CUDSS_OPT = -I$(CUDSS_INCLUDE_DIR)
|
||||
CUDSS_LIB = \
|
||||
$(XLINKER)-rpath,$(CUDSS_LIBRARY_DIR) -L$(CUDSS_LIBRARY_DIR) -lcudss
|
||||
# The cuDSS communication and threading libraries.
|
||||
MFEM_CUDSS_COMM_LIB = $(abspath $(wildcard $(or $(CUDSS_COMM_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR), $(CUDSS_LIBRARY_DIR)/libcudss_commlayer_openmpi.so))))
|
||||
MFEM_CUDSS_THREADING_LIB = $(abspath $(wildcard $(or $(CUDSS_THREADING_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR),$(CUDSS_LIBRARY_DIR)/libcudss_mtlayer_gomp.so))))
|
||||
|
||||
# Ginkgo library configuration
|
||||
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
|
||||
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
|
||||
@@ -635,7 +621,7 @@ PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
|
||||
AXOM_DIR = @MFEM_DIR@/../axom
|
||||
TRIBOL_DIR = @MFEM_DIR@/../tribol
|
||||
TRIBOL_OPT = -I$(TRIBOL_DIR)/include -I$(AXOM_DIR)/include
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -ltribol_shared -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
@@ -215,7 +215,7 @@ if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME ex1p_ceed_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex1p> "-no-vis" "-d" "ceed-cpu" "-pa" "-a"
|
||||
$<TARGET_FILE:ex1p> "-no-vis" "-d ceed-cpu" "-pa" "-a"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -64,7 +64,7 @@ PARALLEL_NAME := Parallel AMGX example
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not build)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
clean: clean-build
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
|
||||
@@ -64,12 +64,12 @@ ex1p-test-par: ex1p
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
clean: clean-build clean-exec $(SUBDIRS_CLEAN)
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh mesh.*
|
||||
@rm -f sol.*
|
||||
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
|
||||
|
||||
+21
-34
@@ -50,10 +50,6 @@
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
//
|
||||
// Device simplices sample runs:
|
||||
// ex1 -pa -d gpu -m ../data/inline-tet.mesh
|
||||
// ex1 -pa -d gpu -m ../data/inline-tri.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Poisson problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
@@ -142,25 +138,25 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange finite elements of the specified order.
|
||||
// - If order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// - If the mesh is simplicial and partial assembly is requested,
|
||||
// we use the positive basis, which supports device execution.
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
auto basis_type = (pa && mesh.IsSimplexMesh()) ?
|
||||
BasisType::Positive : BasisType::GaussLobatto;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim, basis_type);
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (mesh.GetNodes())
|
||||
{
|
||||
fec = mesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim, basis_type);
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
FiniteElementSpace fespace(&mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
@@ -228,29 +224,17 @@ int main(int argc, char *argv[])
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
// Use cuDSS to solve the system.
|
||||
CuDSSSolver cudss_solver;
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -289,14 +273,17 @@ int main(int argc, char *argv[])
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
if (order > 0) { delete fec; }
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,9 +5,9 @@
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 464 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 82
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3877 -o 2 -sys
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
|
||||
|
||||
+128
-60
@@ -42,11 +42,7 @@
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/beam-tet.mesh
|
||||
//
|
||||
// Device simplices sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tet.mesh
|
||||
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tri.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Poisson problem
|
||||
@@ -70,6 +66,26 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class VisServer
|
||||
{
|
||||
const char *path{};
|
||||
int port{-1};
|
||||
|
||||
struct Deleter { void operator()(FILE *); };
|
||||
std::unique_ptr<FILE, Deleter> fglvis;
|
||||
|
||||
void Open();
|
||||
public:
|
||||
VisServer(const char *path_, int port_ = 19916)
|
||||
: path(path_), port(port_) { Open(); }
|
||||
|
||||
explicit VisServer() = default;
|
||||
|
||||
inline const char *GetPath() const { return path; }
|
||||
inline int GetPort() const { return port; }
|
||||
inline bool IsOpen() const { return (fglvis != nullptr); }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
@@ -86,10 +102,8 @@ int main(int argc, char *argv[])
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
const char *visserver = "";
|
||||
bool algebraic_ceed = false;
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
bool cudss_solver = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -109,14 +123,12 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&algebraic_ceed, "-a", "--algebraic",
|
||||
"-no-a", "--no-algebraic",
|
||||
"Use algebraic Ceed solver");
|
||||
#endif
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
args.AddOption(&cudss_solver, "-cudss", "--cudss-solver", "-no-cudss",
|
||||
"--no-cudss-solver", "Use the cuDSS Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&visserver, "-vs", "--visualization-server",
|
||||
"Path to GLVis binary to start own server.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -169,20 +181,19 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order.
|
||||
// - If order < 1, we instead use an isoparametric/isogeometric space.
|
||||
// - If the mesh is simplicial and partial assembly is requested,
|
||||
// we use the positive basis, which supports device execution.
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
auto basis_type = (pa && pmesh.IsSimplexMesh()) ?
|
||||
BasisType::Positive : BasisType::GaussLobatto;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim, basis_type);
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
@@ -190,7 +201,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim, basis_type);
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
@@ -259,51 +271,33 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (!pa && (Device::Allows(Backend::CUDA_MASK) && cudss_solver))
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
// Solve using a direct solver with cuDSS
|
||||
CuDSSSolver cudss_solver(MPI_COMM_WORLD);
|
||||
cudss_solver.SetMatrixSymType(
|
||||
CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE);
|
||||
cudss_solver.SetMatrixViewType(CuDSSSolver::UPPER);
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec)
|
||||
{
|
||||
cg.SetPreconditioner(*prec);
|
||||
}
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
@@ -330,6 +324,14 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
|
||||
VisServer server;
|
||||
if (strlen(visserver) > 0)
|
||||
{
|
||||
server = VisServer(visserver, visport);
|
||||
visport = server.GetPort();
|
||||
}
|
||||
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
@@ -337,7 +339,73 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (order > 0) { delete fec; }
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VisServer::Open()
|
||||
{
|
||||
if (!path || strlen(path) <= 0) { return; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Mpi::Root())
|
||||
#endif // MFEM_USE_MPI
|
||||
{
|
||||
// Open a log file
|
||||
FILE *ftmp = popen("mktemp --tmpdir glvis-log.XXXXXX", "r");
|
||||
constexpr size_t ssize = 256;
|
||||
char stmp[ssize];
|
||||
fgets(stmp, ssize, ftmp);
|
||||
pclose(ftmp);
|
||||
int len = strlen(stmp);
|
||||
if (len > 0 && stmp[len-1] == '\n') { stmp[--len] = '\0'; }
|
||||
std::cout << "Starting GLVis log in: " << stmp << std::endl;
|
||||
|
||||
// Start the server
|
||||
std::stringstream ss;
|
||||
ss << "trap '' SIGPIPE && " << path <<
|
||||
" -no-pr -no-ex 2>&1 | tee \"" << stmp <<
|
||||
"\" 2> /dev/null | grep -m 1 ^GLVIS_SERVER_PORT";
|
||||
fglvis.reset(popen(ss.str().c_str(), "r"));
|
||||
|
||||
// Capture the port number
|
||||
char line[ssize];
|
||||
bool captured = false;
|
||||
while (fgets(line, ssize, fglvis.get()))
|
||||
{
|
||||
if (strncmp(line, "GLVIS_SERVER_PORT=", 18) == 0)
|
||||
{
|
||||
sscanf(line, "GLVIS_SERVER_PORT=%d", &port);
|
||||
captured = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (captured)
|
||||
{
|
||||
std::cout << "Started GLVis server at port: " << port << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "GLVis server did not start normally." << std::endl;
|
||||
}
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Bcast(&port, 1, MPI_INT, 0, MPI_COMM_WORLD);
|
||||
#endif // MFEM_USE_MPI
|
||||
}
|
||||
|
||||
void VisServer::Deleter::operator()(FILE *f)
|
||||
{
|
||||
if (f != nullptr)
|
||||
{
|
||||
int ierr = pclose(f);
|
||||
if (ierr != 0)
|
||||
{
|
||||
std::cerr << "GLVis server pclose() returns: " << ierr << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-27
@@ -302,21 +302,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
|
||||
<< "window_title 'Exact: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
|
||||
<< "window_title 'Exact: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
|
||||
// 11. Set up the parallel sesquilinear form a(.,.) on the finite element
|
||||
@@ -540,21 +534,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u.real()
|
||||
<< "window_title 'Solution: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u.imag()
|
||||
<< "window_title 'Solution: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
if (visualization && exact_sol)
|
||||
{
|
||||
@@ -563,21 +551,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
|
||||
<< "window_title 'Error: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
|
||||
<< "window_title 'Error: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
|
||||
@@ -95,15 +95,6 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (amg_elast && !static_cond && reorder_space)
|
||||
{
|
||||
if (myid == 0)
|
||||
cerr << "\nThe AMG elasticity solver requires ordering byVDIM! "
|
||||
<< "Ignoring the specified option -nodes/--by-nodes.\n"
|
||||
<< endl;
|
||||
reorder_space = false;
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
|
||||
+52
-11
@@ -5,8 +5,8 @@
|
||||
// Sample runs:
|
||||
// ex37 -alpha 10
|
||||
// ex37 -alpha 10 -pv
|
||||
// ex37 -lambda 0.1 -mu 0.1 -growth 1
|
||||
// ex37 -o 2 -alpha 10.0 -mi 50 -vf 0.4 -ntol 1e-5 -growth 1.5
|
||||
// ex37 -lambda 0.1 -mu 0.1
|
||||
// ex37 -o 2 -alpha 5.0 -mi 50 -vf 0.4 -ntol 1e-5
|
||||
// ex37 -r 6 -o 1 -alpha 25.0 -epsilon 0.02 -mi 50 -ntol 1e-5
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve a
|
||||
@@ -55,6 +55,53 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
/**
|
||||
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
|
||||
* ∫_Ω ρ dx = θ vol(Ω) as follows:
|
||||
*
|
||||
* 1. Compute the root of the R → R function
|
||||
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
|
||||
* 2. Set ψ ← ψ + c.
|
||||
*
|
||||
* @param psi a GridFunction to be updated
|
||||
* @param target_volume θ vol(Ω)
|
||||
* @param tol Newton iteration tolerance
|
||||
* @param max_its Newton maximum iteration number
|
||||
* @return real_t Final volume, ∫_Ω sigmoid(ψ)
|
||||
*/
|
||||
real_t proj(GridFunction &psi, real_t target_volume, real_t tol=1e-12,
|
||||
int max_its=10)
|
||||
{
|
||||
MappedGridFunctionCoefficient sigmoid_psi(&psi, sigmoid);
|
||||
MappedGridFunctionCoefficient der_sigmoid_psi(&psi, der_sigmoid);
|
||||
|
||||
LinearForm int_sigmoid_psi(psi.FESpace());
|
||||
int_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
|
||||
LinearForm int_der_sigmoid_psi(psi.FESpace());
|
||||
int_der_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(
|
||||
der_sigmoid_psi));
|
||||
bool done = false;
|
||||
for (int k=0; k<max_its; k++) // Newton iteration
|
||||
{
|
||||
int_sigmoid_psi.Assemble(); // Recompute f(c) with updated ψ
|
||||
const real_t f = int_sigmoid_psi.Sum() - target_volume;
|
||||
|
||||
int_der_sigmoid_psi.Assemble(); // Recompute df(c) with updated ψ
|
||||
const real_t df = int_der_sigmoid_psi.Sum();
|
||||
|
||||
const real_t dc = -f/df;
|
||||
psi += dc;
|
||||
if (abs(dc) < tol) { done = true; break; }
|
||||
}
|
||||
if (!done)
|
||||
{
|
||||
mfem_warning("Projection reached maximum iteration without converging. "
|
||||
"Result may not be accurate.");
|
||||
}
|
||||
int_sigmoid_psi.Assemble();
|
||||
return int_sigmoid_psi.Sum();
|
||||
}
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ALGORITHM PREAMBLE
|
||||
@@ -133,11 +180,10 @@ int main(int argc, char *argv[])
|
||||
int ref_levels = 5;
|
||||
int order = 2;
|
||||
real_t alpha = 1.0;
|
||||
real_t growth = 2;
|
||||
real_t epsilon = 0.01;
|
||||
real_t vol_fraction = 0.5;
|
||||
int max_it = 1e3;
|
||||
real_t itol = 1e-2;
|
||||
real_t itol = 1e-1;
|
||||
real_t ntol = 1e-4;
|
||||
real_t rho_min = 1e-6;
|
||||
real_t lambda = 1.0;
|
||||
@@ -152,8 +198,6 @@ int main(int argc, char *argv[])
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&alpha, "-alpha", "--alpha-step-length",
|
||||
"Step length for gradient descent.");
|
||||
args.AddOption(&growth, "-growth", "--alpha-growth-rate",
|
||||
"Growth rate of step length for gradient descent.");
|
||||
args.AddOption(&epsilon, "-epsilon", "--epsilon-thickness",
|
||||
"Length scale for ρ.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
@@ -288,7 +332,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
FilterSolver->SetEssentialBoundary(ess_bdr_filter);
|
||||
FilterSolver->SetupFEM();
|
||||
FilterSolver->AssembleDiffusionBilinear();
|
||||
|
||||
BilinearForm mass(&control_fes);
|
||||
mass.AddDomainIntegrator(new InverseIntegrator(new MassIntegrator(one)));
|
||||
@@ -342,7 +385,7 @@ int main(int argc, char *argv[])
|
||||
// 11. Iterate:
|
||||
for (int k = 1; k <= max_it; k++)
|
||||
{
|
||||
if (k > 1) { alpha = std::pow((real_t) k,growth); }
|
||||
if (k > 1) { alpha *= ((real_t) k) / ((real_t) k-1); }
|
||||
|
||||
mfem::out << "\nStep = " << k << std::endl;
|
||||
|
||||
@@ -379,9 +422,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Step 5 - Update design variable ψ ← proj(ψ - αG)
|
||||
psi.Add(-alpha, grad);
|
||||
GridFunction alpha_grad(grad);
|
||||
alpha_grad *= alpha;
|
||||
const real_t material_volume = proj(psi, alpha_grad, target_volume);
|
||||
const real_t material_volume = proj(psi, target_volume);
|
||||
|
||||
// Compute ||ρ - ρ_old|| in control fes.
|
||||
real_t norm_increment = zerogf.ComputeL1Error(succ_diff_rho);
|
||||
|
||||
+23
-183
@@ -137,7 +137,7 @@ public:
|
||||
exponent(exponent_), rho_min(rho_min_)
|
||||
{
|
||||
MFEM_ASSERT(rho_min_ >= 0.0, "rho_min must be >= 0");
|
||||
MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be < 1");
|
||||
MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be > 1");
|
||||
MFEM_ASSERT(u, "displacement field is not set");
|
||||
MFEM_ASSERT(rho_filter, "density field is not set");
|
||||
}
|
||||
@@ -231,12 +231,9 @@ private:
|
||||
FiniteElementCollection * fec = nullptr;
|
||||
FiniteElementSpace * fes = nullptr;
|
||||
Array<int> ess_bdr;
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> neumann_bdr;
|
||||
GridFunction * u = nullptr;
|
||||
LinearForm * b = nullptr;
|
||||
BilinearForm * a = nullptr;
|
||||
OperatorPtr A;
|
||||
bool parallel;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh * pmesh = nullptr;
|
||||
@@ -270,8 +267,6 @@ public:
|
||||
void ResetFEM();
|
||||
void SetupFEM();
|
||||
|
||||
void UpdateEssentialTDofs();
|
||||
void AssembleDiffusionBilinear(bool update_ess_tdofs=true);
|
||||
void Solve();
|
||||
GridFunction * GetFEMSolution();
|
||||
LinearForm * GetLinearForm() {return b;}
|
||||
@@ -376,130 +371,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
|
||||
* ∫_Ω ρ dx = θ vol(Ω) as follows:
|
||||
*
|
||||
* 1. Compute the root of the R → R function
|
||||
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
|
||||
* using the Illinois method
|
||||
* 2. Set ψ ← ψ + c.
|
||||
*
|
||||
* @param psi a GridFunction to be updated
|
||||
* @param alpha_grad alpha multiplied by gradient
|
||||
* @param target_volume θ vol(Ω)
|
||||
* @param tol Illinois iteration tolerance
|
||||
* @param max_its Illinois maximum iteration number
|
||||
* @return real_t Final volume (∫_Ω sigmoid(ψ) dx)
|
||||
*/
|
||||
real_t proj(GridFunction &psi, GridFunction &alpha_grad, real_t target_volume,
|
||||
real_t tol = 1e-12, int max_its = 100)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
FiniteElementSpace *fes = psi.FESpace();
|
||||
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace*>(fes);
|
||||
#endif
|
||||
ConstantCoefficient zero_cf(0.0);
|
||||
real_t a = -alpha_grad.ComputeMaxError(zero_cf);
|
||||
real_t b = -a;
|
||||
real_t y = 0.0;
|
||||
|
||||
MappedGridFunctionCoefficient sigmoid_psi(
|
||||
&psi, [&y](const real_t x) { return sigmoid(x + y); });
|
||||
std::unique_ptr<LinearForm> int_sigmoid_psi;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParGridFunction *par_psi = dynamic_cast<ParGridFunction *>(&psi);
|
||||
if (par_psi)
|
||||
{
|
||||
int_sigmoid_psi.reset(new ParLinearForm(par_psi->ParFESpace()));
|
||||
}
|
||||
else
|
||||
{
|
||||
int_sigmoid_psi.reset(new LinearForm(psi.FESpace()));
|
||||
}
|
||||
#else
|
||||
int_sigmoid_psi.reset(new LinearForm(psi.FESpace()));
|
||||
#endif
|
||||
int_sigmoid_psi->AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
|
||||
|
||||
y = a;
|
||||
int_sigmoid_psi->Assemble();
|
||||
real_t f_a = int_sigmoid_psi->Sum(); // f_a := f(a) + θ vol(Ω)
|
||||
|
||||
y = b;
|
||||
int_sigmoid_psi->Assemble();
|
||||
real_t f_b = int_sigmoid_psi->Sum(); // f_b := f(b) + θ vol(Ω)
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (pfes)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f_a, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f_b, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
f_a -= target_volume; // f_a := f(a)
|
||||
f_b -= target_volume; // f_b := f(b)
|
||||
real_t c = 0.0;
|
||||
real_t f_c = 0.0;
|
||||
int side = 0;
|
||||
|
||||
bool done = false;
|
||||
for (int k=0; k < max_its; k++)
|
||||
{
|
||||
c = (f_a * b - f_b * a) / (f_a - f_b);
|
||||
|
||||
if (abs(b - a) < tol * abs(b + a)) { done = true; break; }
|
||||
|
||||
y = c;
|
||||
int_sigmoid_psi->Assemble();
|
||||
f_c = int_sigmoid_psi->Sum(); // f_c := f(c) + θ vol(Ω)
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (pfes)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f_c, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
f_c -= target_volume; // f_c := f(c)
|
||||
|
||||
if (f_c * f_b > 0)
|
||||
{
|
||||
b = c;
|
||||
f_b = f_c;
|
||||
if (side == -1) { f_a /= 2.0; }
|
||||
side = -1;
|
||||
}
|
||||
else if (f_c * f_a > 0)
|
||||
{
|
||||
a = c;
|
||||
f_a = f_c;
|
||||
if (side == 1) { f_b /= 2.0; }
|
||||
side = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
done = true; break;
|
||||
}
|
||||
}
|
||||
if (!done)
|
||||
{
|
||||
mfem_warning("Projection reached maximum iteration without converging. "
|
||||
"Result may not be accurate.");
|
||||
}
|
||||
y = 0.0;
|
||||
psi += c;
|
||||
int_sigmoid_psi->Assemble();
|
||||
real_t material_volume = int_sigmoid_psi->Sum();
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (pfes)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE, &material_volume, 1,
|
||||
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
|
||||
}
|
||||
#endif
|
||||
return material_volume;
|
||||
}
|
||||
|
||||
// Poisson solver
|
||||
|
||||
@@ -551,8 +422,12 @@ void DiffusionSolver::SetupFEM()
|
||||
}
|
||||
}
|
||||
|
||||
void DiffusionSolver::UpdateEssentialTDofs()
|
||||
void DiffusionSolver::Solve()
|
||||
{
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
Array<int> ess_tdof_list;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
@@ -565,39 +440,7 @@ void DiffusionSolver::UpdateEssentialTDofs()
|
||||
#else
|
||||
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DiffusionSolver::AssembleDiffusionBilinear(bool update_ess_tdofs)
|
||||
{
|
||||
if (update_ess_tdofs)
|
||||
{
|
||||
UpdateEssentialTDofs();
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
a = new ParBilinearForm(pfes);
|
||||
}
|
||||
else
|
||||
{
|
||||
a = new BilinearForm(fes);
|
||||
}
|
||||
#else
|
||||
a = new BilinearForm(fes);
|
||||
#endif
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf));
|
||||
if (masscf)
|
||||
{
|
||||
a->AddDomainIntegrator(new MassIntegrator(*masscf));
|
||||
}
|
||||
a->Assemble();
|
||||
a->FormSystemMatrix(ess_tdof_list, A);
|
||||
}
|
||||
|
||||
void DiffusionSolver::Solve()
|
||||
{
|
||||
Vector B, X;
|
||||
|
||||
*u=0.0;
|
||||
if (b)
|
||||
{
|
||||
delete b;
|
||||
@@ -632,33 +475,31 @@ void DiffusionSolver::Solve()
|
||||
|
||||
b->Assemble();
|
||||
|
||||
*u=0.0;
|
||||
if (essbdr_cf)
|
||||
{
|
||||
u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr);
|
||||
}
|
||||
BilinearForm * a = nullptr;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
X.SetSize(pfes->TrueVSize());
|
||||
B.SetSize(pfes->TrueVSize());
|
||||
dynamic_cast<ParGridFunction*>(u)->ParallelAssemble(X);
|
||||
dynamic_cast<ParLinearForm*>(b)->ParallelAssemble(B);
|
||||
dynamic_cast<ParBilinearForm*>(a)->ParallelEliminateTDofsInRHS(
|
||||
ess_tdof_list, X, B);
|
||||
a = new ParBilinearForm(pfes);
|
||||
}
|
||||
else
|
||||
{
|
||||
X.NewDataAndSize(u->GetData(), u->Size());
|
||||
B.NewDataAndSize(b->GetData(), b->Size());
|
||||
a->EliminateVDofsInRHS(ess_tdof_list, X, B);
|
||||
a = new BilinearForm(fes);
|
||||
}
|
||||
#else
|
||||
X.NewDataAndSize(u->GetData(), u->Size());
|
||||
B.NewDataAndSize(b->GetData(), b->Size());
|
||||
a->EliminateVDofsInRHS(ess_tdof_list, X, B);
|
||||
a = new BilinearForm(fes);
|
||||
#endif
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf));
|
||||
if (masscf)
|
||||
{
|
||||
a->AddDomainIntegrator(new MassIntegrator(*masscf));
|
||||
}
|
||||
a->Assemble();
|
||||
if (essbdr_cf)
|
||||
{
|
||||
u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr);
|
||||
}
|
||||
a->FormLinearSystem(ess_tdof_list, *u, *b, A, X, B);
|
||||
|
||||
CGSolver * cg = nullptr;
|
||||
Solver * M = nullptr;
|
||||
@@ -687,6 +528,7 @@ void DiffusionSolver::Solve()
|
||||
delete M;
|
||||
delete cg;
|
||||
a->RecoverFEMSolution(X, *b, *u);
|
||||
delete a;
|
||||
}
|
||||
|
||||
GridFunction * DiffusionSolver::GetFEMSolution()
|
||||
@@ -718,8 +560,6 @@ DiffusionSolver::~DiffusionSolver()
|
||||
#endif
|
||||
delete fec; fec = nullptr;
|
||||
delete b;
|
||||
A.Clear();
|
||||
delete a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+60
-11
@@ -4,8 +4,8 @@
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex37p -alpha 10 -pv
|
||||
// mpirun -np 4 ex37p -lambda 0.1 -mu 0.1 -growth 1
|
||||
// mpirun -np 4 ex37p -o 2 -alpha 10.0 -mi 50 -vf 0.4 -ntol 1e-5 -growth 1.5
|
||||
// mpirun -np 4 ex37p -lambda 0.1 -mu 0.1
|
||||
// mpirun -np 4 ex37p -o 2 -alpha 5.0 -mi 50 -vf 0.4 -ntol 1e-5
|
||||
// mpirun -np 4 ex37p -r 6 -o 2 -alpha 10.0 -epsilon 0.02 -mi 50 -ntol 1e-5
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve a
|
||||
@@ -54,6 +54,61 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
/**
|
||||
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
|
||||
* ∫_Ω ρ dx = θ vol(Ω) as follows:
|
||||
*
|
||||
* 1. Compute the root of the R → R function
|
||||
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
|
||||
* 2. Set ψ ← ψ + c.
|
||||
*
|
||||
* @param psi a GridFunction to be updated
|
||||
* @param target_volume θ vol(Ω)
|
||||
* @param tol Newton iteration tolerance
|
||||
* @param max_its Newton maximum iteration number
|
||||
* @return real_t Final volume, ∫_Ω sigmoid(ψ)
|
||||
*/
|
||||
real_t proj(ParGridFunction &psi, real_t target_volume, real_t tol=1e-12,
|
||||
int max_its=10)
|
||||
{
|
||||
MappedGridFunctionCoefficient sigmoid_psi(&psi, sigmoid);
|
||||
MappedGridFunctionCoefficient der_sigmoid_psi(&psi, der_sigmoid);
|
||||
|
||||
ParLinearForm int_sigmoid_psi(psi.ParFESpace());
|
||||
int_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
|
||||
ParLinearForm int_der_sigmoid_psi(psi.ParFESpace());
|
||||
int_der_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(
|
||||
der_sigmoid_psi));
|
||||
bool done = false;
|
||||
for (int k=0; k<max_its; k++) // Newton iteration
|
||||
{
|
||||
int_sigmoid_psi.Assemble(); // Recompute f(c) with updated ψ
|
||||
real_t f = int_sigmoid_psi.Sum();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &f, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
f -= target_volume;
|
||||
|
||||
int_der_sigmoid_psi.Assemble(); // Recompute df(c) with updated ψ
|
||||
real_t df = int_der_sigmoid_psi.Sum();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &df, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
const real_t dc = -f/df;
|
||||
psi += dc;
|
||||
if (abs(dc) < tol) { done = true; break; }
|
||||
}
|
||||
if (!done)
|
||||
{
|
||||
mfem_warning("Projection reached maximum iteration without converging. "
|
||||
"Result may not be accurate.");
|
||||
}
|
||||
int_sigmoid_psi.Assemble();
|
||||
real_t material_volume = int_sigmoid_psi.Sum();
|
||||
MPI_Allreduce(MPI_IN_PLACE, &material_volume, 1,
|
||||
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
|
||||
return material_volume;
|
||||
}
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* ALGORITHM PREAMBLE
|
||||
@@ -138,11 +193,10 @@ int main(int argc, char *argv[])
|
||||
int ref_levels = 5;
|
||||
int order = 2;
|
||||
real_t alpha = 1.0;
|
||||
real_t growth = 2;
|
||||
real_t epsilon = 0.01;
|
||||
real_t vol_fraction = 0.5;
|
||||
int max_it = 1e3;
|
||||
real_t itol = 1e-2;
|
||||
real_t itol = 1e-1;
|
||||
real_t ntol = 1e-4;
|
||||
real_t rho_min = 1e-6;
|
||||
real_t lambda = 1.0;
|
||||
@@ -157,8 +211,6 @@ int main(int argc, char *argv[])
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&alpha, "-alpha", "--alpha-step-length",
|
||||
"Step length for gradient descent.");
|
||||
args.AddOption(&growth, "-growth", "--alpha-growth-rate",
|
||||
"Growth rate of step length for gradient descent.");
|
||||
args.AddOption(&epsilon, "-epsilon", "--epsilon-thickness",
|
||||
"Length scale for ρ.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
@@ -307,7 +359,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
FilterSolver->SetEssentialBoundary(ess_bdr_filter);
|
||||
FilterSolver->SetupFEM();
|
||||
FilterSolver->AssembleDiffusionBilinear();
|
||||
|
||||
ParBilinearForm mass(&control_fes);
|
||||
mass.AddDomainIntegrator(new InverseIntegrator(new MassIntegrator(one)));
|
||||
@@ -361,7 +412,7 @@ int main(int argc, char *argv[])
|
||||
// 11. Iterate:
|
||||
for (int k = 1; k <= max_it; k++)
|
||||
{
|
||||
if (k > 1) { alpha = std::pow((real_t) k,growth); }
|
||||
if (k > 1) { alpha *= ((real_t) k) / ((real_t) k-1); }
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
@@ -401,9 +452,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Step 5 - Update design variable ψ ← proj(ψ - αG)
|
||||
psi.Add(-alpha, grad);
|
||||
ParGridFunction alpha_grad(grad);
|
||||
alpha_grad *= alpha;
|
||||
const real_t material_volume = proj(psi, alpha_grad, target_volume);
|
||||
const real_t material_volume = proj(psi, target_volume);
|
||||
|
||||
// Compute ||ρ - ρ_old|| in control fes.
|
||||
real_t norm_increment = zerogf.ComputeL1Error(succ_diff_rho);
|
||||
|
||||
@@ -76,4 +76,4 @@ clean-build:
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh sol.gf mesh.* sol.*
|
||||
@rm -f refined.mesh sol.gf
|
||||
|
||||
+2
-7
@@ -71,7 +71,6 @@ endif
|
||||
|
||||
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
|
||||
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
|
||||
SUBDIRS_TEST_NOCLEAN = $(addsuffix /test-noclean,$(SUBDIRS))
|
||||
SUBDIRS_CLEAN = $(addsuffix /clean,$(SUBDIRS))
|
||||
SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
|
||||
|
||||
@@ -88,9 +87,8 @@ SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
|
||||
|
||||
all: $(EXAMPLES) $(SUBDIRS_ALL)
|
||||
|
||||
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) \
|
||||
$(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
|
||||
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) $(SUBDIRS_CLEAN):
|
||||
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
|
||||
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN):
|
||||
$(MAKE) -C $(@D) $(@F)
|
||||
$(SUBDIRS_TPRINT):
|
||||
@$(MAKE) -C $(@D) $(@F)
|
||||
@@ -109,7 +107,6 @@ endif
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
test: $(SUBDIRS_TEST)
|
||||
test-noclean: $(SUBDIRS_TEST_NOCLEAN)
|
||||
test-print: $(SUBDIRS_TPRINT)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
@@ -160,8 +157,6 @@ ex37-test-seq: ex37
|
||||
@$(call mfem-test,$<,, Serial example,-mi 3)
|
||||
ex37p-test-par: ex37p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-mi 3)
|
||||
ex39-test-seq: ex39
|
||||
@$(call mfem-test,$<,, Serial example,-m ../data/compass.mesh)
|
||||
ex41-test-seq: ex41
|
||||
@$(call mfem-test,$<,, Serial example,-tf 1.0)
|
||||
ex41p-test-par: ex41p
|
||||
|
||||
@@ -171,12 +171,8 @@ set(SRCS
|
||||
tmop_tools.cpp
|
||||
tmop_amr.cpp
|
||||
gslib.cpp
|
||||
gslib/findptsedge_local_2.cpp
|
||||
gslib/findptsedge_local_3.cpp
|
||||
gslib/findptssurf_local_3.cpp
|
||||
gslib/findpts_local_2.cpp
|
||||
gslib/findpts_local_3.cpp
|
||||
gslib/interpolate_local_1.cpp
|
||||
gslib/interpolate_local_2.cpp
|
||||
gslib/interpolate_local_3.cpp
|
||||
transfer.cpp
|
||||
@@ -195,14 +191,12 @@ set(HDRS
|
||||
integ/bilininteg_dgtrace_kernels.hpp
|
||||
integ/bilininteg_vecdiffusion_kernels.hpp
|
||||
integ/bilininteg_convection_kernels.hpp
|
||||
integ/bilininteg_diffusion_pa_simplices.hpp
|
||||
integ/bilininteg_diffusion_kernels.hpp
|
||||
integ/bilininteg_elasticity_kernels.hpp
|
||||
integ/bilininteg_hcurl_kernels.hpp
|
||||
integ/bilininteg_hdiv_kernels.hpp
|
||||
integ/bilininteg_hcurlhdiv_kernels.hpp
|
||||
integ/bilininteg_mass_kernels.hpp
|
||||
integ/bilininteg_mass_pa_simplices.hpp
|
||||
integ/bilininteg_vecdiffusion_pa.hpp
|
||||
integ/bilininteg_vecmass_pa.hpp
|
||||
coefficient.hpp
|
||||
|
||||
+3
-32
@@ -729,8 +729,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
tr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (tr != NULL)
|
||||
{
|
||||
mfem::DofTransformation doftrans;
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
fe1 = fes -> GetFE (tr -> Elem1No);
|
||||
// The fe2 object is really a dummy and not used on the boundaries,
|
||||
// but we can't dereference a NULL pointer, and we don't want to
|
||||
@@ -744,7 +743,6 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
|
||||
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
|
||||
elemmat);
|
||||
doftrans.TransformDual(elemmat);
|
||||
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -1255,31 +1253,6 @@ void BilinearForm::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void BilinearForm::AddMult(const Vector &x, Vector &y, const real_t a) const
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->AddMult(x, y, a);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->AddMult(x, y, a);
|
||||
}
|
||||
}
|
||||
|
||||
void BilinearForm::AddMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a) const
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->AddMultTranspose(x, y, a);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->AddMultTranspose(x, y, a);
|
||||
}
|
||||
}
|
||||
|
||||
void BilinearForm::MultTranspose(const Vector & x, Vector & y) const
|
||||
{
|
||||
if (ext)
|
||||
@@ -1750,7 +1723,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < trial_fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
@@ -1759,8 +1731,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
ftr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (ftr != NULL)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs, ran_dof_trans);
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
@@ -1776,7 +1748,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,8 +307,8 @@ public:
|
||||
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
|
||||
|
||||
/// Add the matrix vector multiple to a vector: $ y += a M x $
|
||||
void AddMult(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const override;
|
||||
void AddMult(const Vector &x, Vector &y, const real_t a = 1.0) const override
|
||||
{ mat -> AddMult (x, y, a); }
|
||||
|
||||
/** @brief Add the original uneliminated matrix vector multiple to a vector.
|
||||
The original matrix is $ M + Me $ so we have:
|
||||
@@ -318,7 +318,8 @@ public:
|
||||
|
||||
/// Add the matrix transpose vector multiplication: $ y += a M^T x $
|
||||
void AddMultTranspose(const Vector & x, Vector & y,
|
||||
const real_t a = 1.0) const override;
|
||||
const real_t a = 1.0) const override
|
||||
{ mat->AddMultTranspose(x, y, a); }
|
||||
|
||||
/** @brief Add the original uneliminated matrix transpose vector
|
||||
multiple to a vector. The original matrix is $ M + M_e $
|
||||
|
||||
+4
-22
@@ -1345,8 +1345,7 @@ real_t DiffusionIntegrator::ComputeFluxEnergy
|
||||
}
|
||||
|
||||
const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe,
|
||||
const bool stroud)
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe)
|
||||
{
|
||||
int order;
|
||||
if (trial_fe.Space() == FunctionSpace::Pk)
|
||||
@@ -1363,15 +1362,7 @@ const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
if (stroud)
|
||||
{
|
||||
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
MassIntegrator::MassIntegrator(const IntegrationRule *ir)
|
||||
@@ -1458,8 +1449,7 @@ void MassIntegrator::AssembleElementMatrix2(
|
||||
|
||||
const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans,
|
||||
const bool stroud)
|
||||
const ElementTransformation &Trans)
|
||||
{
|
||||
// int order = trial_fe.GetOrder() + test_fe.GetOrder();
|
||||
const int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
|
||||
@@ -1468,15 +1458,7 @@ const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
if (stroud)
|
||||
{
|
||||
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-124
@@ -1110,36 +1110,20 @@ public:
|
||||
MixedDotProductIntegrator(VectorCoefficient &vq)
|
||||
: MixedScalarVectorIntegrator(vq, true) {}
|
||||
|
||||
inline bool VerifyFiniteElementTypes(
|
||||
inline virtual bool VerifyFiniteElementTypes(
|
||||
const FiniteElement & trial_fe,
|
||||
const FiniteElement & test_fe) const override
|
||||
const FiniteElement & test_fe) const
|
||||
{
|
||||
return (trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
|
||||
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
|
||||
}
|
||||
|
||||
inline const char * FiniteElementTypeFailureMessage() const override
|
||||
inline virtual const char * FiniteElementTypeFailureMessage() const
|
||||
{
|
||||
return "MixedDotProductIntegrator: "
|
||||
"Trial space must be a vector field "
|
||||
"and the test space must be a scalar field";
|
||||
}
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes) override;
|
||||
|
||||
void AddMultPA(const Vector&, Vector&) const override;
|
||||
void AddMultTransposePA(const Vector&, Vector&) const override;
|
||||
|
||||
private:
|
||||
Vector pa_data;
|
||||
const DofToQuad *mapsO = nullptr; ///< Not owned. Trial open map.
|
||||
const DofToQuad *mapsC = nullptr; ///< Not owned. Trial closed map.
|
||||
const DofToQuad *mapsTest = nullptr; ///< Not owned. Scalar test map.
|
||||
const GeometricFactors *geom = nullptr;///< Not owned.
|
||||
int dim = 0, ne = 0, dofs1D = 0, dofs1Dtest = 0, quad1D = 0;
|
||||
bool test_map_integral = false;
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\vec{V} \cdot u, \nabla \cdot v)$ in 2D or
|
||||
@@ -2200,22 +2184,11 @@ public:
|
||||
const Vector&, const Vector&,
|
||||
Vector&, const int, const int);
|
||||
|
||||
using ApplySimplexKernelType = void(*)(const int, const bool, const Array<int>&,
|
||||
const Array<int>&,
|
||||
const Array<int>&, const Array<int>&, const Array<int>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Vector&, const Vector&,
|
||||
Vector&, const int, const int);
|
||||
|
||||
using DiagonalKernelType = void(*)(const int, const bool, const Array<real_t>&,
|
||||
const Array<real_t>&, const Vector&, Vector&,
|
||||
const int, const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
|
||||
int));
|
||||
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
@@ -2368,8 +2341,7 @@ public:
|
||||
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const bool stroud = false);
|
||||
const FiniteElement &test_fe);
|
||||
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
@@ -2380,13 +2352,6 @@ public:
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
AddSimplexSpecialization<DIM,D1D,Q1D>();
|
||||
}
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
static void AddSimplexSpecialization()
|
||||
{
|
||||
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
}
|
||||
protected:
|
||||
const IntegrationRule* GetDefaultIntegrationRule(
|
||||
@@ -2423,22 +2388,11 @@ public:
|
||||
const Array<real_t>&, const Vector&,
|
||||
const Vector&, Vector&, const int, const int);
|
||||
|
||||
using ApplySimplexKernelType = void(*)(const int, const Array<int>&,
|
||||
const Array<int>&,
|
||||
const Array<int>&, const Array<int>&, const Array<int>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Array<real_t>&, const Array<real_t>&,
|
||||
const Vector&, const Vector&, Vector&,
|
||||
const int, const int);
|
||||
|
||||
using DiagonalKernelType = void(*)(const int, const Array<real_t>&,
|
||||
const Vector&, Vector&, const int,
|
||||
const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
|
||||
int));
|
||||
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
@@ -2487,8 +2441,7 @@ public:
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans,
|
||||
const bool stroud = false);
|
||||
const ElementTransformation &Trans);
|
||||
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
@@ -2499,13 +2452,6 @@ public:
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
AddSimplexSpecialization<DIM,D1D,Q1D>();
|
||||
}
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
static void AddSimplexSpecialization()
|
||||
{
|
||||
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -2764,7 +2710,7 @@ public:
|
||||
|
||||
|
||||
/** Integrator for $(-Q u, \nabla v)$ for Nedelec ($u$) and $H^1$ ($v$) elements.
|
||||
This is equivalent to a weak divergence of the $H(curl)$ basis functions. */
|
||||
This is equivalent to a weak divergence of the $H(curl$ basis functions. */
|
||||
class VectorFEWeakDivergenceIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
@@ -3011,10 +2957,11 @@ public:
|
||||
vector (diagonal matrix), or matrix), trial function $u$ is in $H(curl$ or
|
||||
$H(div)$, and test function $v$ is in $H(curl$, $H(div)$, or $v=(v_1,\dots,v_n)$, where
|
||||
$v_i$ are in $H^1$. */
|
||||
class VectorFEMassIntegrator : public BilinearFormIntegrator
|
||||
class VectorFEMassIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
void Init(Coefficient *q, DiagonalMatrixCoefficient *dq, MatrixCoefficient *mq);
|
||||
void Init(Coefficient *q, DiagonalMatrixCoefficient *dq, MatrixCoefficient *mq)
|
||||
{ Q = q; DQ = dq; MQ = mq; }
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector shape;
|
||||
@@ -3037,8 +2984,7 @@ protected:
|
||||
const DofToQuad *mapsOtest; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsCtest; ///< Not owned. DOF-to-quad map, closed.
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq, dofs1D, dofs1Dtest, quad1D;
|
||||
FiniteElement::DerivType trial_fetype, test_fetype;
|
||||
int dim, ne, nq, dofs1D, dofs1Dtest, quad1D, trial_fetype, test_fetype;
|
||||
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
|
||||
|
||||
public:
|
||||
@@ -3069,29 +3015,6 @@ public:
|
||||
const bool add) override;
|
||||
|
||||
const Coefficient *GetCoefficient() const { return Q; }
|
||||
|
||||
using ApplyKernelType =
|
||||
void (*)(const int NE, bool symmetric, const bool scalar_coeff,
|
||||
const Array<real_t> &trialBO, const Array<real_t> &trialBC,
|
||||
const Array<real_t> &testBOt, const Array<real_t> &testBCt,
|
||||
const Vector &pa_data, const Vector &x, Vector &y,
|
||||
const int triald1d, const int testd1d, const int q1d);
|
||||
|
||||
/// parameters: trial_fetype, test_fetype, ndims, trial_d1d, test_d1d, q1d
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType,
|
||||
(FiniteElement::DerivType, FiniteElement::DerivType,
|
||||
int, int, int, int));
|
||||
|
||||
struct Kernels { Kernels(); };
|
||||
|
||||
template <FiniteElement::DerivType TrialType,
|
||||
FiniteElement::DerivType TestType, int DIM, int TRIAL_D1D,
|
||||
int TEST_D1D, int Q1D>
|
||||
static void AddSpecialization()
|
||||
{
|
||||
ApplyPAKernels::Specialization<TrialType, TestType, DIM, TRIAL_D1D,
|
||||
TEST_D1D, Q1D>::Add();
|
||||
}
|
||||
};
|
||||
|
||||
/** Integrator for $(Q \nabla \cdot u, v)$ where $u=(u_1,\cdots,u_n)$ and all $u_i$ are in the same
|
||||
@@ -3945,7 +3868,7 @@ class DiscreteInterpolator : public BilinearFormIntegrator { };
|
||||
|
||||
|
||||
/** Class for constructing the gradient as a DiscreteLinearOperator from an
|
||||
$H^1$-conforming space to an $H(curl)$-conforming space. The range space can be
|
||||
$H^1$-conforming space to an $H(curl$-conforming space. The range space can be
|
||||
vector $L_2$ space as well. */
|
||||
class GradientInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
@@ -4054,48 +3977,12 @@ public:
|
||||
discrete curl matrix. */
|
||||
class CurlInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
int dim, ne;
|
||||
// "dof" are the domain fespace dof counts
|
||||
int ndof_o;
|
||||
// "quads" are the range fespace dof counts
|
||||
int nquad_o;
|
||||
|
||||
Vector pa_data;
|
||||
|
||||
public:
|
||||
CurlInterpolator();
|
||||
|
||||
void AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat) override
|
||||
{ ran_fe.ProjectCurl(dom_fe, Trans, elmat); }
|
||||
|
||||
void AssemblePA(const FiniteElementSpace &dom_fes,
|
||||
const FiniteElementSpace &ran_fes) override;
|
||||
void AssemblePA(const FiniteElementSpace &fes) override
|
||||
{
|
||||
AssemblePA(fes, fes);
|
||||
}
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
|
||||
using ApplyKernelType = void (*)(const int ne, const int ndof_o,
|
||||
const int nquad_o, const Vector &pa,
|
||||
const Vector &x, Vector &y);
|
||||
|
||||
/// arguments: DIM, ndof_o, nquad_o
|
||||
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
|
||||
/// arguments: DIM, ndof_o, nquad_o
|
||||
MFEM_REGISTER_KERNELS(ApplyTPAKernels, ApplyKernelType, (int, int, int));
|
||||
|
||||
template <int DIM, int NDOF_O, int NQUAD_O> static void AddSpecialization()
|
||||
{
|
||||
ApplyPAKernels::Specialization<DIM, NDOF_O, NQUAD_O>::Add();
|
||||
ApplyTPAKernels::Specialization<DIM, NDOF_O, NQUAD_O>::Add();
|
||||
}
|
||||
|
||||
struct Kernels { Kernels(); };
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -54,8 +54,6 @@ void Coefficient::Project(QuadratureFunction &qf)
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
Vector values;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -329,8 +327,6 @@ void VectorCoefficient::Project(QuadratureFunction &qf)
|
||||
const int ne = qspace.GetNE();
|
||||
DenseMatrix values;
|
||||
Vector col;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
@@ -699,8 +695,6 @@ void MatrixCoefficient::Project(QuadratureFunction &qf, bool transpose)
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
DenseMatrix values, matrix;
|
||||
// GetValues makes a reference, but we need it to be valid on Host
|
||||
qf.HostWrite();
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
{
|
||||
qf.GetValues(iel, values);
|
||||
|
||||
@@ -52,9 +52,6 @@ public:
|
||||
/// Get the time for time dependent coefficients
|
||||
real_t GetTime() { return time; }
|
||||
|
||||
/// Returns dimension of the vector.
|
||||
int GetVDim() { return 1; }
|
||||
|
||||
/** @brief Evaluate the coefficient in the element described by @a T at the
|
||||
point @a ip. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
|
||||
+26
-13
@@ -830,9 +830,15 @@ ParComplexGridFunction::ParComplexGridFunction(ParMesh *m, std::istream &input)
|
||||
int vsize = pfes->GetVSize();
|
||||
Vector::Load(input, 2*vsize);
|
||||
|
||||
real_t *h_data = HostReadWrite();
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
|
||||
@@ -1045,14 +1051,15 @@ void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
os << '\n';
|
||||
|
||||
int vsize = pfes->GetVSize();
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t *h_data = const_cast<real_t*>(HostRead());
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
|
||||
if (pfes->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
@@ -1063,8 +1070,14 @@ void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
Vector::Print(os, pfes->GetVDim());
|
||||
}
|
||||
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
|
||||
os.flush();
|
||||
}
|
||||
|
||||
@@ -114,10 +114,6 @@ void ConduitDataCollection::Save()
|
||||
n_mesh["fields"][name]);
|
||||
}
|
||||
|
||||
// TODO: in parallel, we need to call ParFiniteElementSpace::ApplyDofSigns
|
||||
// for all ParGridFunction objects before and after saving, see
|
||||
// ParGridFunction::Save.
|
||||
|
||||
// save mesh data
|
||||
SaveMeshAndFields(myid,
|
||||
n_mesh,
|
||||
|
||||
+5
-18
@@ -492,8 +492,6 @@ void VisItDataCollection::SaveRootFile()
|
||||
to_padded_string(cycle, pad_digits_cycle) +
|
||||
".mfem_root";
|
||||
std::ofstream root_file(root_name);
|
||||
MFEM_VERIFY(root_file.is_open(),
|
||||
"Failed to open ofstream " << root_name);
|
||||
root_file << GetVisItRootString();
|
||||
if (!root_file)
|
||||
{
|
||||
@@ -979,10 +977,7 @@ void ParaViewDataCollection::Save()
|
||||
// Save the local part of the mesh and grid functions fields to the local
|
||||
// VTU file. Also save coefficient fields.
|
||||
{
|
||||
std::string os_str = vtu_prefix + GenerateVTUFileName("proc", myid);
|
||||
std::ofstream os(os_str);
|
||||
MFEM_VERIFY(os.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
std::ofstream os(vtu_prefix + GenerateVTUFileName("proc", myid));
|
||||
os.precision(precision);
|
||||
SaveDataVTU(os, levels_of_detail);
|
||||
}
|
||||
@@ -994,10 +989,7 @@ void ParaViewDataCollection::Save()
|
||||
"QuadratureFunction output is not supported for "
|
||||
"ParaViewDataCollection on domain boundary!");
|
||||
const std::string &field_name = qfield.first;
|
||||
std::string os_str = vtu_prefix + GenerateVTUFileName(field_name, myid);
|
||||
std::ofstream os(os_str);
|
||||
MFEM_VERIFY(os.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
std::ofstream os(vtu_prefix + GenerateVTUFileName(field_name, myid));
|
||||
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel(), field_name);
|
||||
}
|
||||
|
||||
@@ -1008,10 +1000,7 @@ void ParaViewDataCollection::Save()
|
||||
{
|
||||
// Create the main PVTU file
|
||||
{
|
||||
std::string os_str = vtu_prefix + GeneratePVTUFileName("data");
|
||||
std::ofstream pvtu_out(os_str);
|
||||
MFEM_VERIFY(pvtu_out.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
std::ofstream pvtu_out(vtu_prefix + GeneratePVTUFileName("data"));
|
||||
WritePVTUHeader(pvtu_out);
|
||||
|
||||
// Grid function fields and coefficient fields
|
||||
@@ -1066,10 +1055,8 @@ void ParaViewDataCollection::Save()
|
||||
const std::string &q_field_name = q_field.first;
|
||||
std::string q_fname = GeneratePVTUPath() + "/"
|
||||
+ GeneratePVTUFileName(q_field_name);
|
||||
std::string os_str = col_path + "/" + q_fname;
|
||||
std::ofstream pvtu_out(os_str);
|
||||
MFEM_VERIFY(pvtu_out.is_open(),
|
||||
"Failed to open ofstream " << os_str);
|
||||
|
||||
std::ofstream pvtu_out(col_path + "/" + q_fname);
|
||||
WritePVTUHeader(pvtu_out);
|
||||
int vec_dim = q_field.second->GetVDim();
|
||||
pvtu_out << "<PPointData>\n";
|
||||
|
||||
@@ -57,7 +57,7 @@ void DGMassApply(const int e,
|
||||
}
|
||||
else if (DIM == 3)
|
||||
{
|
||||
SmemPAMassApply3D_Element<TD1D,TQ1D,NBZ,ACCUM>(e, NE, B, pa_data, x, y);
|
||||
SmemPAMassApply3D_Element<TD1D,TQ1D,ACCUM>(e, NE, B, pa_data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+6
-6
@@ -320,8 +320,8 @@ public:
|
||||
error estimation procedure where the flux averaging is replaced by a global
|
||||
L2 projection (requiring a mass matrix solve).
|
||||
|
||||
The required BilinearFormIntegrator must implement the method
|
||||
ComputeElementFlux().
|
||||
The required BilinearFormIntegrator must implement the methods
|
||||
ComputeElementFlux() and ComputeFluxEnergy().
|
||||
|
||||
Implemented for the parallel case only.
|
||||
*/
|
||||
@@ -357,8 +357,8 @@ protected:
|
||||
|
||||
public:
|
||||
/** @brief Construct a new L2ZienkiewiczZhuEstimator object.
|
||||
@param integ This BilinearFormIntegrator must implement the method
|
||||
ComputeElementFlux().
|
||||
@param integ This BilinearFormIntegrator must implement the methods
|
||||
ComputeElementFlux() and ComputeFluxEnergy().
|
||||
@param sol The solution field whose error is to be estimated.
|
||||
@param flux_fes The L2ZienkiewiczZhuEstimator assumes ownership of this
|
||||
FiniteElementSpace and will call its Update() method when
|
||||
@@ -382,8 +382,8 @@ public:
|
||||
{ }
|
||||
|
||||
/** @brief Construct a new L2ZienkiewiczZhuEstimator object.
|
||||
@param integ This BilinearFormIntegrator must implement the method
|
||||
ComputeElementFlux().
|
||||
@param integ This BilinearFormIntegrator must implement the methods
|
||||
ComputeElementFlux() and ComputeFluxEnergy().
|
||||
@param sol The solution field whose error is to be estimated.
|
||||
@param flux_fes The L2ZienkiewiczZhuEstimator does NOT assume ownership
|
||||
of this FiniteElementSpace; will call its Update() method
|
||||
|
||||
+4
-47
@@ -167,15 +167,7 @@ public:
|
||||
/** @brief Full multidimensional representation which does not use tensor
|
||||
product structure. The ordering of the degrees of freedom is the
|
||||
same as TENSOR, but the sizes of B and G are the same as FULL.*/
|
||||
LEXICOGRAPHIC_FULL,
|
||||
|
||||
/** @brief Ragged tensor product representation using 1D matrices/tensors
|
||||
with dimensions using 1D number of quadrature points and ragged tensor degrees of
|
||||
freedom. */
|
||||
/** Used only for partial assembly of the H1 positive basis. The
|
||||
size of B is d1d x qnpt x dim. Since different Gauss-Jacobi quadrature rules
|
||||
are employed in each dimension, we need to store dim arrays. */
|
||||
RAGGED_TENSOR
|
||||
LEXICOGRAPHIC_FULL
|
||||
};
|
||||
|
||||
/// Describes the contents of the #B, #Bt, #G, and #Gt arrays, see #Mode.
|
||||
@@ -236,39 +228,6 @@ public:
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode);
|
||||
|
||||
virtual ~DofToQuad() = default;
|
||||
};
|
||||
|
||||
/** @brief Structure representing the matrices/tensors needed to evaluate (in
|
||||
reference space) the values, gradients, divergences, or curls of a positive
|
||||
FiniteElement on simplices at the quadrature points of Stroud conical quadrature. */
|
||||
class RaggedDofToQuad : public DofToQuad
|
||||
{
|
||||
public:
|
||||
/** @brief Special basis function structures for positive (Bernstein) basis with
|
||||
partial assembly. The storage layout of Ba1 is ndof x nqpt for scalar elements.
|
||||
The storage layout of Ba2 is ndof x ndof x nqpt. In particular, we have
|
||||
Ba2(iqpt, a1, a2) = B^{p-a1}_{a2}(x_{iqpt}). */
|
||||
Array<real_t> Ba1, Ba2, Ba3;
|
||||
Array<real_t> Ba1t, Ba2t, Ba3t;
|
||||
|
||||
/** @brief Special structures for gradients of positive basis with partial assembly.
|
||||
The gradient arrays exploit properties of the Bernstein basis which allow grad(B^p_alpha)
|
||||
to be expressed as the sum of products of B^{p-1}_alpha and the barycentric coordinates.
|
||||
Thus, Ga1 and Ga2 simply contain the ragged tensor product components of B^{p-1}_alpha */
|
||||
Array<real_t> Ga1, Ga2, Ga3;
|
||||
Array<real_t> Ga1t, Ga2t, Ga3t;
|
||||
|
||||
/** @brief Mapping from the Bernstein multi-index (a_1, ..., a_d) to the lexicographic
|
||||
dof index. */
|
||||
Array<int> lex_map;
|
||||
|
||||
Array<int> forward_map2d_diff, forward_map3d_diff;
|
||||
Array<int> inverse_map2d_diff, inverse_map3d_diff;
|
||||
|
||||
Array<int> forward_map2d_mass, forward_map3d_mass;
|
||||
Array<int> inverse_map2d_mass, inverse_map3d_mass;
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
@@ -390,7 +349,7 @@ public:
|
||||
vector-valued finite elements, which is also the width of the
|
||||
DenseMatrix argument in
|
||||
CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape). */
|
||||
virtual int GetPhysRangeDim(int /* space_dim */) const { return vdim; }
|
||||
int GetPhysRangeDim(int /* space_dim */) const { return vdim; }
|
||||
|
||||
/** Returns the dimension of the curl for vector-valued finite elements,
|
||||
which is also the width of the DenseMatrix argument in
|
||||
@@ -401,7 +360,7 @@ public:
|
||||
finite elements, which is also the width of the DenseMatrix argument in
|
||||
CalcPhysCurlShape(ElementTransformation &Trans, DenseMatrix &curl_shape).
|
||||
*/
|
||||
virtual int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
|
||||
int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
|
||||
|
||||
/// Returns the Geometry::Type of the reference element.
|
||||
Geometry::Type GetGeomType() const { return geom_type; }
|
||||
@@ -1058,7 +1017,7 @@ public:
|
||||
VectorFiniteElement(int D, Geometry::Type G, int Do, int O, int M,
|
||||
int F = FunctionSpace::Pk);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const override { return space_dim; }
|
||||
int GetPhysRangeDim(int space_dim) const { return space_dim; }
|
||||
};
|
||||
|
||||
/// @brief Class for computing 1D special polynomials and their associated basis
|
||||
@@ -1446,8 +1405,6 @@ public:
|
||||
dof2quad_array_open);
|
||||
}
|
||||
|
||||
const Poly_1D::Basis &GetOpenBasis1D() const { return obasis1d; }
|
||||
|
||||
virtual ~VectorTensorFiniteElement();
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -663,8 +663,8 @@ public:
|
||||
const int cb_type = BasisType::GaussLobatto,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const override { return 2; }
|
||||
int GetPhysCurlDim(int space_dim) const override { return 1; }
|
||||
int GetPhysRangeDim(int space_dim) const { return 2; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 1; }
|
||||
|
||||
void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const override;
|
||||
@@ -708,8 +708,8 @@ private:
|
||||
DenseMatrix &I) const;
|
||||
|
||||
public:
|
||||
int GetPhysRangeDim(int space_dim) const override { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const override { return 3; }
|
||||
int GetPhysRangeDim(int space_dim) const { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 3; }
|
||||
|
||||
using FiniteElement::CalcVShape;
|
||||
using FiniteElement::CalcPhysCurlShape;
|
||||
|
||||
@@ -557,101 +557,6 @@ H1Pos_TriangleElement::H1Pos_TriangleElement(const int p)
|
||||
}
|
||||
}
|
||||
|
||||
const DofToQuad &H1Pos_TriangleElement::GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array)
|
||||
{
|
||||
DofToQuad *d2q = nullptr;
|
||||
MFEM_VERIFY(mode == DofToQuad::RAGGED_TENSOR, "invalid mode requested");
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new RaggedDofToQuad;
|
||||
const int ndof = fe.GetOrder() + 1; // verify
|
||||
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
|
||||
d2q->FE = &fe;
|
||||
d2q->IntRule = &ir;
|
||||
d2q->mode = mode;
|
||||
d2q->ndof = ndof;
|
||||
d2q->nqpt = nqpt;
|
||||
|
||||
RaggedDofToQuad *rd2q = static_cast<RaggedDofToQuad*>(d2q);
|
||||
rd2q->Ba1.SetSize(nqpt*ndof);
|
||||
// second component of ragged tensor basis, technically dof*(dof-1)/2 entries
|
||||
rd2q->Ba2.SetSize((int)nqpt*ndof*ndof);
|
||||
rd2q->Ba1t.SetSize(nqpt*ndof);
|
||||
rd2q->Ba2t.SetSize((int)nqpt*ndof*ndof);
|
||||
// stores first component of ragged tensor basis with order p-1, for gradients only
|
||||
rd2q->Ga1.SetSize(nqpt*(ndof -1));
|
||||
// stores second component of ragged tensor basis with order p-1
|
||||
rd2q->Ga2.SetSize(nqpt*(ndof-1)*(ndof -1));
|
||||
rd2q->Ga1t.SetSize(nqpt*(ndof -1));
|
||||
rd2q->Ga2t.SetSize(nqpt*(ndof-1)*(ndof -1));
|
||||
rd2q->lex_map.SetSize(ndof * ndof);
|
||||
Vector shape_a1(ndof), shape_a2(ndof * ndof);
|
||||
Vector shape_Ga1(ndof-1), shape_Ga2((ndof-1) * (ndof-1));
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
// The first 'nqpt' points in the first dimension 'ir' have the same x-coordinates as those
|
||||
// of the 1D rule (ie. (2,0) Gauss-Jacobi rule). The first 'nqpt' points in the second dimension
|
||||
// 'ir' have the same y-coordinates as those of the 1D rule for second dimension (i.e. (1,0)
|
||||
// Gauss-Jacobi rule). Additionally, the Bernstein PA algorithms expect evaluation of the
|
||||
// component 1D bases at the Stroud nodes pulled back to the unit square, so perform the pullback
|
||||
// on the fly.
|
||||
const real_t x = ir.IntPoint(i).x;
|
||||
const real_t y = ir.IntPoint(nqpt*i).y / (1.0 - ir.IntPoint(nqpt*i).x);
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1, x, shape_a1);
|
||||
Poly_1D::CalcBernstein(ndof-2, x, shape_Ga1);
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
rd2q->Ba1t[i+nqpt*j] = rd2q->Ba1[j+ndof*i] = shape_a1(j);
|
||||
if (j < ndof-1)
|
||||
{
|
||||
rd2q->Ga1t[i+nqpt*j] = rd2q->Ga1[j+(ndof-1)*i] = shape_Ga1(j);
|
||||
Poly_1D::CalcBernstein(ndof-2-j, y, shape_Ga2);
|
||||
}
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1-j, y, shape_a2);
|
||||
for (int k = 0; k < ndof-j; k++)
|
||||
{
|
||||
rd2q->Ba2t[i + nqpt*(j + ndof*k)] = rd2q->Ba2[k + ndof*(j + ndof*i)] = shape_a2(
|
||||
k);
|
||||
if (j < ndof-1 && k < ndof-j-1)
|
||||
{
|
||||
rd2q->Ga2t[i + nqpt*(j + (ndof-1)*k)] = rd2q->Ga2[k + (ndof-1)*(j +
|
||||
(ndof-1)*i)] = shape_Ga2(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stores the mapping from 2D Bernstein multi-index (i,j,p-i-j) to the
|
||||
// lexicographic DOF ordering
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof-i; j++)
|
||||
{
|
||||
int idx = ((2 * (ndof-1) + 3) - j) * j / 2 + i;
|
||||
rd2q->lex_map[j + ndof*i] = idx;
|
||||
}
|
||||
}
|
||||
dof2quad_array.Append(d2q);
|
||||
}
|
||||
}
|
||||
return *d2q;
|
||||
}
|
||||
|
||||
// static method
|
||||
void H1Pos_TriangleElement::CalcShape(
|
||||
const int p, const real_t l1, const real_t l2, real_t *shape)
|
||||
@@ -844,213 +749,6 @@ H1Pos_TetrahedronElement::H1Pos_TetrahedronElement(const int p)
|
||||
}
|
||||
}
|
||||
|
||||
const DofToQuad &H1Pos_TetrahedronElement::GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array)
|
||||
{
|
||||
DofToQuad *d2q = nullptr;
|
||||
MFEM_VERIFY(mode == DofToQuad::RAGGED_TENSOR, "invalid mode requested");
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new RaggedDofToQuad;
|
||||
const int ndof = fe.GetOrder() + 1; // verify
|
||||
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
|
||||
const int basis_dim2d = ndof*(ndof+1) / 2;
|
||||
const int basis_dim3d = ndof*(ndof+1)*(ndof+2) / 6;
|
||||
const int basis_dim2d_diff = (ndof-1)*(ndof) / 2;
|
||||
const int basis_dim3d_diff = (ndof-1)*(ndof)*(ndof+1) / 6;
|
||||
d2q->FE = &fe;
|
||||
d2q->IntRule = &ir;
|
||||
d2q->mode = mode;
|
||||
d2q->ndof = ndof;
|
||||
d2q->nqpt = nqpt;
|
||||
|
||||
RaggedDofToQuad *rd2q = static_cast<RaggedDofToQuad*>(d2q);
|
||||
rd2q->Ba1.SetSize(nqpt * ndof);
|
||||
// second component of ragged tensor basis, technically dof*(dof-1)/2 entries
|
||||
rd2q->Ba2.SetSize(nqpt * basis_dim2d);
|
||||
// third component of ragged tensor basis, technically dof*(dof-1)/2 entries
|
||||
rd2q->Ba3.SetSize(nqpt * basis_dim3d);
|
||||
rd2q->Ba1t.SetSize(nqpt * ndof);
|
||||
rd2q->Ba2t.SetSize(nqpt * basis_dim2d);
|
||||
rd2q->Ba3t.SetSize(nqpt * basis_dim3d);
|
||||
// stores first component of ragged tensor basis with order p-1, for gradients only
|
||||
rd2q->Ga1.SetSize(nqpt * (ndof-1));
|
||||
// stores second component of ragged tensor basis with order p-1
|
||||
rd2q->Ga2.SetSize(nqpt * basis_dim2d_diff);
|
||||
// stores third component of ragged tensor basis with order p-1
|
||||
rd2q->Ga3.SetSize(nqpt * basis_dim3d_diff);
|
||||
rd2q->Ga1t.SetSize(nqpt * (ndof-1));
|
||||
rd2q->Ga2t.SetSize(nqpt * basis_dim2d_diff);
|
||||
rd2q->Ga3t.SetSize(nqpt * basis_dim3d_diff);
|
||||
rd2q->lex_map.SetSize(ndof * ndof * ndof);
|
||||
|
||||
rd2q->forward_map2d_diff.SetSize((ndof-1) * (ndof-1));
|
||||
rd2q->forward_map3d_diff.SetSize((ndof-1) * (ndof-1) * (ndof-1));
|
||||
rd2q->inverse_map2d_diff.SetSize(2 * basis_dim2d_diff);
|
||||
rd2q->inverse_map3d_diff.SetSize(3 * basis_dim3d_diff);
|
||||
|
||||
rd2q->forward_map2d_mass.SetSize(ndof * ndof);
|
||||
rd2q->forward_map3d_mass.SetSize(ndof * ndof * ndof);
|
||||
rd2q->inverse_map2d_mass.SetSize(2 * basis_dim2d);
|
||||
rd2q->inverse_map3d_mass.SetSize(2 * basis_dim3d);
|
||||
|
||||
// forward and inverse maps for multi-index to collpased 1d index for diffusion, can combine
|
||||
// these four loops, but need four idx's and clause for shorter diff loops
|
||||
int idx = 0;
|
||||
for (int i = 0; i < ndof-1; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof-i-1; j++)
|
||||
{
|
||||
rd2q->forward_map2d_diff[j + (ndof-1)*i] = idx;
|
||||
rd2q->inverse_map2d_diff[2*idx] = i;
|
||||
rd2q->inverse_map2d_diff[1 + 2*idx] = j;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
for (int k = 0; k < ndof-1; k++)
|
||||
{
|
||||
for (int j = 0; j < ndof-k-1; j++)
|
||||
{
|
||||
for (int i = 0; i < ndof-k-j-1; i++)
|
||||
{
|
||||
rd2q->forward_map3d_diff[k + (ndof-1)*(j + (ndof-1)*i)] = idx;
|
||||
rd2q->inverse_map3d_diff[3*idx] = i;
|
||||
rd2q->inverse_map3d_diff[1 + 3*idx] = j;
|
||||
rd2q->inverse_map3d_diff[2 + 3*idx] = k;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// forward and inverse maps for multi-index to collpased 1d index for mass
|
||||
idx = 0;
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
for (int i = 0; i < ndof-j; i++)
|
||||
{
|
||||
rd2q->forward_map2d_mass[j + ndof*i] = idx;
|
||||
rd2q->inverse_map2d_mass[2*idx] = i;
|
||||
rd2q->inverse_map2d_mass[1 + 2*idx] = j;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
for (int k = 0; k < ndof; k++)
|
||||
{
|
||||
for (int j = 0; j < ndof-k; j++)
|
||||
{
|
||||
for (int i = 0; i < ndof-k-j; i++)
|
||||
{
|
||||
rd2q->forward_map3d_mass[k + ndof*(j + ndof*i)] = idx;
|
||||
rd2q->inverse_map3d_mass[2*idx] = i;
|
||||
rd2q->inverse_map3d_mass[1 + 2*idx] = j;
|
||||
// d2q->inverse_map3d_mass[2 + 3*idx] = k;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector shape_a1(ndof), shape_a2(ndof * ndof), shape_a3(ndof * ndof * ndof);
|
||||
Vector shape_Ga1(ndof-1), shape_Ga2(ndof-1), shape_Ga3(ndof-1);
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
// The first 'nqpt' points in the first dimension 'ir' have the same x-coordinates as those
|
||||
// of the 1D rule (ie. (2,0) Gauss-Jacobi rule). The first 'nqpt' points in the second dimension
|
||||
// 'ir' have the same y-coordinates as those of the 1D rule for second dimension (i.e. (1,0)
|
||||
// Gauss-Jacobi rule). The first 'nqpt' points in the third dimension have the same z-coordinates
|
||||
// as those of the 1D rule for the third dimension (i.e. Gauss-Legendre rule). Additionally,
|
||||
// the Bernstein PA algorithms expect evaluation of the component 1D bases at the Stroud nodes
|
||||
// pulled back to the unit cube, so perform the pullback on the fly.
|
||||
const real_t x = ir.IntPoint(i).x;
|
||||
const real_t y = ir.IntPoint(nqpt*i).y / (1.0 - ir.IntPoint(nqpt*i).x);
|
||||
const real_t z = ir.IntPoint(nqpt*nqpt*i).z / (1.0 - ir.IntPoint(
|
||||
nqpt*nqpt*i).x - ir.IntPoint(nqpt*nqpt*i).y);
|
||||
Poly_1D::CalcBernstein(ndof-1, x, shape_a1);
|
||||
Poly_1D::CalcBernstein(ndof-2, x, shape_Ga1);
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
rd2q->Ba1t[i+nqpt*j] = rd2q->Ba1[j+ndof*i] = shape_a1(j);
|
||||
if (j < ndof-1)
|
||||
{
|
||||
rd2q->Ga1t[i+nqpt*j] = rd2q->Ga1[j+(ndof-1)*i] = shape_Ga1(j);
|
||||
Poly_1D::CalcBernstein(ndof-2-j, y, shape_Ga2);
|
||||
}
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1-j, y, shape_a2);
|
||||
for (int k = 0; k < ndof-j; k++)
|
||||
{
|
||||
const int a_2d_mass = rd2q->forward_map2d_mass[k + ndof*j];
|
||||
rd2q->Ba2t[i + nqpt*a_2d_mass] = rd2q->Ba2[a_2d_mass + basis_dim2d*i] =
|
||||
shape_a2(
|
||||
k);
|
||||
if (j < ndof-1 && k < ndof-j-1)
|
||||
{
|
||||
const int a_2d_diff = rd2q->forward_map2d_diff[k + (ndof-1)*j];
|
||||
rd2q->Ga2t[i + nqpt*a_2d_diff] = rd2q->Ga2[a_2d_diff + basis_dim2d_diff*i] =
|
||||
shape_Ga2(k);
|
||||
Poly_1D::CalcBernstein(ndof-2-j-k, z, shape_Ga3);
|
||||
}
|
||||
|
||||
Poly_1D::CalcBernstein(ndof-1-j-k, z, shape_a3);
|
||||
for (int m = 0; m < ndof-j-k; m++)
|
||||
{
|
||||
const int a_3d_mass = rd2q->forward_map3d_mass[m + ndof*(k + ndof*j)];
|
||||
rd2q->Ba3t[i + nqpt*a_3d_mass] = rd2q->Ba3[a_3d_mass + basis_dim3d*i] =
|
||||
shape_a3(
|
||||
m);
|
||||
if (j < ndof-1 && k < ndof-j-1 && m < ndof-j-k-1)
|
||||
{
|
||||
// // collapsed 1D access
|
||||
// d2q->Ga3[i + nqpt*(m + d2q->offset3d[k + (ndof-1)*j])] = shape_Ga3(m);
|
||||
// collapsed 1D access with forward mapping
|
||||
const int a_3d_diff = rd2q->forward_map3d_diff[m + (ndof-1)*(k + (ndof-1)*j)];
|
||||
rd2q->Ga3t[i + nqpt*a_3d_diff] = rd2q->Ga3[a_3d_diff + basis_dim3d_diff*i] =
|
||||
shape_Ga3(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stores the mapping from 3D Bernstein multi-index (i,j,k,p-i-j-k) to the
|
||||
// lexicographic DOF ordering
|
||||
int p = ndof - 1;
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof-i; j++)
|
||||
{
|
||||
for (int k = 0; k < ndof-i-j; k++)
|
||||
{
|
||||
int dof = (p+1)*(p+2)*(p+3) / 6;
|
||||
int tet = (p-k)*(p-k+1)*(p-k+2) / 6;
|
||||
int tri = (p+1-k-j)*(p+2-k-j)/2;
|
||||
int multi_idx = dof - tet - tri + i;
|
||||
rd2q->lex_map[k + ndof*(j + ndof*i)] = multi_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q);
|
||||
}
|
||||
}
|
||||
return *d2q;
|
||||
}
|
||||
|
||||
// static method
|
||||
void H1Pos_TetrahedronElement::CalcShape(
|
||||
const int p, const real_t l1, const real_t l2, const real_t l3,
|
||||
|
||||
@@ -191,21 +191,6 @@ public:
|
||||
/// Construct the H1Pos_TriangleElement of order @a p
|
||||
H1Pos_TriangleElement(const int p);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const override
|
||||
{
|
||||
return (mode == DofToQuad::RAGGED_TENSOR) ?
|
||||
GetRaggedTensorDofToQuad(*this, ir, mode, dof2quad_array) :
|
||||
FiniteElement::GetDofToQuad(ir, mode);
|
||||
}
|
||||
|
||||
static const DofToQuad &GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array);
|
||||
|
||||
const Array<int> &GetDofMap() const { return dof_map; }
|
||||
|
||||
// The size of shape is (p+1)(p+2)/2 (dof).
|
||||
static void CalcShape(const int p, const real_t x, const real_t y,
|
||||
real_t *shape);
|
||||
@@ -235,21 +220,6 @@ public:
|
||||
/// Construct the H1Pos_TetrahedronElement of order @a p
|
||||
H1Pos_TetrahedronElement(const int p);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const override
|
||||
{
|
||||
return (mode == DofToQuad::RAGGED_TENSOR) ?
|
||||
GetRaggedTensorDofToQuad(*this, ir, mode, dof2quad_array) :
|
||||
FiniteElement::GetDofToQuad(ir, mode);
|
||||
}
|
||||
|
||||
static const DofToQuad &GetRaggedTensorDofToQuad(
|
||||
const FiniteElement &fe, const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode,
|
||||
Array<DofToQuad*> &dof2quad_array);
|
||||
|
||||
const Array<int> &GetDofMap() const { return dof_map; }
|
||||
|
||||
// The size of shape is (p+1)(p+2)(p+3)/6 (dof).
|
||||
static void CalcShape(const int p, const real_t x, const real_t y,
|
||||
const real_t z, real_t *shape);
|
||||
|
||||
+4
-4
@@ -510,8 +510,8 @@ public:
|
||||
RT_R2D_SegmentElement(const int p,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const override { return 2; }
|
||||
int GetPhysCurlDim(int space_dim) const override { return 0; }
|
||||
int GetPhysRangeDim(int space_dim) const { return 2; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 0; }
|
||||
|
||||
void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const override;
|
||||
@@ -550,8 +550,8 @@ private:
|
||||
DenseMatrix &I) const;
|
||||
|
||||
public:
|
||||
int GetPhysRangeDim(int space_dim) const override { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const override { return 0; }
|
||||
int GetPhysRangeDim(int space_dim) const { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 0; }
|
||||
|
||||
using FiniteElement::CalcVShape;
|
||||
|
||||
|
||||
@@ -250,14 +250,6 @@ public:
|
||||
its GetOrder() method. */
|
||||
virtual FiniteElementCollection *Clone(int p) const;
|
||||
|
||||
/** @brief Return the order parameter used to construct this collection.
|
||||
* This differs from GetOrder() depending on the collection type. */
|
||||
virtual int GetConstructorOrder() const
|
||||
{
|
||||
MFEM_ABORT("Collection " << Name() << " does not support GetConstructorOrder");
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected:
|
||||
const int base_p; ///< Order as returned by GetOrder().
|
||||
|
||||
@@ -322,9 +314,6 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new H1_FECollection(p, dim, b_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return base_p; }
|
||||
|
||||
virtual ~H1_FECollection();
|
||||
};
|
||||
|
||||
@@ -354,10 +343,6 @@ class H1_Trace_FECollection : public H1_FECollection
|
||||
public:
|
||||
H1_Trace_FECollection(const int p, const int dim,
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new H1_Trace_FECollection(p, dim+1, b_type); }
|
||||
|
||||
};
|
||||
|
||||
/// Arbitrary order "L2-conforming" discontinuous finite elements.
|
||||
@@ -411,9 +396,6 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new L2_FECollection(p, dim, b_type, m_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return base_p; }
|
||||
|
||||
virtual ~L2_FECollection();
|
||||
};
|
||||
|
||||
@@ -474,9 +456,6 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new RT_FECollection(p, dim, cb_type, ob_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return base_p-1; }
|
||||
|
||||
virtual ~RT_FECollection();
|
||||
};
|
||||
|
||||
@@ -557,9 +536,6 @@ public:
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new ND_FECollection(p, dim, cb_type, ob_type); }
|
||||
|
||||
int GetConstructorOrder() const override
|
||||
{ return dim>1 ? base_p : base_p+1; }
|
||||
|
||||
virtual ~ND_FECollection();
|
||||
};
|
||||
|
||||
@@ -572,9 +548,6 @@ public:
|
||||
ND_Trace_FECollection(const int p, const int dim,
|
||||
const int cb_type = BasisType::GaussLobatto,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new ND_Trace_FECollection(p, dim+1, cb_type, ob_type); }
|
||||
};
|
||||
|
||||
/// Arbitrary order 3D H(curl)-conforming Nedelec finite elements in 1D.
|
||||
|
||||
+2
-16
@@ -3934,16 +3934,6 @@ const FiniteElement *FiniteElementSpace::GetBE(int i) const
|
||||
return BE;
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetTypicalBE() const
|
||||
{
|
||||
if (mesh->GetNBE() > 0) { return GetBE(0); }
|
||||
|
||||
Geometry::Type geom = mesh->GetTypicalFaceGeometry();
|
||||
const FiniteElement *be = fec->FiniteElementForGeometry(geom);
|
||||
MFEM_VERIFY(be != nullptr, "Could not determine a typical BE!");
|
||||
return be;
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetFaceElement(int i) const
|
||||
{
|
||||
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
|
||||
@@ -3974,11 +3964,6 @@ const FiniteElement *FiniteElementSpace::GetFaceElement(int i) const
|
||||
return fe;
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetTypicalFaceElement() const
|
||||
{
|
||||
return fec->FiniteElementForGeometry(mesh->GetTypicalFaceGeometry());
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetEdgeElement(int i,
|
||||
int variant) const
|
||||
{
|
||||
@@ -4631,8 +4616,9 @@ FiniteElementCollection *FiniteElementSpace::Load(Mesh *m, std::istream &input)
|
||||
|
||||
ElementDofOrdering GetEVectorOrdering(const FiniteElementSpace& fes)
|
||||
{
|
||||
return (UsesTensorBasis(fes) || fes.UsesRaggedTensorBasis()) ?
|
||||
return UsesTensorBasis(fes)?
|
||||
ElementDofOrdering::LEXICOGRAPHIC:
|
||||
ElementDofOrdering::NATIVE;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+1
-25
@@ -839,7 +839,7 @@ public:
|
||||
Note: For vector-valued elements, the results pads up the range dimension
|
||||
to the spatial dimension. E.g., consider a stack of 5 vector-valued
|
||||
elements each representing 2D vectors, living in a 3 dimensional space.
|
||||
Then this function would give 15, not 10.
|
||||
Then this fucntion would give 15, not 10.
|
||||
*/
|
||||
int GetVectorDim() const;
|
||||
|
||||
@@ -1323,24 +1323,12 @@ public:
|
||||
associated with i'th boundary face in the mesh object. */
|
||||
const FiniteElement *GetBE(int i) const;
|
||||
|
||||
/// @brief Return a "typical" boundary element.
|
||||
///
|
||||
/// This can be used in situations where the local mesh partition may be
|
||||
/// empty.
|
||||
const FiniteElement *GetTypicalBE() const;
|
||||
|
||||
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th face in the mesh object. Faces in this case refer
|
||||
to the MESHDIM-1 primitive so in 2D they are segments and in 1D they are
|
||||
points.*/
|
||||
const FiniteElement *GetFaceElement(int i) const;
|
||||
|
||||
/// @brief Return a "typical" face element.
|
||||
///
|
||||
/// This can be used in situations where the local mesh partition may be
|
||||
/// empty.
|
||||
const FiniteElement *GetTypicalFaceElement() const;
|
||||
|
||||
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th edge in the mesh object. */
|
||||
const FiniteElement *GetEdgeElement(int i, int variant = 0) const;
|
||||
@@ -1514,18 +1502,6 @@ public:
|
||||
return dynamic_cast<const L2_FECollection*>(fec) != NULL;
|
||||
}
|
||||
|
||||
/// @brief Return true if the mesh contains only one topology, the elements are
|
||||
/// all triangles or tetrahedrons, and the elements are ragged tensor elements
|
||||
/// i.e. Bernstein/positive basis.
|
||||
bool UsesRaggedTensorBasis() const
|
||||
{
|
||||
bool simplex = this->GetMesh()->IsSimplexMesh();
|
||||
bool positive =
|
||||
dynamic_cast<const mfem::H1Pos_TriangleElement *>(this->GetTypicalFE()) ||
|
||||
dynamic_cast<const mfem::H1Pos_TetrahedronElement *>(this->GetTypicalFE());
|
||||
return simplex && positive;
|
||||
}
|
||||
|
||||
/** In variable-order spaces on nonconforming (NC) meshes, this function
|
||||
controls whether strict conformity is enforced in cases where coarse
|
||||
edges/faces have higher polynomial order than their fine NC neighbors.
|
||||
|
||||
+75
-252
@@ -345,6 +345,27 @@ void GridFunction::ComputeFlux(BilinearFormIntegrator &blfi,
|
||||
}
|
||||
}
|
||||
|
||||
int GridFunction::VectorDim() const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return fes->GetVDim();
|
||||
}
|
||||
return fes->GetVDim()*std::max(fes->GetMesh()->SpaceDimension(),
|
||||
fe->GetRangeDim());
|
||||
}
|
||||
|
||||
int GridFunction::CurlDim() const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return 2 * fes->GetMesh()->SpaceDimension() - 3;
|
||||
}
|
||||
return fes->GetVDim()*fe->GetCurlDim();
|
||||
}
|
||||
|
||||
void GridFunction::GetTrueDofs(Vector &tv) const
|
||||
{
|
||||
const SparseMatrix *R = fes->GetRestrictionMatrix();
|
||||
@@ -2029,18 +2050,6 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
Coefficient *coeff[], VectorCoefficient *vcoeff, const Array<int> &attr,
|
||||
Array<int> &values_counter)
|
||||
{
|
||||
if (vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == vcoeff->GetVDim(),
|
||||
"vcoeff vdim != fes VDim");
|
||||
MFEM_VERIFY(fes->GetTypicalBE()->GetMapType() == FiniteElement::VALUE &&
|
||||
fes->GetTypicalBE()->GetRangeType() ==
|
||||
FiniteElement::SCALAR,
|
||||
"Can only call ProjectBdrCoefficient on scalar value-type "
|
||||
"boundary elements. "
|
||||
"Did you intended to call ProjectBdrCoefficientNormal or "
|
||||
"ProjectBdrCoefficientTangent for vector finite elements?");
|
||||
}
|
||||
Array<int> vdofs;
|
||||
Vector vc;
|
||||
|
||||
@@ -2193,9 +2202,6 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
VectorCoefficient &vcoeff, const Array<int> &bdr_attr,
|
||||
Array<int> &values_counter)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetTypicalBE()->GetPhysRangeDim(
|
||||
fes->GetMesh()->SpaceDimension()) == vcoeff.GetVDim(),
|
||||
"vcoeff vdim != PhysRangeDim");
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
@@ -2256,104 +2262,6 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::AccumulateAndCountTraceValues(
|
||||
Coefficient *coeff[], VectorCoefficient *vcoeff,
|
||||
Array<int> &values_counter)
|
||||
{
|
||||
if (vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == vcoeff->GetVDim(),
|
||||
"vcoeff vdim != fes VDim");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetMapType() ==
|
||||
FiniteElement::VALUE &&
|
||||
fes->GetTypicalTraceElement()->GetRangeType() ==
|
||||
FiniteElement::SCALAR,
|
||||
"Can only call ProjectTraceCoefficient on scalar value-type "
|
||||
"trace elements. "
|
||||
"Use ProjectTraceCoefficientNormal for RT and "
|
||||
"ProjectTraceCoefficientTangent for ND finite elements.");
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
Vector vc;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
|
||||
const int vdim = fes->GetVDim();
|
||||
HostReadWrite();
|
||||
|
||||
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
|
||||
{
|
||||
|
||||
const FiniteElement *fe = fes->GetFaceElement(i);
|
||||
const int fdof = fe->GetDof();
|
||||
ElementTransformation *transf = fes->GetMesh()->GetFaceTransformation(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
fes->GetFaceVDofs(i, vdofs);
|
||||
|
||||
for (int j = 0; j < fdof; j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
transf->SetIntPoint(&ip);
|
||||
if (vcoeff) { vcoeff->Eval(vc, *transf, ip); }
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
if (!vcoeff && !coeff[d]) { continue; }
|
||||
|
||||
real_t val = vcoeff ? vc(d) : coeff[d]->Eval(*transf, ip);
|
||||
int ind = vdofs[fdof*d+j];
|
||||
if ( ind < 0 )
|
||||
{
|
||||
val = -val, ind = -1-ind;
|
||||
}
|
||||
if (++values_counter[ind] == 1)
|
||||
{
|
||||
(*this)(ind) = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*this)(ind) += val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::AccumulateAndCountTraceTangentValues(
|
||||
VectorCoefficient &vcoeff, Array<int> &values_counter)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()
|
||||
->GetRangeType() == FiniteElement::VECTOR &&
|
||||
fes->GetTypicalTraceElement()
|
||||
->GetMapType() == FiniteElement::H_CURL,
|
||||
"Not an ND FE space!");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetPhysRangeDim(
|
||||
fes->GetMesh()->SpaceDimension()) == vcoeff.GetVDim(),
|
||||
"vcoeff vdim != PhysRangeDim");
|
||||
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector lvec;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
|
||||
HostReadWrite();
|
||||
|
||||
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
|
||||
{
|
||||
fe = fes->GetFaceElement(i);
|
||||
T = fes->GetMesh()->GetFaceTransformation(i);
|
||||
fes->GetFaceVDofs(i, dofs);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ComputeMeans(AvgType type, Array<int> &zones_per_vdof)
|
||||
{
|
||||
switch (type)
|
||||
@@ -2447,9 +2355,6 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
{
|
||||
MFEM_VERIFY(
|
||||
VectorDim() == 1,
|
||||
"Cannot project scalar Coefficient onto vector GridFunction");
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
@@ -2725,7 +2630,6 @@ void GridFunction::ProjectCoefficient(
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type)
|
||||
{
|
||||
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
DofTransformation doftrans;
|
||||
@@ -2796,74 +2700,6 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficient(Coefficient *coeff[])
|
||||
{
|
||||
Array<int> values_counter;
|
||||
AccumulateAndCountTraceValues(coeff, NULL, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficient(Coefficient &coeff)
|
||||
{
|
||||
MFEM_VERIFY(FESpace()->GetVDim() == 1, "ProjectTraceCoefficient(Coefficient&)"
|
||||
"is only valid for scalar GridFunction");
|
||||
Coefficient *coeff_p = &coeff;
|
||||
ProjectTraceCoefficient(&coeff_p);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficient(VectorCoefficient &vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(FESpace()->GetVDim() == vcoeff.GetVDim(),
|
||||
"Incompatible vcoeff vdim and fes vdim");
|
||||
Array<int> values_counter;
|
||||
AccumulateAndCountTraceValues(NULL, &vcoeff, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
|
||||
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetRangeType() ==
|
||||
FiniteElement::SCALAR &&
|
||||
fes->GetTypicalTraceElement()->GetMapType() ==
|
||||
FiniteElement::INTEGRAL, "Not an RT FE space!");
|
||||
MFEM_VERIFY(vcoeff.GetVDim() == fes->GetMesh()->SpaceDimension(),
|
||||
"vcoeff vdim (" << vcoeff.GetVDim()
|
||||
<< ") != SpaceDimension ("
|
||||
<< fes->GetMesh()->SpaceDimension() << ")");
|
||||
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
|
||||
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
|
||||
{
|
||||
fe = fes->GetFaceElement(i);
|
||||
T = fes->GetMesh()->GetFaceTransformation(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
lvec.SetSize(fe->GetDof());
|
||||
for (int j = 0; j < ir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
fes->GetFaceVDofs(i, dofs);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff)
|
||||
{
|
||||
Array<int> values_counter;
|
||||
AccumulateAndCountTraceTangentValues(vcoeff, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol, int iter)
|
||||
{
|
||||
@@ -3109,7 +2945,6 @@ void GridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
|
||||
void GridFunction::ProjectCoefficient(
|
||||
VectorCoefficient &vcoeff, Array<int> &dofs)
|
||||
{
|
||||
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
|
||||
int el = -1;
|
||||
ElementTransformation *T = NULL;
|
||||
const FiniteElement *fe = NULL;
|
||||
@@ -3139,7 +2974,6 @@ void GridFunction::ProjectCoefficient(
|
||||
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
{
|
||||
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
@@ -3196,14 +3030,9 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectDiscCoefficient(
|
||||
std::variant<Coefficient*, VectorCoefficient*> coeff, Array<int> &dof_attr)
|
||||
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
|
||||
Array<int> &dof_attr)
|
||||
{
|
||||
std::visit([&](auto* c)
|
||||
{
|
||||
MFEM_VERIFY(VectorDim() == c->GetVDim(), "coeff vdim != VectorDim()");
|
||||
}, coeff);
|
||||
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
@@ -3217,10 +3046,7 @@ void GridFunction::ProjectDiscCoefficient(
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
vals.SetSize(vdofs.Size());
|
||||
std::visit([&](auto* c)
|
||||
{
|
||||
fes->GetFE(i)->Project(*c, *fes->GetElementTransformation(i), vals);
|
||||
}, coeff);
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
|
||||
// the values in shared dofs are determined from the element with maximal
|
||||
// attribute
|
||||
@@ -3236,15 +3062,17 @@ void GridFunction::ProjectDiscCoefficient(
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
|
||||
{
|
||||
Array<int> dof_attr;
|
||||
ProjectDiscCoefficient(coeff, dof_attr);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
|
||||
{
|
||||
// Harmonic (x1 ... xn) = [ (1/x1 + ... + 1/xn) / n ]^-1.
|
||||
// Arithmetic(x1 ... xn) = (x1 + ... + xn) / n.
|
||||
|
||||
MFEM_VERIFY(
|
||||
VectorDim() == 1,
|
||||
"Cannot project a scalar coefficient onto a vector GridFunction");
|
||||
|
||||
Array<int> zones_per_vdof;
|
||||
AccumulateAndCountZones(coeff, type, zones_per_vdof);
|
||||
|
||||
@@ -3254,7 +3082,6 @@ void GridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
|
||||
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
|
||||
AvgType type)
|
||||
{
|
||||
MFEM_VERIFY(VectorDim() == coeff.GetVDim(), "coeff vdim != VectorDim()");
|
||||
Array<int> zones_per_vdof;
|
||||
AccumulateAndCountZones(coeff, type, zones_per_vdof);
|
||||
|
||||
@@ -3310,33 +3137,52 @@ void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[],
|
||||
}
|
||||
|
||||
void GridFunction::ProjectBdrCoefficientNormal(
|
||||
Coefficient *coeff, VectorCoefficient *vcoeff, const Array<int> &bdr_attr)
|
||||
VectorCoefficient &vcoeff, const Array<int> &bdr_attr)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
|
||||
MFEM_VERIFY(fes->GetTypicalBE()->GetRangeType() == FiniteElement::SCALAR &&
|
||||
fes->GetTypicalBE()->GetMapType() == FiniteElement::INTEGRAL,
|
||||
"Not an RT FE space!");
|
||||
if (vcoeff)
|
||||
{
|
||||
MFEM_VERIFY(vcoeff->GetVDim() == fes->GetMesh()->SpaceDimension(),
|
||||
"vcoeff vdim (" << vcoeff->GetVDim()
|
||||
<< ") != SpaceDimension ("
|
||||
<< fes->GetMesh()->SpaceDimension() << ")");
|
||||
}
|
||||
#if 0
|
||||
// implementation for the case when the face dofs are integrals of the
|
||||
// normal component.
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec, shape;
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
if (bdr_attr[fes->GetBdrAttribute(i)-1] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
fe = fes->GetBE(i);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
int intorder = 2*fe->GetOrder(); // !!!
|
||||
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(), intorder);
|
||||
int nd = fe->GetDof();
|
||||
lvec.SetSize(nd);
|
||||
shape.SetSize(nd);
|
||||
lvec = 0.0;
|
||||
for (int j = 0; j < ir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
fe->CalcShape(ip, shape);
|
||||
lvec.Add(ip.weight * (vc * nor), shape);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#else
|
||||
// implementation for the case when the face dofs are scaled point
|
||||
// values of the normal component.
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector vc, nor, lvec;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
DofTransformation doftrans;
|
||||
if (vcoeff)
|
||||
{
|
||||
const int dim = vcoeff->GetVDim();
|
||||
vc.SetSize(dim);
|
||||
nor.SetSize(dim);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
@@ -3352,22 +3198,15 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
if (coeff)
|
||||
{
|
||||
const real_t c = coeff->Eval(*T, ip);
|
||||
lvec(j) = c * T->Weight();
|
||||
}
|
||||
else if (vcoeff)
|
||||
{
|
||||
vcoeff->Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs, doftrans);
|
||||
doftrans.TransformPrimal(lvec);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GridFunction::ProjectBdrCoefficientTangent(
|
||||
@@ -5168,14 +5007,6 @@ real_t ExtrudeCoefficient::Eval(ElementTransformation &T,
|
||||
return sol_in.Eval(*T_in, ip);
|
||||
}
|
||||
|
||||
void VectorExtrudeCoefficient::Eval(Vector &v, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
ElementTransformation *T_in =
|
||||
mesh_in->GetElementTransformation(T.ElementNo / n);
|
||||
T_in->SetIntPoint(&ip);
|
||||
sol_in.Eval(v, *T_in, ip);
|
||||
}
|
||||
|
||||
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
GridFunction *sol, const int ny)
|
||||
@@ -5226,17 +5057,10 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
return NULL;
|
||||
}
|
||||
FiniteElementSpace *solfes2d;
|
||||
const int vdim = sol->FESpace()->GetVDim();
|
||||
solfes2d = new FiniteElementSpace(mesh2d, solfec2d, vdim);
|
||||
// assuming sol is scalar
|
||||
solfes2d = new FiniteElementSpace(mesh2d, solfec2d);
|
||||
sol2d = new GridFunction(solfes2d);
|
||||
sol2d->MakeOwner(solfec2d);
|
||||
if (vdim > 1)
|
||||
{
|
||||
VectorGridFunctionCoefficient vcsol(sol);
|
||||
VectorExtrudeCoefficient vc2d(mesh, vcsol, ny);
|
||||
sol2d->ProjectCoefficient(vc2d);
|
||||
}
|
||||
else
|
||||
{
|
||||
GridFunctionCoefficient csol(sol);
|
||||
ExtrudeCoefficient c2d(mesh, csol, ny);
|
||||
@@ -5452,7 +5276,6 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
|
||||
Vector lel, uel;
|
||||
GetElementBounds(plb, lel, uel, vdim);
|
||||
|
||||
@@ -5935,4 +5758,4 @@ std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
return std::make_pair(global_max_lower, global_max_upper);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+21
-117
@@ -23,7 +23,6 @@
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -80,18 +79,10 @@ protected:
|
||||
bool wcoef,
|
||||
int subdomain);
|
||||
|
||||
/** @brief Project a discontinuous (vector) coefficient as a grid function on
|
||||
a continuous finite element space. Return in dof_attr the maximal
|
||||
attribute of the elements containing each degree of freedom. */
|
||||
virtual void ProjectDiscCoefficient(
|
||||
std::variant<Coefficient*, VectorCoefficient*> coeff, Array<int> &dof_attr);
|
||||
|
||||
/** @brief Project a discontinuous (vector) coefficient as a grid function on
|
||||
a continuous finite element space. The values in shared dofs are
|
||||
determined from the element with maximal attribute. */
|
||||
virtual void ProjectDiscCoefficient(
|
||||
std::variant<Coefficient*, VectorCoefficient*> coeff)
|
||||
{ Array<int> dof_attr; ProjectDiscCoefficient(coeff, dof_attr); };
|
||||
/** Project a discontinuous vector coefficient in a continuous space and
|
||||
return in dof_attr the maximal attribute of the elements containing each
|
||||
degree of freedom. */
|
||||
void ProjectDiscCoefficient(VectorCoefficient &coeff, Array<int> &dof_attr);
|
||||
|
||||
/** Helper function for ProjectCoefficientElementL2 */
|
||||
void ProjectCoefficientElementL2_(Coefficient &coeff, Vector &sol, Vector &Va);
|
||||
@@ -159,13 +150,11 @@ public:
|
||||
|
||||
FiniteElementCollection *OwnFEC() { return fec_owned; }
|
||||
|
||||
/** @brief Shortcut for calling FiniteElementSpace::GetVectorDim() on the
|
||||
underlying #fes */
|
||||
int VectorDim() const { return fes->GetVectorDim(); }
|
||||
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying #fes
|
||||
int VectorDim() const;
|
||||
|
||||
/** @brief Shortcut for calling FiniteElementSpace::GetCurlDim() on the
|
||||
underlying #fes */
|
||||
int CurlDim() const { return fes->GetCurlDim(); }
|
||||
/// Shortcut for calling FiniteElementSpace::GetCurlDim() on the underlying #fes
|
||||
int CurlDim() const;
|
||||
|
||||
/// Read only access to the (optional) internal true-dof Vector.
|
||||
const Vector &GetTrueVector() const
|
||||
@@ -524,17 +513,10 @@ public:
|
||||
but using an array of scalar coefficients for each component. */
|
||||
void ProjectCoefficient(Coefficient *coeff[]);
|
||||
|
||||
/** @brief Project a discontinuous coefficient as a grid function on
|
||||
a continuous finite element space. The values in shared dofs are
|
||||
determined from the element with maximal attribute. */
|
||||
virtual void ProjectDiscCoefficient(Coefficient &coeff)
|
||||
{ ProjectDiscCoefficient(&coeff); }
|
||||
|
||||
/** @brief Project a discontinuous vector coefficient as a grid function on
|
||||
a continuous finite element space. The values in shared dofs are
|
||||
determined from the element with maximal attribute. */
|
||||
virtual void ProjectDiscCoefficient(VectorCoefficient &coeff)
|
||||
{ ProjectDiscCoefficient(&coeff); }
|
||||
virtual void ProjectDiscCoefficient(VectorCoefficient &coeff);
|
||||
|
||||
enum AvgType {ARITHMETIC, HARMONIC};
|
||||
/** @brief Projects a discontinuous coefficient so that the values in shared
|
||||
@@ -550,9 +532,6 @@ public:
|
||||
std::unique_ptr<GridFunction> ProlongateToMaxOrder() const;
|
||||
|
||||
protected:
|
||||
void ProjectBdrCoefficientNormal(Coefficient *coeff, VectorCoefficient *vcoeff,
|
||||
const Array<int> &attr);
|
||||
|
||||
/** @brief Accumulates (depending on @a type) the values of @a coeff at all
|
||||
shared vdofs and counts in how many zones each vdof appears. */
|
||||
void AccumulateAndCountZones(Coefficient &coeff, AvgType type,
|
||||
@@ -578,13 +557,6 @@ protected:
|
||||
const Array<int> &bdr_attr,
|
||||
Array<int> &values_counter);
|
||||
|
||||
void AccumulateAndCountTraceValues(Coefficient *coeff[],
|
||||
VectorCoefficient *vcoeff,
|
||||
Array<int> &values_counter);
|
||||
|
||||
void AccumulateAndCountTraceTangentValues(VectorCoefficient &vcoeff,
|
||||
Array<int> &values_counter);
|
||||
|
||||
// Complete the computation of averages; called e.g. after
|
||||
// AccumulateAndCountZones().
|
||||
void ComputeMeans(AvgType type, Array<int> &zones_per_vdof);
|
||||
@@ -670,23 +642,6 @@ public:
|
||||
ProjectBdrCoefficient(&coeff_p, attr);
|
||||
}
|
||||
|
||||
/// Project a Coefficient on a GridFunction defined on H1 trace space
|
||||
void ProjectTraceCoefficient(Coefficient *coeff[]);
|
||||
void ProjectTraceCoefficient(Coefficient &coeff);
|
||||
|
||||
/** @brief Project a VectorCoefficient @a vcoeff on a GridFunction
|
||||
defined on a Vector H1 trace space. Note that this also works
|
||||
for a scalar H1 trace space, where only the first component of
|
||||
@a vcoeff is used. */
|
||||
void ProjectTraceCoefficient(VectorCoefficient &vcoeff);
|
||||
/** @brief Project a VectorCoefficient on a GridFunction
|
||||
defined on an RT trace space */
|
||||
void ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff);
|
||||
/** @brief Project a VectorCoefficient on a GridFunction
|
||||
defined on an ND trace space */
|
||||
void ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff);
|
||||
|
||||
|
||||
/** @brief Project a VectorCoefficient on the GridFunction, modifying only
|
||||
DOFs on the boundary associated with the boundary attributes marked in
|
||||
the @a attr array. */
|
||||
@@ -701,26 +656,15 @@ public:
|
||||
virtual void ProjectBdrCoefficient(Coefficient *coeff[],
|
||||
const Array<int> &attr);
|
||||
|
||||
/** @brief Project the normal component of the given VectorCoefficient on
|
||||
the boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr are
|
||||
projected. Assumes RT-type vector finite element GridFunction. */
|
||||
/** Project the normal component of the given VectorCoefficient on
|
||||
the boundary. Only boundary attributes that are marked in
|
||||
'bdr_attr' are projected. Assumes RT-type VectorFE GridFunction. */
|
||||
void ProjectBdrCoefficientNormal(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr)
|
||||
{ ProjectBdrCoefficientNormal(NULL, &vcoeff, bdr_attr); }
|
||||
|
||||
/** @brief Project the given Coefficient in the normal direction on the
|
||||
boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr are projected.
|
||||
Assumes RT-type vector finite element GridFunction. */
|
||||
void ProjectBdrCoefficientNormal(Coefficient &coeff,
|
||||
const Array<int> &bdr_attr)
|
||||
{ ProjectBdrCoefficientNormal(&coeff, NULL, bdr_attr); }
|
||||
const Array<int> &bdr_attr);
|
||||
|
||||
/** @brief Project the tangential components of the given VectorCoefficient
|
||||
on the boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr
|
||||
are projected. Assumes ND-type vector finite element GridFunction. */
|
||||
on the boundary. Only boundary attributes that are marked in @a bdr_attr
|
||||
are projected. Assumes ND-type VectorFE GridFunction. */
|
||||
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr);
|
||||
|
||||
@@ -1791,8 +1735,8 @@ public:
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
/// ordered byNODES:
|
||||
/// points based on \p ref_factor, and returns the bounds for each element
|
||||
/// ordered byNodes:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
@@ -1826,7 +1770,7 @@ public:
|
||||
const int vdim = -1) const;
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byNODES:
|
||||
/// are returned in @b lower and @b upper, ordered byNodes:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
@@ -1970,7 +1914,7 @@ real_t ComputeElementLpDistance(real_t p, int i,
|
||||
GridFunction& gf1, GridFunction& gf2);
|
||||
|
||||
|
||||
/// Class used for extruding a scalar coefficient
|
||||
/// Class used for extruding scalar GridFunctions
|
||||
class ExtrudeCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
@@ -1978,53 +1922,13 @@ private:
|
||||
Mesh *mesh_in;
|
||||
Coefficient &sol_in;
|
||||
public:
|
||||
/// Constructs an instance of VectorExtrudeCoefficient
|
||||
/**
|
||||
* @param m 1D mesh
|
||||
* @param s 1D vector coefficient
|
||||
* @param n_ number of transverse elements of the extruded mesh
|
||||
*/
|
||||
ExtrudeCoefficient(Mesh *m, Coefficient &s, int n_)
|
||||
: n(n_), mesh_in(m), sol_in(s)
|
||||
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
|
||||
|
||||
: n(n_), mesh_in(m), sol_in(s) { }
|
||||
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~ExtrudeCoefficient() { }
|
||||
};
|
||||
|
||||
/// Class used for extruding a vector coefficient
|
||||
class VectorExtrudeCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
int n;
|
||||
Mesh *mesh_in;
|
||||
VectorCoefficient &sol_in;
|
||||
public:
|
||||
/// Constructs an instance of VectorExtrudeCoefficient
|
||||
/**
|
||||
* @param m 1D mesh
|
||||
* @param s 1D vector coefficient
|
||||
* @param n_ number of transverse elements of the extruded mesh
|
||||
*/
|
||||
VectorExtrudeCoefficient(Mesh *m, VectorCoefficient &s, int n_)
|
||||
: VectorCoefficient(s.GetVDim()), n(n_), mesh_in(m), sol_in(s)
|
||||
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
|
||||
|
||||
void Eval(Vector &v, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
using VectorCoefficient::Eval;
|
||||
|
||||
virtual ~VectorExtrudeCoefficient() { }
|
||||
};
|
||||
|
||||
/// Extrude a 1D GridFunction, after extruding the mesh with Extrude1D()
|
||||
/**
|
||||
* @param mesh 1D mesh
|
||||
* @param mesh2d extruded mesh
|
||||
* @param sol grid function
|
||||
* @param ny number of transverse elements of the extruded mesh
|
||||
*/
|
||||
/// Extrude a scalar 1D GridFunction, after extruding the mesh with Extrude1D.
|
||||
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
GridFunction *sol, const int ny);
|
||||
|
||||
|
||||
+158
-2391
File diff suppressed because it is too large
Load Diff
+67
-349
@@ -21,45 +21,6 @@
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
/* gslib license and copyright statement for code adapted from gslib:
|
||||
|
||||
Copyright (c) 2008-2024, UCHICAGO ARGONNE, LLC.
|
||||
|
||||
The UChicago Argonne, LLC as Operator of Argonne National
|
||||
Laboratory holds copyright in the Software. The copyright holder
|
||||
reserves all rights except those expressly granted to licensees,
|
||||
and U.S. Government license rights.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the disclaimer below.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the disclaimer (as noted below)
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. Neither the name of ANL nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
UCHICAGO ARGONNE, LLC, THE U.S. DEPARTMENT OF
|
||||
ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace gslib
|
||||
{
|
||||
struct comm;
|
||||
@@ -125,7 +86,7 @@ protected:
|
||||
void *fdataD;
|
||||
struct gslib::crystal *cr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm; // gslib's internal data
|
||||
int dim, spacedim, points_cnt; // mesh dimension and number of points
|
||||
int dim, points_cnt; // mesh dimension and number of points
|
||||
Array<unsigned int> gsl_code, gsl_proc, gsl_elem, gsl_mfem_elem;
|
||||
Vector gsl_mesh, gsl_ref, gsl_dist, gsl_mfem_ref;
|
||||
Array<unsigned int> recv_proc, recv_index; // data for custom interpolation
|
||||
@@ -143,23 +104,18 @@ protected:
|
||||
bool gpu_to_cpu_fallback = false;
|
||||
|
||||
// Device specific data used for FindPoints
|
||||
struct DEV_STRUCT
|
||||
struct
|
||||
{
|
||||
bool setup_device = false;
|
||||
bool find_device = false;
|
||||
int local_hash_size, dof1d, dof1d_sol, lh_nx, gh_nx;
|
||||
int local_hash_size, dof1d, dof1d_sol, h_o_size, h_nx;
|
||||
double newt_tol; // Tolerance specified during setup for Newton solve
|
||||
struct gslib::crystal *cr;
|
||||
struct gslib::hash_data_3 *hash3;
|
||||
struct gslib::hash_data_2 *hash2;
|
||||
mutable Vector bb, wtend, gll1d, lagcoeff, gll1d_sol, lagcoeff_sol;
|
||||
mutable Array<unsigned int> lh_offset, gh_offset;
|
||||
mutable Vector lh_min, lh_fac, gh_min, gh_fac;
|
||||
// Tolerance to mark points found on the surface as CODE_INTERNAL
|
||||
// or CODE_BORDER. This is needed because we cannot only use reference
|
||||
// space coordinates to determine if a point is located inside the
|
||||
// element or not.
|
||||
mutable double surf_dist_tol;
|
||||
mutable Array<unsigned int> loc_hash_offset;
|
||||
mutable Vector loc_hash_min, loc_hash_fac;
|
||||
} DEV;
|
||||
|
||||
/// Use GSLIB for communication and interpolation
|
||||
@@ -171,157 +127,88 @@ protected:
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/** @brief Since GSLIB is designed to work with quads/hexes, we split every
|
||||
* triangle/tet/prism/pyramid element into quads/hexes. */
|
||||
/// Since GSLIB is designed to work with quads/hexes, we split every
|
||||
/// triangle/tet/prism/pyramid element into quads/hexes.
|
||||
virtual void SetupSplitMeshes();
|
||||
|
||||
/** @brief Setup integration points that will be used to interpolate the
|
||||
* nodal location at points expected by GSLIB. */
|
||||
/// Setup integration points that will be used to interpolate the nodal
|
||||
/// location at points expected by GSLIB.
|
||||
virtual void SetupIntegrationRuleForSplitMesh(Mesh *mesh,
|
||||
IntegrationRule *irule,
|
||||
int order);
|
||||
|
||||
/** @brief Helper function that calls \ref SetupSplitMeshes and
|
||||
* \ref SetupIntegrationRuleForSplitMesh. */
|
||||
/// Helper function that calls \ref SetupSplitMeshes and
|
||||
/// \ref SetupIntegrationRuleForSplitMesh.
|
||||
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
|
||||
|
||||
/// Get GridFunction value at the points expected by GSLIB.
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
|
||||
|
||||
/** @brief Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For
|
||||
* simplices, find the original element number (that was split into
|
||||
* micro quads/hexes) during the setup phase. */
|
||||
/// Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices,
|
||||
/// find the original element number (that was split into micro quads/hexes)
|
||||
/// during the setup phase.
|
||||
virtual void MapRefPosAndElemIndices();
|
||||
|
||||
/// FindPoints locally on device for 3D.
|
||||
// Device functions
|
||||
// FindPoints locally on device for 3D.
|
||||
void FindPointsLocal3(const Vector &point_pos, int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l, int npt);
|
||||
|
||||
/// FindPoints locally on device for 2D.
|
||||
// FindPoints locally on device for 2D.
|
||||
void FindPointsLocal2(const Vector &point_pos, int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l, int npt);
|
||||
|
||||
/// FindPoints locally on device for 3D surface elements.
|
||||
void FindPointsSurfLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
|
||||
/// FindPoints locally on device for 3D edge elements.
|
||||
void FindPointsEdgeLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
|
||||
/// FindPoints locally on device for 2D edge elements.
|
||||
void FindPointsEdgeLocal2(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
|
||||
/// Interpolate on device for 3D.
|
||||
// Interpolate on device for 3D.
|
||||
void InterpolateLocal3(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1dsol);
|
||||
|
||||
/// Interpolate on device for 2D.
|
||||
int nel, int dof1dsol);
|
||||
// Interpolate on device for 2D.
|
||||
void InterpolateLocal2(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1dsol);
|
||||
int nel, int dof1dsol);
|
||||
|
||||
/// Interpolate on device for 1D.
|
||||
void InterpolateLocal1(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp, int dof1dsol);
|
||||
|
||||
/// Prepare data for device execution for volume meshes.
|
||||
// Prepare data for device functions.
|
||||
void SetupDevice();
|
||||
|
||||
/** @brief Searches positions given in physical space by @a point_pos.
|
||||
/** Searches positions given in physical space by @a point_pos.
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by @a point_pos_ordering. */
|
||||
void FindPointsOnDevice(const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
* positions.
|
||||
* @param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
* Assumed ordering is NDOFSxVDIMxNEL
|
||||
* @param[in] nel Number of elements in the mesh.
|
||||
* @param[in] ncomp Number of components in the field.
|
||||
* @param[in] dof1dsol Number of degrees of freedom in each reference
|
||||
* space direction.
|
||||
* @param[in] ordering Ordering of the out field values: byNodes/byVDIM
|
||||
*
|
||||
* @param[out] field_out Interpolated values. For points that are not
|
||||
* found the value is set to
|
||||
* #default_interp_value. */
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
Assumed ordering is NDOFSxVDIMxNEL
|
||||
@param[in] nel Number of elements in the mesh.
|
||||
@param[in] ncomp Number of components in the field.
|
||||
@param[in] dof1dsol Number of degrees of freedom in each reference
|
||||
space direction.
|
||||
@param[in] ordering Ordering of the out field values: byNodes/byVDIM
|
||||
|
||||
@param[out] field_out Interpolated values. For points that are not found
|
||||
the value is set to #default_interp_value. */
|
||||
void InterpolateOnDevice(const Vector &field_in_evec, Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
const int dof1dsol, const int ordering);
|
||||
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
* positions for surface meshes. */
|
||||
void InterpolateSurfBase(const Vector &field_in, Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
const int dof1dsol, const int field_out_ordering);
|
||||
|
||||
/// Preprocess 2D surface mesh needed for FindPoints.
|
||||
void findptsedge_setup_2(DEV_STRUCT &devs,
|
||||
const double *const elx[2],
|
||||
const unsigned n,
|
||||
const uint nel,
|
||||
const unsigned m,
|
||||
const double bbox_tol,
|
||||
const uint local_hash_size,
|
||||
const uint global_hash_size);
|
||||
|
||||
/// Preprocess 3D surface mesh needed for FindPoints.
|
||||
void findptssurf_setup_3(DEV_STRUCT &devs,
|
||||
const double *const elx[3],
|
||||
const unsigned n,
|
||||
const uint nel,
|
||||
const unsigned m,
|
||||
const double bbox_tol,
|
||||
const uint local_hash_size,
|
||||
const uint global_hash_size,
|
||||
const int rD);
|
||||
|
||||
public:
|
||||
/// Serial constructor
|
||||
FindPointsGSLIB();
|
||||
|
||||
/// Serial constructor + setup with given Mesh (see \ref Setup)
|
||||
FindPointsGSLIB(Mesh &mesh_in, const double bb_t = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Constructor for ParMesh
|
||||
FindPointsGSLIB(MPI_Comm comm_);
|
||||
|
||||
/// Constructor + setup with given ParMesh (see \ref Setup)
|
||||
FindPointsGSLIB(ParMesh &mesh_in, const double bb_t = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
@@ -331,10 +218,8 @@ public:
|
||||
FindPointsGSLIB(const FindPointsGSLIB&) = delete;
|
||||
FindPointsGSLIB& operator=(const FindPointsGSLIB&) = delete;
|
||||
|
||||
/** @brief Preprocess the internal mesh in gslib.
|
||||
|
||||
@details Initializes the internal mesh in gslib, by sending the
|
||||
positions of the Gauss-Lobatto nodes of the input Mesh object \p m.
|
||||
/** Initializes the internal mesh in gslib, by sending the positions of the
|
||||
Gauss-Lobatto nodes of the input Mesh object \p m.
|
||||
Note: not tested with periodic (L2).
|
||||
Note: the input mesh \p m must have Nodes set.
|
||||
|
||||
@@ -345,22 +230,13 @@ public:
|
||||
search methods.
|
||||
@param[in] npt_max (Optional) Number of points for simultaneous
|
||||
iteration. This alters performance and
|
||||
memory footprint.
|
||||
*/
|
||||
memory footprint.*/
|
||||
|
||||
void Setup(Mesh &m, const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/// Preprocess the surface mesh to compute data for FindPoints.
|
||||
void SetupSurf(Mesh &m,
|
||||
const double bb_t = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** @brief Searches positions given in physical space by \p point_pos.
|
||||
|
||||
@details These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
/** Searches positions given in physical space by \p point_pos.
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by \p point_pos_ordering.
|
||||
|
||||
This function populates the following member variables:
|
||||
#gsl_code Return codes for each point: inside element (0),
|
||||
element boundary (1), not found (2).
|
||||
@@ -379,34 +255,19 @@ public:
|
||||
#gsl_dist Distance between the sought and the found point
|
||||
in physical space. */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPoints(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPoints(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
|
||||
/** @brief Searches positions given in physical space by \p point_pos on
|
||||
* surface mesh. */
|
||||
void FindPointsSurf(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPointsSurf(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPointsSurf(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
* positions.
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in Function values that will be interpolated on the
|
||||
reference positions. Note: it is assumed that
|
||||
\p field_in is in H1 and in the same space as the
|
||||
@@ -415,36 +276,19 @@ public:
|
||||
the value is set to #default_interp_value.
|
||||
The output ordering is determined from field_in.*/
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
|
||||
|
||||
/// Interpolation of field values, with output ordering specification.
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/** @brief Same as Interpolate but for surface meshes */
|
||||
virtual void InterpolateSurf(const GridFunction &field_in,
|
||||
Vector &field_out);
|
||||
|
||||
/** @brief Same as Interpolate but for surface meshes with specified output
|
||||
ordering */
|
||||
virtual void InterpolateSurf(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/** @brief Search positions and interpolate.
|
||||
*
|
||||
* @details The ordering (byNODES or byVDIM) of the output values in
|
||||
* \p field_out corresponds to the ordering used in the input
|
||||
* GridFunction \p field_in.
|
||||
*/
|
||||
/** Search positions and interpolate. The ordering (byNODES or byVDIM) of
|
||||
the output values in \p field_out corresponds to the ordering used
|
||||
in the input GridFunction \p field_in. */
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Search positions and interpolate with given point and output ordering.
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out, const int point_pos_ordering,
|
||||
const int field_out_ordering);
|
||||
|
||||
/** Setup FindPoints, search positions and interpolate. The ordering (byNODES
|
||||
or byVDIM) of the output values in \p field_out corresponds to the
|
||||
ordering used in the input GridFunction \p field_in. */
|
||||
@@ -452,36 +296,32 @@ public:
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** @brief Average type to be used for L2 functions in-case a point is
|
||||
* located at an element boundary where the function might be multi-valued.
|
||||
*/
|
||||
/// Average type to be used for L2 functions in-case a point is located at
|
||||
/// an element boundary where the function might be multi-valued.
|
||||
virtual void SetL2AvgType(AvgType avgtype_) { avgtype = avgtype_; }
|
||||
|
||||
/** @brief Set the default interpolation value for points that are not found in the mesh. */
|
||||
/// Set the default interpolation value for points that are not found in the
|
||||
/// mesh.
|
||||
virtual void SetDefaultInterpolationValue(double interp_value_)
|
||||
{
|
||||
default_interp_value = interp_value_;
|
||||
}
|
||||
|
||||
/** @brief Tolerance for detecting points outside the 'curvilinear' boundary.
|
||||
*
|
||||
* @details When using FindPoints, gslib may return points as found on the
|
||||
* boundary even when they are slightly outside the domain. This tolerance
|
||||
* is used to filter such points based on the distance^2 value and mark them
|
||||
* as not found.*/
|
||||
/// Set the tolerance for detecting points outside the 'curvilinear' boundary
|
||||
/// that gslib may return as found on the boundary. Points found on boundary
|
||||
/// with distance greater than @ bdr_tol are marked as not found.
|
||||
virtual void SetDistanceToleranceForPointsFoundOnBoundary(double bdr_tol_)
|
||||
{
|
||||
bdr_tol = bdr_tol_;
|
||||
}
|
||||
|
||||
/** @brief Enable/Disable use of CPU functions for GPU data if the gslib
|
||||
* version is older. */
|
||||
/// Enable/Disable use of CPU functions for GPU data if the gslib version
|
||||
/// is older.
|
||||
virtual void SetGPUtoCPUFallback(bool mode) { gpu_to_cpu_fallback = mode; }
|
||||
|
||||
/** @brief Cleans up memory allocated internally by gslib.
|
||||
|
||||
@details Note that in parallel, this must be called before MPI_Finalize,
|
||||
as it calls MPI_Comm_free() for internal gslib communicators. FreeData is
|
||||
/** Cleans up memory allocated internally by gslib.
|
||||
Note that in parallel, this must be called before MPI_Finalize(), as it
|
||||
calls MPI_Comm_free() for internal gslib communicators. FreeData is
|
||||
also called by the class destructor and there are no memory leaks if the
|
||||
destructor is called before MPI_Finalize(). If the destructor is called
|
||||
after MPI_Finalize(), there will be an error because gslib will try to
|
||||
@@ -489,8 +329,8 @@ public:
|
||||
*/
|
||||
virtual void FreeData();
|
||||
|
||||
/** @brief Return code for each point searched by FindPoints:
|
||||
* inside element (0), element boundary (1), or not found (2). */
|
||||
/// Return code for each point searched by FindPoints: inside element (0), on
|
||||
/// element boundary (1), or not found (2).
|
||||
virtual const Array<unsigned int> &GetCode() const { return gsl_code; }
|
||||
/// Return element number for each point found by FindPoints.
|
||||
virtual const Array<unsigned int> &GetElem() const { return gsl_mfem_elem; }
|
||||
@@ -498,15 +338,15 @@ public:
|
||||
virtual const Array<unsigned int> &GetProc() const { return gsl_proc; }
|
||||
/// Return reference coordinates for each point found by FindPoints.
|
||||
virtual const Vector &GetReferencePosition() const { return gsl_mfem_ref; }
|
||||
/// Return distance between the sought and the found point in physical space.
|
||||
/// Return distance between the sought and the found point in physical space,
|
||||
/// for each point found by FindPoints.
|
||||
virtual const Vector &GetDist() const { return gsl_dist; }
|
||||
|
||||
/** @brief Return element number for each point found by FindPoints
|
||||
* corresponding to GSLIB mesh. gsl_mfem_elem != gsl_elem for mesh with
|
||||
* simplices. */
|
||||
/// Return element number for each point found by FindPoints corresponding to
|
||||
/// GSLIB mesh. gsl_mfem_elem != gsl_elem for mesh with simplices.
|
||||
virtual const Array<unsigned int> &GetGSLIBElem() const { return gsl_elem; }
|
||||
/** @brief Return reference coordinates in [-1,1] (internal range in GSLIB)
|
||||
* for each point found by FindPoints. */
|
||||
/// Return reference coordinates in [-1,1] (internal range in GSLIB) for each
|
||||
/// point found by FindPoints.
|
||||
virtual const Vector &GetGSLIBReferencePosition() const { return gsl_ref; }
|
||||
|
||||
/// Get array of indices of not-found points.
|
||||
@@ -549,7 +389,7 @@ public:
|
||||
|
||||
/// Return the axis-aligned bounding boxes (AABB) computed during \ref Setup.
|
||||
/// The size of the returned vector is (nel x nverts x dim), where nel is the
|
||||
/// number of elements (after splitting for simplicies), nverts is number of
|
||||
/// number of elements (after splitting for simplcies), nverts is number of
|
||||
/// vertices (4 in 2D, 8 in 3D), and dim is the spatial dimension.
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb) const;
|
||||
|
||||
@@ -563,18 +403,6 @@ public:
|
||||
/// \p obbV, a vector of size (nel x nverts x dim) .
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV) const;
|
||||
|
||||
/** @brief Return the bounding boxes as a mesh on rank 0.
|
||||
*
|
||||
* @param[in] type Bounding-box type: 0 - AABB, 1 - OBB.
|
||||
*
|
||||
* @return On rank 0, returns a newly allocated mesh containing the
|
||||
* bounding boxes. The caller owns the returned pointer and is responsible
|
||||
* for deleting it. On other ranks, returns nullptr.
|
||||
*/
|
||||
Mesh *GetBoundingBoxMesh(int type);
|
||||
|
||||
virtual const Vector &GetGLLMesh() const { return gsl_mesh; }
|
||||
};
|
||||
|
||||
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
|
||||
@@ -702,116 +530,6 @@ public:
|
||||
void GS(Vector &senddata, GSOp op);
|
||||
};
|
||||
|
||||
#if defined(MFEM_USE_MPI)
|
||||
/** \brief Class to map a point in physical space to candidate ranks.
|
||||
*
|
||||
* This class builds a Cartesian-aligned tensor grid that covers the entire
|
||||
* domain and precomputes which ranks have elements intersecting each
|
||||
* grid cell. Given a point in physical space, the grid cell containing
|
||||
* the point is determined, and the list of candidate ranks whose
|
||||
* elements intersect that cell is returned. This yields a fast, conservative
|
||||
* point-to-rank candidate query. This is used internally by FindPointsGSLIB
|
||||
* to speed up point searches in parallel.
|
||||
*
|
||||
* See Mittal et al., "General Field Evaluation in High-Order Meshes on GPUs".
|
||||
* (2025). Computers & Fluids. for technical details.
|
||||
*
|
||||
*/
|
||||
class GlobalBBoxTensorGridMap
|
||||
{
|
||||
private:
|
||||
struct gslib::crystal *cr = nullptr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm = nullptr; // gslib's internal data
|
||||
int sdim, n_local_cells, num_procs;
|
||||
Array<int> gmap_n;
|
||||
Vector gmap_bnd_min, gmap_bnd_max;
|
||||
Vector gmap_fac;
|
||||
Array<int> ggrid_map;
|
||||
|
||||
void SetupCrystal(const MPI_Comm &comm);
|
||||
public:
|
||||
/// Constructor for a given mesh and number of tensor grid divisions
|
||||
GlobalBBoxTensorGridMap(ParMesh &pmesh, int nx);
|
||||
|
||||
/** @brief Constructor for given element bounds and spatial dimension.
|
||||
*
|
||||
* @details This constructor must be called collectively on \a comm.
|
||||
* Supports spatial dimensions 1, 2, and 3, and accepts nel == 0 on a rank.
|
||||
*
|
||||
* Assumes elmin, elmax Ordering::byNodes:
|
||||
* elmin -> [x_{0,min},x_{1,min},... ,y_{0,min},y_{1,min},..,z_{nel-1,min}]
|
||||
* elmax -> [x_{0,max},x_{1,max},... ,y_{0,max},y_{1,max},..,z_{nel-1,max}]
|
||||
* Note elmin, elmax can be obtained using GridFunction::GetElementBounds()
|
||||
*
|
||||
* When by_max_size=false, n gives the number of tensor-grid divisions in
|
||||
* each direction. When by_max_size=true, n is a per-rank size hint used to
|
||||
* derive a uniform global resolution. The communicator-wide sum of n is
|
||||
* converted to nx = ceil(pow(sum(n), 1./sdim)) in each direction, so n is
|
||||
* not a hard cap on ggrid_map.Size().
|
||||
*/
|
||||
GlobalBBoxTensorGridMap(const MPI_Comm &comm, Vector &elmin,
|
||||
Vector &elmax, int nel, int sdim, int n,
|
||||
bool by_max_size);
|
||||
|
||||
/** @brief Constructor for given element bounds, spatial dimension, and
|
||||
* tensor-grid divisions in each direction.
|
||||
*
|
||||
* @details This constructor must be called collectively on \a comm.
|
||||
* Supports spatial dimensions 1, 2, and 3, and accepts nel == 0 on a rank.
|
||||
* Requires nx.Size() == sdim and positive entries in nx.
|
||||
*
|
||||
* Assumes elmin, elmax Ordering::byNodes:
|
||||
* elmin -> [x_{0,min},x_{1,min},... ,y_{0,min},y_{1,min},..,z_{nel-1,min}]
|
||||
* elmax -> [x_{0,max},x_{1,max},... ,y_{0,max},y_{1,max},..,z_{nel-1,max}]
|
||||
* Note elmin, elmax can be obtained using GridFunction::GetElementBounds()
|
||||
*/
|
||||
GlobalBBoxTensorGridMap(const MPI_Comm &comm, Vector &elmin,
|
||||
Vector &elmax, int nel, int sdim, Array<int> &nx);
|
||||
|
||||
~GlobalBBoxTensorGridMap();
|
||||
|
||||
/** @brief Get list of procs corresponding to the list of points.
|
||||
*
|
||||
* @details This method must be called collectively on the communicator
|
||||
* used to construct the map. The input points can be ordered byNodes:
|
||||
* (XXX...,YYY...,ZZZ) or byVDIM: (XYZ,XYZ,...), as specified by
|
||||
* \a ordering.
|
||||
*
|
||||
* The output map contains one entry for each input point, keyed by the
|
||||
* point's local index in \a xyz. Points with no candidate ranks, including
|
||||
* points outside the global bounding box, have an empty list of candidate
|
||||
* ranks.
|
||||
*/
|
||||
void MapPointsToProcs(Vector &xyz, int ordering,
|
||||
std::map<int, std::vector<int>> &pt_to_procs) const;
|
||||
|
||||
// Some getters
|
||||
const Array<int> &GetGridMap() const { return ggrid_map; }
|
||||
const Vector &GetGridFac() const { return gmap_fac; }
|
||||
const Vector &GetGridMin() const { return gmap_bnd_min; }
|
||||
const Vector &GetGridMax() const { return gmap_bnd_max; }
|
||||
const Array<int> &GetGridN() const { return gmap_n; }
|
||||
|
||||
private:
|
||||
/// Setup the map given element bounds and number of tensor grid divisions.
|
||||
void Setup(const MPI_Comm &comm, Vector &elmin, Vector &elmax,
|
||||
int nel, Array<int> &nx);
|
||||
|
||||
/// Get global hash cell index for a given point.
|
||||
int GetGlobalGridCellFromPoint(Vector &xyz) const;
|
||||
|
||||
/** @brief Get owning proc and local index on that proc for given global
|
||||
* grid cell index. */
|
||||
void GlobalGridCellToProcAndLocalIndex(int i, int &proc, int &idx) const;
|
||||
|
||||
/// Map a point to proc and local index of the corresponding grid cell
|
||||
void GetProcAndLocalIndexFromPoint(Vector &xyz, int &proc, int &idx) const;
|
||||
|
||||
/// Given local cell index, return list of procs saved in the map
|
||||
Array<int> MapCellToProcs(int l_idx) const;
|
||||
};
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_GSLIB
|
||||
|
||||
@@ -254,7 +254,7 @@ get_edge(const double *elx[2], const double *wtend, int ei,
|
||||
edge.dxdn[d] = workspace + (2 + d) * pN; //dxdn and dydn at DOFs along edge
|
||||
}
|
||||
|
||||
if (static_cast<unsigned>(side_init) != (1u << ei))
|
||||
if (side_init != (1u << ei))
|
||||
{
|
||||
#define ELX(d, j, k) elx[d][j + k * pN] // assumes lexicographic ordering
|
||||
for (int d = 0; d < 2; ++d)
|
||||
@@ -562,7 +562,7 @@ newton_area_fin:
|
||||
int f = flags >> (2 * dd) & 3u;
|
||||
res->r[dd] = f == 0 ? r0[dd] + dr[dd] : (f == 1 ? -1 : 1);
|
||||
}
|
||||
res->flags = flags | ((p->flags & FLAG_MASK) << 5);
|
||||
res->flags = flags | (p->flags << 5);
|
||||
}
|
||||
|
||||
// Full Newton solve on the face. One of r/s/t is constrained.
|
||||
@@ -635,8 +635,7 @@ newton_edge_fin:
|
||||
res->r[de] = nr;
|
||||
res->r[dn]=p->r[dn];
|
||||
res->dist2p = -v;
|
||||
res->flags = flags | new_flags | ((p->flags & FLAG_MASK) << 5);
|
||||
#undef EVAL
|
||||
res->flags = flags | new_flags | (p->flags << 5);
|
||||
}
|
||||
|
||||
// Find closest mesh node to the sought point.
|
||||
@@ -715,6 +714,7 @@ static void FindPointsLocal2D_Kernel(const int npt,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
#define MAX_CONST(a, b) (((a) > (b)) ? (a) : (b))
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_NE = D1D*D1D;
|
||||
@@ -729,7 +729,7 @@ static void FindPointsLocal2D_Kernel(const int npt,
|
||||
// 3D1D for seed, 10D1D+6 for area, 3D1D+9 for edge
|
||||
constexpr int size1 = 10*MD1 + 6;
|
||||
constexpr int size2 = MD1*4; // edge constraints
|
||||
constexpr int size3 = MD1*MD1*DIM; // local element coordinates
|
||||
constexpr int size3 = MD1*MD1*MD1*DIM; // local element coordinates
|
||||
|
||||
MFEM_SHARED double r_workspace[size1];
|
||||
MFEM_SHARED findptsElementPoint_t el_pts[2];
|
||||
@@ -1162,9 +1162,9 @@ void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
|
||||
auto pgslm = gsl_mesh.Read();
|
||||
auto pwt = DEV.wtend.Read();
|
||||
auto pbb = DEV.bb.Read();
|
||||
auto plhm = DEV.lh_min.Read();
|
||||
auto plhf = DEV.lh_fac.Read();
|
||||
auto plho = DEV.lh_offset.ReadWrite();
|
||||
auto plhm = DEV.loc_hash_min.Read();
|
||||
auto plhf = DEV.loc_hash_fac.Read();
|
||||
auto plho = DEV.loc_hash_offset.ReadWrite();
|
||||
auto pcode = code.Write();
|
||||
auto pelem = elem.Write();
|
||||
auto pref = ref.Write();
|
||||
@@ -1177,32 +1177,30 @@ void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
|
||||
case 2:
|
||||
return FindPointsLocal2D_Kernel<2>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
case 3:
|
||||
return FindPointsLocal2D_Kernel<3>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
case 4:
|
||||
return FindPointsLocal2D_Kernel<4>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
case 5:
|
||||
return FindPointsLocal2D_Kernel<5>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
default:
|
||||
return FindPointsLocal2D_Kernel(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx,
|
||||
plhm, plhf, plho, pcode, pelem,
|
||||
pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
}
|
||||
}
|
||||
#undef DIM2
|
||||
#undef DIM
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
|
||||
@@ -294,7 +294,7 @@ get_face(const double *elx[3], const double *wtend, int fi, double *workspace,
|
||||
face.dxdn[d] = workspace+(3+d)*p_Nfr;
|
||||
}
|
||||
|
||||
if (static_cast<unsigned>(side_init) != (1u << fi))
|
||||
if (side_init != (1u << fi))
|
||||
{
|
||||
const int e_stride[3] = {1, pN, pN*pN};
|
||||
#define ELX(d, j, k, l) elx[d][j*e_stride[d1]+k*e_stride[d2]+l*e_stride[dn]]
|
||||
@@ -342,7 +342,7 @@ get_edge(const double *elx[3], const double *wtend, int ei, double *workspace,
|
||||
|
||||
if (jidx >= 3*pN) { return edge; }
|
||||
|
||||
if (static_cast<unsigned>(side_init) != (64u << ei))
|
||||
if (side_init != (64u << ei))
|
||||
{
|
||||
const int e_stride[3] = {1, pN, pN*pN};
|
||||
#define ELX(d, j, k, l) elx[d][j*e_stride[de]+k*e_stride[dn1]+l*e_stride[dn2]]
|
||||
@@ -706,7 +706,7 @@ newton_vol_fin:
|
||||
int f = flags >> (2*dd) & 3u;
|
||||
res->r[dd] = f == 0 ? r0[dd]+dr[dd] : (f == 1 ? -1 : 1);
|
||||
}
|
||||
res->flags = flags | ((p->flags & FLAG_MASK) << 7);
|
||||
res->flags = flags | (p->flags << 7);
|
||||
}
|
||||
|
||||
// Full Newton solve on the face. One of r/s/t is constrained.
|
||||
@@ -889,7 +889,7 @@ newton_face_fin:
|
||||
res->r[dn] = p->r[dn];
|
||||
res->r[d1] = r[0];
|
||||
res->r[d2] = r[1];
|
||||
res->flags = new_flags | ((p->flags & FLAG_MASK) << 7);
|
||||
res->flags = new_flags | (p->flags << 7);
|
||||
}
|
||||
|
||||
// Full Newton solve on the edge. Two of r/s/t are constrained.
|
||||
@@ -973,8 +973,7 @@ newton_edge_fin:
|
||||
res->r[dn1] = p->r[dn1];
|
||||
res->r[dn2] = p->r[dn2];
|
||||
res->dist2p = -v;
|
||||
res->flags = flags | new_flags | ((p->flags & FLAG_MASK) << 7);
|
||||
#undef EVAL
|
||||
res->flags = flags | new_flags | (p->flags << 7);
|
||||
}
|
||||
|
||||
// Find closest mesh node to the sought point.
|
||||
@@ -1253,6 +1252,7 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
case 0: // findpt_vol
|
||||
{
|
||||
double *wtr = r_workspace_ptr;
|
||||
|
||||
double *resid = wtr+6*D1D;
|
||||
double *jac = resid+3;
|
||||
double *resid_temp = jac+9;
|
||||
@@ -1503,7 +1503,7 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
// Hes_T is transposed version (i.e. in col major)
|
||||
// n1*[2, 1, 1, 0, 0]
|
||||
// j==1 => wt_j = wt+n1
|
||||
double *wt_j = wt+D1D*(2 - (row+1)/2);
|
||||
double *wt_j = wt+D1D*(2-(row+1) / 2);
|
||||
const double *x = e_x[row+1][d];
|
||||
hes_T[j] = 0.0;
|
||||
for (int k = 0; k < D1D; ++k)
|
||||
@@ -1522,6 +1522,7 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
hes[j] += resid[d]*hes_T[j*3+d];
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(l,x,1)
|
||||
@@ -1779,7 +1780,6 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
} //findpts_local
|
||||
} //elp
|
||||
});
|
||||
#undef MAXC
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
@@ -1796,9 +1796,9 @@ void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
auto pgslm = gsl_mesh.Read();
|
||||
auto pwt = DEV.wtend.Read();
|
||||
auto pbb = DEV.bb.Read();
|
||||
auto plhm = DEV.lh_min.Read();
|
||||
auto plhf = DEV.lh_fac.Read();
|
||||
auto plho = DEV.lh_offset.ReadWrite();
|
||||
auto plhm = DEV.loc_hash_min.Read();
|
||||
auto plhf = DEV.loc_hash_fac.Read();
|
||||
auto plho = DEV.loc_hash_offset.ReadWrite();
|
||||
auto pcode = code.Write();
|
||||
auto pelem = elem.Write();
|
||||
auto pref = ref.Write();
|
||||
@@ -1809,31 +1809,31 @@ void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
{
|
||||
case 2:
|
||||
FindPointsLocal3DKernel<2>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
case 3:
|
||||
FindPointsLocal3DKernel<3>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
case 4:
|
||||
FindPointsLocal3DKernel<4>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
case 5:
|
||||
FindPointsLocal3DKernel<5>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
default:
|
||||
FindPointsLocal3DKernel(npt, DEV.newt_tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
NE_split_total, pwt, pbb, DEV.h_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc,
|
||||
DEV.dof1d);
|
||||
}
|
||||
|
||||
@@ -1,725 +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 "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#include "gslib.h"
|
||||
#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
|
||||
#define GSLIB_RELEASE_VERSION 10007
|
||||
#endif
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
#define CODE_INTERNAL 0
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
#define sDIM 2
|
||||
#define sDIM2 4
|
||||
#define rDIM 1
|
||||
|
||||
struct findptsElementPoint_t
|
||||
{
|
||||
double x[sDIM], r, oldr, dist2, dist2p, tr;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct findptsElementGEdge_t
|
||||
{
|
||||
double *x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsElementGPT_t
|
||||
{
|
||||
double x[sDIM], jac[sDIM*rDIM], hes[sDIM*rDIM];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2 * (x-z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p1[i] = 2.0 * lCoeff[i] * u1;
|
||||
p2[i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside
|
||||
}
|
||||
|
||||
/* positive when given point is possibly inside given obbox b */
|
||||
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = obbox_axis_test(b,x);
|
||||
if (bxyz<0) // test if point is in AABB
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else // test OBB only if inside AABB
|
||||
{
|
||||
double dxyz[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*2 + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[2])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline double l2norm2(const double x[2])
|
||||
{
|
||||
return x[0] * x[0] + x[1] * x[1];
|
||||
}
|
||||
|
||||
/* the bit structure of flags is CRR
|
||||
the C bit --- 1<<2 --- is set when the point is converged
|
||||
RR is 0 = 00b if r is unconstrained,
|
||||
1 = 01b if r is constrained at -1, i.e., rmin
|
||||
2 = 10b if r is constrained at +1, i.e., rmax
|
||||
*/
|
||||
|
||||
#define CONVERGED_FLAG (1u<<2)
|
||||
#define FLAG_MASK 0x07u // = 111b
|
||||
|
||||
/* returns 1 if r direction (the only free direction in 2D) is constrained.
|
||||
returns 1 if either 1st or 2nd bit of flags is set.
|
||||
*/
|
||||
static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
|
||||
{
|
||||
return ((flags | flags>>1) & 1u);
|
||||
}
|
||||
|
||||
/* pi=0, r=-1; pi=1, r=+1 */
|
||||
static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
{
|
||||
return ((x>>1) & 1u);
|
||||
}
|
||||
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
const double resid[2],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = l2norm2(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
out->x[0] = p->x[0];
|
||||
out->x[1] = p->x[1];
|
||||
out->oldr = p->r;
|
||||
out->dist2 = dist2;
|
||||
if (decr >= 0.01*pred)
|
||||
{
|
||||
if (decr >= 0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = p->tr*2;
|
||||
}
|
||||
else // somewhat good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* reject step; note: the point will pass through this routine
|
||||
again, and we set things up here so it gets classed as a
|
||||
"very good iteration" --- this doubles the trust radius,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r - p->oldr);
|
||||
out->tr = v0/4.0;
|
||||
out->dist2 = p->dist2;
|
||||
out->r = p->oldr;
|
||||
out->flags = p->flags>>3;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
if (pred < dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge( findptsElementPoint_t *const
|
||||
out,
|
||||
const double jac[2],
|
||||
const double rhess,
|
||||
const double resid[2],
|
||||
int flags,
|
||||
const findptsElementPoint_t *const p,
|
||||
const double tol )
|
||||
{
|
||||
const double tr = p->tr;
|
||||
const double A = jac[0] * jac[0] + jac[1] * jac[1] -
|
||||
rhess; // A = J^T J - resid_d H_d
|
||||
const double y = jac[0]*resid[0] + jac[1]*resid[1]; // y = J^T resid
|
||||
|
||||
const double oldr = p->r;
|
||||
double dr, newr, tdr, tnewr, v, tv;
|
||||
int new_flags=0, tnew_flags=0;
|
||||
|
||||
#define EVAL(dr) ( (dr*A - 2*y) * dr )
|
||||
if (A>0)
|
||||
{
|
||||
dr = y/A;
|
||||
if (fabs(dr)<tol)
|
||||
{
|
||||
dr=0.0;
|
||||
newr = oldr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newr = oldr+dr;
|
||||
}
|
||||
|
||||
if (fabs(dr)<tr && fabs(newr)<1)
|
||||
{
|
||||
v = EVAL(dr);
|
||||
goto newton_edge_fin;
|
||||
}
|
||||
}
|
||||
|
||||
if ((newr=oldr-tr) > -1)
|
||||
{
|
||||
dr = -tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newr = -1, dr = -1-oldr, new_flags = flags|1u;
|
||||
}
|
||||
v = EVAL(dr);
|
||||
|
||||
if ((tnewr=oldr+tr) < 1)
|
||||
{
|
||||
tdr = tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
tnewr = 1, tdr = 1-oldr, tnew_flags = flags|2u;
|
||||
}
|
||||
tv = EVAL(tdr);
|
||||
|
||||
if (tv<v)
|
||||
{
|
||||
newr = tnewr, dr = tdr, v = tv, new_flags = tnew_flags;
|
||||
}
|
||||
#undef EVAL
|
||||
|
||||
newton_edge_fin:
|
||||
// check convergence by testing if change in r is less than tol
|
||||
if (fabs(dr)<tol)
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r = newr;
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE void seed_j( const double *elx[sDIM],
|
||||
const double x[sDIM],
|
||||
const double *z,
|
||||
double *dist2,
|
||||
double *r,
|
||||
const int ir,
|
||||
const int pN )
|
||||
{
|
||||
double dx[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dx[d] = x[d] - elx[d][ir];
|
||||
}
|
||||
dist2[ir] = HUGE_VAL;
|
||||
const double dist2_rs = l2norm2(dx);
|
||||
if (dist2[ir]>dist2_rs)
|
||||
{
|
||||
dist2[ir] = dist2_rs;
|
||||
r[ir] = z[ir];
|
||||
}
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsEdgeLocal2D_Kernel( const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0 )
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_NEL = nel*D1D;
|
||||
MFEM_VERIFY(MD1<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D!=0, "Polynomial order not specified.");
|
||||
const int nThreads = D1D*sDIM;
|
||||
|
||||
mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
// 2D1D for seed, 3D1D + 7 for edge
|
||||
constexpr int size1 = 3*MD1 + 7;
|
||||
// edge coordinates = D1D*2
|
||||
constexpr int size2 = 2*MD1;
|
||||
// local element coordinates in shared memory
|
||||
constexpr int size3 = MD1*sDIM;
|
||||
|
||||
MFEM_SHARED findptsElementPoint_t el_pts[2];
|
||||
MFEM_SHARED double r_workspace[size1];
|
||||
|
||||
MFEM_SHARED double constraint_workspace[size2];
|
||||
|
||||
MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
|
||||
|
||||
double *r_workspace_ptr = r_workspace;
|
||||
findptsElementPoint_t *fpt, *tmp;
|
||||
fpt = el_pts + 0;
|
||||
tmp = el_pts + 1;
|
||||
|
||||
// x and y coord index within point_pos for point i
|
||||
int id_x = point_pos_ordering == 0 ? i : i*sDIM;
|
||||
int id_y = point_pos_ordering == 0 ? i+npt : i*sDIM+1;
|
||||
double x_i[2] = {x[id_x], x[id_y]};
|
||||
|
||||
unsigned int *code_i = code_base + i;
|
||||
double *dist2_i = dist2_base + i;
|
||||
|
||||
//---------------- map_points_to_els --------------------
|
||||
findptsLocalHashData_t hash;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
hash.bnd[d].min = hashMin[d];
|
||||
hash.fac[d] = hashFac[d];
|
||||
}
|
||||
hash.hash_n = hash_n;
|
||||
hash.offset = hashOffset;
|
||||
|
||||
const int hi = hash_index(&hash, x_i);
|
||||
const unsigned int *elp = hash.offset + hash.offset[hi];
|
||||
const unsigned int *const ele = hash.offset + hash.offset[hi+1];
|
||||
*code_i = CODE_NOT_FOUND;
|
||||
*dist2_i = HUGE_VAL;
|
||||
|
||||
for (; elp!=ele; ++elp)
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
|
||||
if (obbox_test(&box,x_i)>=0)
|
||||
{
|
||||
//------------ findpts_local ------------------
|
||||
{
|
||||
if (MD1 <= 6)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
|
||||
{
|
||||
const int qp = j % D1D;
|
||||
const int d = j / D1D;
|
||||
elem_coords[qp + d*D1D] =
|
||||
xElemCoord[qp + el*D1D + d*p_NEL];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
const double *elx[sDIM];
|
||||
for (int d=0; d<sDIM; d++)
|
||||
{
|
||||
elx[d] = MD1<= 6 ? &elem_coords[d*D1D] :
|
||||
xElemCoord + d*p_NEL + el*D1D;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
//// findpts_el ////
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
fpt->dist2 = HUGE_VAL;
|
||||
fpt->dist2p = 0;
|
||||
fpt->tr = 1;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
fpt->x[j] = x_i[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
{
|
||||
double *dist2_temp = r_workspace_ptr;
|
||||
double *r_temp = dist2_temp + D1D;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
for (int ir=0; ir<D1D; ++ir)
|
||||
{
|
||||
if (dist2_temp[ir]<fpt->dist2)
|
||||
{
|
||||
fpt->dist2 = dist2_temp[ir];
|
||||
fpt->r = r_temp[ir];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} //seed done
|
||||
|
||||
// Initialize tmp struct with fpt values before starting Newton iterations
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
tmp->dist2 = HUGE_VAL;
|
||||
tmp->dist2p = 0;
|
||||
tmp->tr = 1;
|
||||
tmp->flags = 0;
|
||||
tmp->r = fpt->r;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
tmp->x[j] = fpt->x[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
|
||||
for (int step=0; step<50; step++)
|
||||
{
|
||||
int nc = num_constrained(tmp->flags & FLAG_MASK);
|
||||
switch (nc)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
double *wt = r_workspace_ptr;
|
||||
double *resid = wt + 3*D1D;
|
||||
double *jac = resid + sDIM;
|
||||
double *hess = jac + sDIM*rDIM;
|
||||
|
||||
findptsElementGEdge_t edge;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.x[d][j] = elx[d][j];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// compute basis function info upto 2nd derivative
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
lag_eval_second_der(wt, tmp->r, j, gll1D,
|
||||
lagcoeff, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
resid[j] = tmp->x[j];
|
||||
jac[j] = 0.0;
|
||||
hess[j] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
resid[j] -= wt[ k]*edge.x[j][k];
|
||||
jac[j] += wt[D1D+k]*edge.x[j][k];
|
||||
hess[j] += wt[2*D1D+k]*edge.x[j][k];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
hess[2] = resid[0]*hess[0] + resid[1]*hess[1];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
if (!reject_prior_step_q(fpt, resid, tmp, tol))
|
||||
{
|
||||
newton_edge(fpt, jac, hess[2], resid,
|
||||
tmp->flags & FLAG_MASK, tmp, tol);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
}
|
||||
case 1: // r is constrained to either -1 or 1
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
const int pi = point_index(tmp->flags &
|
||||
FLAG_MASK);
|
||||
const double *wt = wtend + pi*3*D1D;
|
||||
findptsElementGPT_t gpt;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
gpt.x[d] = elx[d][pi*(D1D-1)];
|
||||
gpt.jac[d] = 0.0;
|
||||
gpt.hes[d] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
gpt.jac[d] += wt[D1D +k]*elx[d][k];
|
||||
gpt.hes[d] += wt[2*D1D+k]*elx[d][k];
|
||||
}
|
||||
}
|
||||
|
||||
const double *const pt_x = gpt.x;
|
||||
const double *const jac = gpt.jac;
|
||||
const double *const hes = gpt.hes;
|
||||
double resid[sDIM], steep, sr;
|
||||
resid[0] = fpt->x[0] - pt_x[0];
|
||||
resid[1] = fpt->x[1] - pt_x[1];
|
||||
steep = jac[0]*resid[0] + jac[1]*resid[1];
|
||||
sr = steep*tmp->r;
|
||||
if ( !reject_prior_step_q(fpt, resid, tmp, tol) )
|
||||
{
|
||||
if (sr<0)
|
||||
{
|
||||
const double rhess = resid[0]*hes[0] +
|
||||
resid[1]*hes[1];
|
||||
newton_edge(fpt, jac, rhess,
|
||||
resid, 0, tmp, tol);
|
||||
}
|
||||
else // sr==0
|
||||
{
|
||||
fpt->r = tmp->r;
|
||||
fpt->dist2p = 0;
|
||||
fpt->flags = tmp->flags | CONVERGED_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
} // case 1
|
||||
} //switch
|
||||
if (fpt->flags & CONVERGED_FLAG)
|
||||
{
|
||||
break;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*tmp = *fpt;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} //for int step<50
|
||||
} //findpts_el
|
||||
|
||||
bool converged_internal =
|
||||
((fpt->flags&FLAG_MASK) == CONVERGED_FLAG) &&
|
||||
(fpt->dist2<dist2tol);
|
||||
|
||||
if (*code_i == CODE_NOT_FOUND || converged_internal ||
|
||||
fpt->dist2 < *dist2_i)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*(el_base+i) = el;
|
||||
*code_i = converged_internal ? CODE_INTERNAL : CODE_BORDER;
|
||||
*dist2_i = fpt->dist2;
|
||||
*(r_base+i) = fpt->r;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (converged_internal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
} //findpts_local
|
||||
} //obbox_test
|
||||
} //elp
|
||||
});
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal2( const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt )
|
||||
{
|
||||
if (npt==0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MFEM_VERIFY(dim==1 && spacedim==2,"Function for 2D edges only");
|
||||
bool use_dev = point_pos.UseDevice();
|
||||
auto pp = point_pos.Read(use_dev);
|
||||
auto pgslm = gsl_mesh.Read(use_dev);
|
||||
auto pwt = DEV.wtend.Read(use_dev);
|
||||
auto pbb = DEV.bb.Read(use_dev);
|
||||
auto plhm = DEV.lh_min.Read(use_dev);
|
||||
auto plhf = DEV.lh_fac.Read(use_dev);
|
||||
auto plho = DEV.lh_offset.ReadWrite(use_dev);
|
||||
auto pcode = code.Write(use_dev);
|
||||
auto pelem = elem.Write(use_dev);
|
||||
auto pref = ref.Write(use_dev);
|
||||
auto pdist = dist.Write(use_dev);
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsEdgeLocal2D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 3:
|
||||
return FindPointsEdgeLocal2D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 4:
|
||||
return FindPointsEdgeLocal2D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
default:
|
||||
return FindPointsEdgeLocal2D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
}
|
||||
}
|
||||
#undef sDIM
|
||||
#undef rDIM
|
||||
#undef sDIM2
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
#else
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal2( const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt ) {} ;
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
#endif //ifdef MFEM_USE_GSLIB
|
||||
@@ -1,733 +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 "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#include "gslib.h"
|
||||
#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
|
||||
#define GSLIB_RELEASE_VERSION 10007
|
||||
#endif
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
#define CODE_INTERNAL 0
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
#define sDIM 3
|
||||
#define rDIM 1
|
||||
#define sDIM2 (sDIM*sDIM)
|
||||
#define rDIM2 (rDIM*rDIM)
|
||||
|
||||
struct findptsElementPoint_t
|
||||
{
|
||||
double x[sDIM], r, oldr, dist2, dist2p, tr;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct findptsElementGEdge_t
|
||||
{
|
||||
double *x[sDIM], *dxdn[sDIM], *d2xdn[sDIM];
|
||||
};
|
||||
|
||||
struct findptsElementGPT_t
|
||||
{
|
||||
double x[sDIM], jac[sDIM], hes[sDIM*(1+1)];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j=0; j<pN; ++j)
|
||||
{
|
||||
if (i!=j)
|
||||
{
|
||||
double d_j = 2 * (x-z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p1[i] = 2.0 * lCoeff[i] * u1;
|
||||
p2[i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside in all dimensions
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = obbox_axis_test(b, x);
|
||||
if (bxyz<0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dxyz[3];
|
||||
// dxyz: distance of the point from the center of the OBB
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
// transform dxyz to the local coordinate system of the OBB,
|
||||
// and check if the point is inside the OBB [-1,1]^sDIM
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*sDIM + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
static MFEM_HOST_DEVICE inline double norm2(const double x[sDIM])
|
||||
{
|
||||
return ( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
|
||||
}
|
||||
|
||||
/* the bit structure of flags is CRR
|
||||
the C bit --- 1<<2 --- is set when the point is converged
|
||||
RR is 0 = 00b if r is unconstrained,
|
||||
1 = 01b if r is constrained at -1, i.e., rmin
|
||||
2 = 10b if r is constrained at +1, i.e., rmax
|
||||
*/
|
||||
#define CONVERGED_FLAG (1u<<2)
|
||||
#define FLAG_MASK 0x07u
|
||||
|
||||
/* returns the number of constrained reference coordinates, max 2
|
||||
*/
|
||||
static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
|
||||
{
|
||||
const int y = (flags | flags>>1);
|
||||
return (y & 1u) + (y>>2 & 1u);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
{
|
||||
return ((x>>1)&1u) | ((x>>2)&2u);
|
||||
}
|
||||
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
const double resid[3],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = norm2(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
out->x[d] = p->x[d];
|
||||
}
|
||||
out->oldr = p->r;
|
||||
out->dist2 = dist2;
|
||||
if (decr>=0.01*pred)
|
||||
{
|
||||
if (decr>=0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = 2*p->tr;
|
||||
}
|
||||
else // good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else // if the iteration in not good
|
||||
{
|
||||
/* reject step; note: the point will pass through this routine
|
||||
again, and we set things up here so it gets classed as a
|
||||
"very good iteration" --- this doubles the trust radius,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r - p->oldr);
|
||||
out->tr = v0/4.0;
|
||||
out->dist2 = p->dist2;
|
||||
out->r = p->oldr;
|
||||
out->flags = p->flags>>3;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
if (pred<dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
|
||||
out,
|
||||
const double jac[sDIM*rDIM],
|
||||
const double rhes,
|
||||
const double resid[sDIM],
|
||||
int flags,
|
||||
const findptsElementPoint_t *const p,
|
||||
const double tol)
|
||||
{
|
||||
const double tr = p->tr;
|
||||
/* A = J^T J - resid_d H_d */
|
||||
const double A = jac[0]*jac[0]+ jac[1] * jac[1] + jac[2] * jac[2]
|
||||
- rhes;
|
||||
/* y = J^T r */
|
||||
const double y = jac[0]*resid[0] + jac[1]*resid[1] + jac[0+2]*resid[2];
|
||||
|
||||
const double oldr = p->r;
|
||||
double dr, nr, tdr, tnr;
|
||||
double v, tv;
|
||||
int new_flags = 0, tnew_flags = 0;
|
||||
|
||||
#define EVAL(dr) (dr*A - 2*y)*dr
|
||||
|
||||
/* if A is not SPD, quadratic model has no minimum */
|
||||
if (A>0)
|
||||
{
|
||||
dr = y/A;
|
||||
|
||||
if (fabs(dr)<tol)
|
||||
{
|
||||
dr=0.0;
|
||||
nr = oldr;
|
||||
}
|
||||
else
|
||||
{
|
||||
nr = oldr+dr;
|
||||
}
|
||||
if ( fabs(dr)<tr && fabs(nr)<1 )
|
||||
{
|
||||
v = EVAL(dr);
|
||||
goto newton_edge_fin;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (nr=oldr-tr)>-1 )
|
||||
{
|
||||
dr = -tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
nr = -1, dr = -1-oldr, new_flags = flags | 1u;
|
||||
}
|
||||
v = EVAL(dr);
|
||||
|
||||
if ( (tnr = oldr+tr)<1 )
|
||||
{
|
||||
tdr = tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
tnr = 1, tdr = 1-oldr, tnew_flags = flags | 2u;
|
||||
}
|
||||
tv = EVAL(tdr);
|
||||
|
||||
if (tv<v)
|
||||
{
|
||||
nr = tnr, dr = tdr, v = tv, new_flags = tnew_flags;
|
||||
}
|
||||
|
||||
newton_edge_fin:
|
||||
/* check convergence */
|
||||
if ( fabs(dr)<tol )
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r = nr;
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
#undef EVAL
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
|
||||
const double x[sDIM],
|
||||
const double *z,
|
||||
double *dist2,
|
||||
double *r,
|
||||
const int ir,
|
||||
const int pN)
|
||||
{
|
||||
if (ir>=pN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double dx[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dx[d] = x[d] - elx[d][ir];
|
||||
}
|
||||
dist2[ir] = norm2(dx);;
|
||||
r[ir] = z[ir];
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsEdgeLocal3D_Kernel(const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_NEL = nel*D1D;
|
||||
MFEM_VERIFY(MD1<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D!=0, "Polynomial order not specified.");
|
||||
const int nThreads = D1D*sDIM;
|
||||
|
||||
mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
constexpr int size1 = 3*MD1 + 13;
|
||||
constexpr int size2 = 3*MD1;
|
||||
constexpr int size3 = MD1*sDIM;
|
||||
|
||||
MFEM_SHARED findptsElementPoint_t el_pts[2];
|
||||
MFEM_SHARED double r_workspace[size1];
|
||||
|
||||
MFEM_SHARED double constraint_workspace[size2];
|
||||
|
||||
MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
|
||||
|
||||
double *r_workspace_ptr = r_workspace;
|
||||
findptsElementPoint_t *fpt, *tmp;
|
||||
fpt = el_pts + 0;
|
||||
tmp = el_pts + 1;
|
||||
|
||||
int id_x = point_pos_ordering==0 ? i : i*sDIM;
|
||||
int id_y = point_pos_ordering==0 ? npt+i : 1+i*sDIM;
|
||||
int id_z = point_pos_ordering==0 ? 2*npt+i : 2+i*sDIM;
|
||||
double x_i[3] = {x[id_x], x[id_y], x[id_z]};
|
||||
|
||||
unsigned int *code_i = code_base + i;
|
||||
double *dist2_i = dist2_base + i;
|
||||
|
||||
//// map_points_to_els ////
|
||||
findptsLocalHashData_t hash;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
hash.bnd[d].min = hashMin[d];
|
||||
hash.fac[d] = hashFac[d];
|
||||
}
|
||||
hash.hash_n = hash_n;
|
||||
hash.offset = hashOffset;
|
||||
|
||||
const unsigned int hi = hash_index(&hash, x_i);
|
||||
const unsigned int *elp = hash.offset + hash.offset[hi];
|
||||
const unsigned int *const ele = hash.offset + hash.offset[hi+1];
|
||||
*code_i = CODE_NOT_FOUND;
|
||||
*dist2_i = HUGE_VAL;
|
||||
|
||||
for (; elp!=ele; ++elp)
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
|
||||
if (obbox_test(&box, x_i)>=0)
|
||||
{
|
||||
//// findpts_local ////
|
||||
{
|
||||
if (MD1 <= 6)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
|
||||
{
|
||||
const int qp = j % D1D;
|
||||
const int d = j / D1D;
|
||||
elem_coords[qp + d*D1D] =
|
||||
xElemCoord[qp + el*D1D + d*p_NEL];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
const double *elx[sDIM];
|
||||
for (int d=0; d<sDIM; d++)
|
||||
{
|
||||
elx[d] = MD1<= 6 ? &elem_coords[d*D1D] :
|
||||
xElemCoord + d*p_NEL + el*D1D;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
//// findpts_el ////
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
fpt->dist2 = HUGE_VAL;
|
||||
fpt->dist2p = 0;
|
||||
fpt->tr = 1.0;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
fpt->x[j] = x_i[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
//// seed ////
|
||||
{
|
||||
double *dist2_temp = r_workspace_ptr;
|
||||
double *r_temp = dist2_temp + D1D;
|
||||
MFEM_FOREACH_THREAD(j,x,nThreads)
|
||||
{
|
||||
seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
fpt->dist2 = HUGE_VAL;
|
||||
for (int ir=0; ir<D1D; ++ir)
|
||||
{
|
||||
if (dist2_temp[ir] < fpt->dist2)
|
||||
{
|
||||
fpt->dist2 = dist2_temp[ir];
|
||||
fpt->r = r_temp[ir];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} //seed done
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
tmp->dist2 = HUGE_VAL;
|
||||
tmp->dist2p = 0;
|
||||
tmp->tr = 1;
|
||||
tmp->flags = 0;
|
||||
tmp->r = fpt->r;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
tmp->x[j] = fpt->x[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int step=0; step<50; step++)
|
||||
{
|
||||
switch (num_constrained(tmp->flags & FLAG_MASK))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
double *wt = r_workspace_ptr;
|
||||
double *resid = wt + 3*D1D;
|
||||
double *jac = resid + sDIM;
|
||||
double *hess = jac + sDIM*rDIM;
|
||||
|
||||
findptsElementGEdge_t edge;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.x[d][j] = elx[d][j];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
lag_eval_second_der(wt, tmp->r, j, gll1D,
|
||||
lagcoeff, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
resid[j] = tmp->x[j];
|
||||
jac[j] = 0.0;
|
||||
hess[j] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
resid[j] -= wt[ k]*edge.x[j][k];
|
||||
jac[j] += wt[D1D+k]*edge.x[j][k];
|
||||
hess[j] += wt[2*D1D+k]*edge.x[j][k];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
hess[3] = resid[0]*hess[0] + resid[1]*hess[1] +
|
||||
resid[2]*hess[2];
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(l,x,1)
|
||||
{
|
||||
if (!reject_prior_step_q(fpt,resid,tmp,tol))
|
||||
{
|
||||
newton_edge(fpt,jac,hess[3],resid,
|
||||
tmp->flags&FLAG_MASK,tmp,tol);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
const int pi = point_index(tmp->flags &
|
||||
FLAG_MASK);
|
||||
const double *wt = wtend + pi*3*D1D;
|
||||
findptsElementGPT_t gpt;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
gpt.x[d] = elx[d][pi*(D1D-1)];
|
||||
gpt.jac[d] = 0.0;
|
||||
gpt.hes[d] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
gpt.jac[d] += wt[D1D +k]*elx[d][k];
|
||||
gpt.hes[d] += wt[2*D1D+k]*elx[d][k];
|
||||
}
|
||||
}
|
||||
|
||||
const double *const pt_x = gpt.x;
|
||||
const double *const jac = gpt.jac;
|
||||
const double *const hes = gpt.hes;
|
||||
double resid[sDIM], steep, sr;
|
||||
resid[0] = fpt->x[0] - pt_x[0];
|
||||
resid[1] = fpt->x[1] - pt_x[1];
|
||||
resid[2] = fpt->x[2] - pt_x[2];
|
||||
steep = jac[0]*resid[0] + jac[1]*resid[1] +
|
||||
jac[2]*resid[2];
|
||||
sr = steep*tmp->r;
|
||||
if (!reject_prior_step_q(fpt, resid, tmp, tol))
|
||||
{
|
||||
if (sr<0)
|
||||
{
|
||||
const double rhess = resid[0]*hes[0] +
|
||||
resid[1]*hes[1] +
|
||||
resid[2]*hes[2];
|
||||
newton_edge(fpt, jac, rhess,
|
||||
resid, 0, tmp, tol);
|
||||
}
|
||||
else // sr==0
|
||||
{
|
||||
fpt->r = tmp->r;
|
||||
fpt->dist2p = 0;
|
||||
fpt->flags = tmp->flags | CONVERGED_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
} // case 1
|
||||
} //switch
|
||||
if (fpt->flags & CONVERGED_FLAG)
|
||||
{
|
||||
break;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*tmp = *fpt;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} // for step<50
|
||||
} // findpts_el
|
||||
|
||||
bool converged_internal =
|
||||
((fpt->flags&FLAG_MASK) == CONVERGED_FLAG) &&
|
||||
(fpt->dist2<dist2tol);
|
||||
if (*code_i==CODE_NOT_FOUND || converged_internal ||
|
||||
fpt->dist2<*dist2_i)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*(el_base+i) = el;
|
||||
*code_i = converged_internal?CODE_INTERNAL:CODE_BORDER;
|
||||
*dist2_i = fpt->dist2;
|
||||
*(r_base+i) = fpt->r;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (converged_internal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // findpts_local
|
||||
} // obbox_test
|
||||
} // elp
|
||||
});
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt)
|
||||
{
|
||||
if (npt == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MFEM_VERIFY(spacedim==3 && dim == 1,"Function for 3D edges only");
|
||||
bool use_dev = point_pos.UseDevice();
|
||||
auto pp = point_pos.Read(use_dev);
|
||||
auto pgslm = gsl_mesh.Read(use_dev);
|
||||
auto pwt = DEV.wtend.Read(use_dev);
|
||||
auto pbb = DEV.bb.Read(use_dev);
|
||||
auto plhm = DEV.lh_min.Read(use_dev);
|
||||
auto plhf = DEV.lh_fac.Read(use_dev);
|
||||
auto plho = DEV.lh_offset.ReadWrite(use_dev);
|
||||
auto pcode = code.Write(use_dev);
|
||||
auto pelem = elem.Write(use_dev);
|
||||
auto pref = ref.Write(use_dev);
|
||||
auto pdist = dist.Write(use_dev);
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsEdgeLocal3D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 3:
|
||||
return FindPointsEdgeLocal3D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 4:
|
||||
return FindPointsEdgeLocal3D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
default:
|
||||
return FindPointsEdgeLocal3D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
}
|
||||
}
|
||||
#undef rDIM2
|
||||
#undef sDIM2
|
||||
#undef rDIM
|
||||
#undef sDIM
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
#else
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal3( const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt ) {} ;
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
#endif //ifdef MFEM_USE_GSLIB
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,157 +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 "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#include "gslib.h"
|
||||
#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
|
||||
#define GSLIB_RELEASE_VERSION 10007
|
||||
#endif
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
#define CODE_INTERNAL 0
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
|
||||
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
|
||||
int i, int p_Nq,
|
||||
double *z, double *lagrangeCoeff)
|
||||
{
|
||||
double p_i = (1 << (p_Nq - 1));
|
||||
for (int j=0; j<p_Nq; ++j)
|
||||
{
|
||||
p_i *= j==i ? 1 : x-z[j];
|
||||
}
|
||||
p0[i] = lagrangeCoeff[i] * p_i;
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void InterpolateLocal1DKernel(const double *const gf_in,
|
||||
int *const el,
|
||||
double *const r,
|
||||
double *const int_out,
|
||||
const int npt,
|
||||
const int nfields,
|
||||
double *gll1D,
|
||||
double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_Nq = D1D;
|
||||
MFEM_VERIFY(MD1 <= DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D != 0, "Polynomial order not specified.");
|
||||
// for each point of the npt points, create a thread block of size dof1Dsol
|
||||
mfem::forall_2D(npt, D1D, 1, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
MFEM_SHARED double wtr[MD1];
|
||||
MFEM_SHARED double sums[MD1];
|
||||
|
||||
// Evaluate basis functions at the reference space coordinates
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
lagrange_eval(wtr, r[i], j, p_Nq, gll1D, lagcoeff);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int fld=0; fld<nfields; ++fld)
|
||||
{
|
||||
// If using GetNodalValues, ordering is NDOFS x NEL x VDIM and the
|
||||
// offset would be `el[i] * p_Nq + fld * gf_offset`.
|
||||
// R->Mult produces element vectors in NDOFS x VDIM x NEL layout.
|
||||
const int elemOffset = el[i]*nfields*p_Nq + fld*p_Nq;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
sums[j] = wtr[j] * gf_in[elemOffset + j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
double sumv = 0.0;
|
||||
// sum the contributions of each lagrange polynomial
|
||||
for (int jj=0; jj<D1D; ++jj)
|
||||
{
|
||||
sumv += sums[jj];
|
||||
}
|
||||
int_out[fld*npt + i] = sumv;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateLocal1( const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt,
|
||||
int ncomp,
|
||||
int dof1Dsol )
|
||||
{
|
||||
MFEM_VERIFY(dim == 1, "Kernel for edges only.");
|
||||
if (npt == 0) { return; }
|
||||
bool use_dev = field_in.UseDevice();
|
||||
auto pfin = field_in.Read(use_dev);
|
||||
auto pgsl = gsl_elem_dev_l.ReadWrite(use_dev);
|
||||
auto pgslr = gsl_ref_l.ReadWrite(use_dev);
|
||||
auto pfout = field_out.Write(use_dev);
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite(use_dev);
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal1DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal1DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal1DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal1DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal1DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
}
|
||||
}
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
#else
|
||||
void FindPointsGSLIB::InterpolateLocal1(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1Dsol) {};
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif //ifdef MFEM_USE_GSLIB
|
||||
@@ -52,6 +52,8 @@ static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
double *const int_out,
|
||||
const int npt,
|
||||
const int ncomp,
|
||||
const int nel,
|
||||
const int gf_offset,
|
||||
double *gll1D,
|
||||
double *lagcoeff,
|
||||
const int pN = 0)
|
||||
@@ -62,8 +64,6 @@ static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
const int p_Np = D1D*D1D;
|
||||
MFEM_VERIFY(MD1 <= DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D != 0, "Polynomial order not specified.");
|
||||
mfem::forall_2D(npt, D1D, D1D, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
@@ -82,9 +82,9 @@ static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
|
||||
for (int fld = 0; fld < Nfields; ++fld)
|
||||
{
|
||||
// If using GetNodalValues, ordering is NDOFS x NEL x VDIM and the
|
||||
// offset would be `el[i] * p_Np + fld * gf_offset`.
|
||||
// R->Mult produces element vectors in NDOFS x VDIM x NEL layout.
|
||||
// If using GetNodalValues, ordering is NDOFSxNELxVDIM
|
||||
// const int elemOffset = el[i] * p_Np + fld * gf_offset;
|
||||
//if using R->Mult for L -> E-Vec use below: NDOFSxVDIMxNEL
|
||||
const int elemOffset = el[i] * p_Np * Nfields + fld * p_Np;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
@@ -120,32 +120,32 @@ void FindPointsGSLIB::InterpolateLocal2(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1Dsol)
|
||||
int nel, int dof1Dsol)
|
||||
{
|
||||
if (npt == 0) { return; }
|
||||
bool use_dev = field_in.UseDevice();
|
||||
auto pfin = field_in.Read(use_dev);
|
||||
auto pgsl = gsl_elem_dev_l.ReadWrite(use_dev);
|
||||
auto pgslr = gsl_ref_l.ReadWrite(use_dev);
|
||||
auto pfout = field_out.Write(use_dev);
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite(use_dev);
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
const int gf_offset = field_in.Size()/ncomp;
|
||||
auto pfin = field_in.Read();
|
||||
auto pgsl = gsl_elem_dev_l.ReadWrite();
|
||||
auto pgslr = gsl_ref_l.ReadWrite();
|
||||
auto pfout = field_out.Write();
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite();
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite();
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal2DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal2DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal2DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal2DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal2DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf, dof1Dsol);
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ void FindPointsGSLIB::InterpolateLocal2(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1Dsol) {};
|
||||
int nel, int dof1Dsol) {};
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ static void InterpolateLocal3DKernel(const double *const gf_in,
|
||||
double *const int_out,
|
||||
const int npt,
|
||||
const int ncomp,
|
||||
const int nel,
|
||||
const int gf_offset,
|
||||
double *gll1D,
|
||||
double *lagcoeff,
|
||||
const int pN = 0)
|
||||
@@ -82,9 +84,9 @@ static void InterpolateLocal3DKernel(const double *const gf_in,
|
||||
|
||||
for (int fld = 0; fld < Nfields; ++fld)
|
||||
{
|
||||
// If using GetNodalValues, ordering is NDOFS x NEL x VDIM and the
|
||||
// offset would be `el[i] * p_Np + fld * gf_offset`.
|
||||
// R->Mult produces element vectors in NDOFS x VDIM x NEL layout.
|
||||
// If using GetNodalValues, ordering is NDOFSxNELxVDIM
|
||||
// const int elemOffset = el[i] * p_Np + fld * gf_offset;
|
||||
//if using R->Mult for L -> E-Vec use below.
|
||||
const int elemOffset = el[i] * p_Np * Nfields + fld * p_Np;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
@@ -123,38 +125,37 @@ void FindPointsGSLIB::InterpolateLocal3(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1Dsol)
|
||||
int nel, int dof1Dsol)
|
||||
{
|
||||
if (npt == 0) { return; }
|
||||
bool use_dev = field_in.UseDevice();
|
||||
auto pfin = field_in.Read(use_dev);
|
||||
auto pgsle = gsl_elem_dev_l.ReadWrite(use_dev);
|
||||
auto pgslr = gsl_ref_l.ReadWrite(use_dev);
|
||||
auto pfout = field_out.Write(use_dev);
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite(use_dev);
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
const int gf_offset = field_in.Size()/ncomp;
|
||||
auto pfin = field_in.Read();
|
||||
auto pgsle = gsl_elem_dev_l.ReadWrite();
|
||||
auto pgslr = gsl_ref_l.ReadWrite();
|
||||
auto pfout = field_out.Write();
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite();
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite();
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal3DKernel<2>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal3DKernel<3>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal3DKernel<4>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal3DKernel<5>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal3DKernel(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
pgll, plcf, dof1Dsol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef MAXC
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
@@ -164,7 +165,7 @@ void FindPointsGSLIB::InterpolateLocal3(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1Dsol) {};
|
||||
int nel, int dof1Dsol) {};
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "bilininteg_diffusion_kernels.hpp"
|
||||
#include "bilininteg_diffusion_pa_simplices.hpp" // IWYU pragma: keep
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -20,13 +19,6 @@ namespace mfem
|
||||
DiffusionIntegrator::Kernels::Kernels()
|
||||
{
|
||||
// 2D
|
||||
// Q = P, only for simplex
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,2,1>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,3,2>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,4,3>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,5,4>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,6,5>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,7,6>();
|
||||
// Q = P+1
|
||||
DiffusionIntegrator::AddSpecialization<2,1,1>();
|
||||
DiffusionIntegrator::AddSpecialization<2,2,2>();
|
||||
@@ -48,18 +40,7 @@ DiffusionIntegrator::Kernels::Kernels()
|
||||
DiffusionIntegrator::AddSpecialization<2,8,9>();
|
||||
DiffusionIntegrator::AddSpecialization<2,9,10>();
|
||||
// others
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,2,5>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<2,3,6>();
|
||||
|
||||
// 3D
|
||||
// Q = P, only for simplex
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,2,1>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,3,2>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,4,3>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,5,4>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,6,5>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,7,6>();
|
||||
DiffusionIntegrator::AddSimplexSpecialization<3,8,7>();
|
||||
// Q = P+1
|
||||
DiffusionIntegrator::AddSpecialization<3,1,1>();
|
||||
DiffusionIntegrator::AddSpecialization<3,2,2>();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#ifndef MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
|
||||
#define MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
|
||||
|
||||
#include "../kernel_dispatch.hpp"
|
||||
#include "../../config/config.hpp"
|
||||
#include "../../general/array.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
@@ -19,8 +20,6 @@
|
||||
#include "../../linalg/vector.hpp"
|
||||
#include "../bilininteg.hpp"
|
||||
|
||||
#include "bilininteg_diffusion_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -638,8 +637,8 @@ inline void SmemPADiffusionApply2D(const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &b_,
|
||||
const Array<real_t> &g_,
|
||||
const Array<real_t> &,
|
||||
const Array<real_t> &,
|
||||
const Array<real_t> &bt_,
|
||||
const Array<real_t> >_,
|
||||
const Vector &d_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
@@ -1219,47 +1218,43 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
namespace
|
||||
{
|
||||
using ApplyKernelType = DiffusionIntegrator::ApplyKernelType;
|
||||
using ApplySimplexKernelType = DiffusionIntegrator::ApplySimplexKernelType;
|
||||
using DiagonalKernelType = DiffusionIntegrator::DiagonalKernelType;
|
||||
}
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
inline
|
||||
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int dim, int, int)
|
||||
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int DIM, int, int)
|
||||
{
|
||||
if (dim == 2) { return internal::PADiffusionApply2D; }
|
||||
else if (dim == 3) { return internal::PADiffusionApply3D; }
|
||||
if (DIM == 2) { return internal::PADiffusionApply2D; }
|
||||
else if (DIM == 3) { return internal::PADiffusionApply3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
DiagonalKernelType DiffusionIntegrator::DiagonalPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D, Q1D>; }
|
||||
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D,Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPADiffusionDiagonal3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
inline DiagonalKernelType
|
||||
DiffusionIntegrator::DiagonalPAKernels::Fallback(int dim, int, int)
|
||||
DiffusionIntegrator::DiagonalPAKernels::Fallback(int DIM, int, int)
|
||||
{
|
||||
if (dim == 2) { return internal::PADiffusionDiagonal2D; }
|
||||
else if (dim == 3) { return internal::PADiffusionDiagonal3D; }
|
||||
if (DIM == 2) { return internal::PADiffusionDiagonal2D; }
|
||||
else if (DIM == 3) { return internal::PADiffusionDiagonal3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../../mesh/nurbs.hpp"
|
||||
#include "../ceed/integrators/diffusion/diffusion.hpp"
|
||||
#include "bilininteg_diffusion_kernels.hpp"
|
||||
#include "bilininteg_diffusion_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -69,24 +68,6 @@ void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
#endif // MFEM_USE_OCCA
|
||||
|
||||
if (fespace->UsesRaggedTensorBasis())
|
||||
{
|
||||
const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
|
||||
return ApplySimplexPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
|
||||
rmaps->lex_map,
|
||||
rmaps->forward_map2d_diff,
|
||||
rmaps->inverse_map2d_diff,
|
||||
rmaps->forward_map3d_diff,
|
||||
rmaps->inverse_map3d_diff,
|
||||
rmaps->Ga1,
|
||||
rmaps->Ga2,
|
||||
rmaps->Ga3,
|
||||
rmaps->Ga1t,
|
||||
rmaps->Ga2t,
|
||||
rmaps->Ga3t,
|
||||
Dv, x, y, dofs1D, quad1D);
|
||||
}
|
||||
|
||||
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
|
||||
Gt, Dv, x, y, dofs1D, quad1D);
|
||||
}
|
||||
@@ -113,8 +94,7 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
fespace = &fes;
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
const FiniteElement &el = *fes.GetTypicalFE();
|
||||
const bool stroud = fes.UsesRaggedTensorBasis();
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, stroud);
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
@@ -139,22 +119,13 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
dim = mesh->Dimension();
|
||||
ne = fes.GetNE();
|
||||
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
|
||||
if (stroud)
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
}
|
||||
const int sdim = mesh->SpaceDimension();
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
|
||||
QuadratureSpace qs(*mesh, *ir);
|
||||
CoefficientVector coeff(qs, CoefficientStorage::COMPRESSED);
|
||||
// QuadratureSpace expects ir defined in reference simplex for Bernstein
|
||||
// elements with partial assembly
|
||||
|
||||
if (MQ) { coeff.ProjectTranspose(*MQ); }
|
||||
else if (VQ) { coeff.Project(*VQ); }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -147,15 +147,18 @@ void PAHcurlMassAssembleDiagonal3D(const int D1D,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
void PAHcurlMassApply2D(const int NE, const bool symmetric,
|
||||
const bool scalar_coeff, const Array<real_t> &bo,
|
||||
const Array<real_t> &bc, const Array<real_t> &bot,
|
||||
const Array<real_t> &bct, const Vector &pa_data,
|
||||
const Vector &x, Vector &y, const int D1D,
|
||||
const int TestD1D, const int Q1D)
|
||||
void PAHcurlMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bot,
|
||||
const Array<real_t> &bct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
MFEM_ASSERT(D1D == TestD1D,
|
||||
"Trial and Test space must have the same number of dofs");
|
||||
auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
|
||||
auto Bc = Reshape(bc.Read(), Q1D, D1D);
|
||||
auto Bot = Reshape(bot.Read(), D1D-1, Q1D);
|
||||
@@ -274,15 +277,18 @@ void PAHcurlMassApply2D(const int NE, const bool symmetric,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
void PAHcurlMassApply3D(const int NE, const bool symmetric,
|
||||
const bool scalar_coeff, const Array<real_t> &bo,
|
||||
const Array<real_t> &bc, const Array<real_t> &bot,
|
||||
const Array<real_t> &bct, const Vector &pa_data,
|
||||
const Vector &x, Vector &y, const int D1D,
|
||||
const int TestD1D, const int Q1D)
|
||||
void PAHcurlMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bot,
|
||||
const Array<real_t> &bct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
MFEM_VERIFY(D1D == TestD1D,
|
||||
"Trial and test spaces must have same number of dofs");
|
||||
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
|
||||
"Error: D1D > MAX_D1D");
|
||||
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
|
||||
|
||||
@@ -181,309 +181,228 @@ inline void SmemPAHcurlMassAssembleDiagonal3D(const int d1d,
|
||||
}
|
||||
|
||||
// PA H(curl) Mass Apply 2D kernel
|
||||
void PAHcurlMassApply2D(const int NE, const bool symmetric,
|
||||
const bool scalar_coeff, const Array<real_t> &bo,
|
||||
const Array<real_t> &bc, const Array<real_t> &bot,
|
||||
const Array<real_t> &bct, const Vector &pa_data,
|
||||
const Vector &x, Vector &y, const int TrialD1D,
|
||||
const int TestD1D, const int Q1D);
|
||||
void PAHcurlMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bot,
|
||||
const Array<real_t> &bct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y);
|
||||
|
||||
// PA H(curl) Mass Apply 3D kernel
|
||||
void PAHcurlMassApply3D(const int NE, const bool symmetric,
|
||||
const bool scalar_coeff, const Array<real_t> &bo,
|
||||
const Array<real_t> &bc, const Array<real_t> &bot,
|
||||
const Array<real_t> &bct, const Vector &pa_data,
|
||||
const Vector &x, Vector &y, const int TrialD1D,
|
||||
const int TestD1D, const int Q1D);
|
||||
void PAHcurlMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bot,
|
||||
const Array<real_t> &bct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y);
|
||||
|
||||
// Shared memory PA H(curl) Mass Apply 3D kernel
|
||||
template <int T_D1D = 0, int T_Q1D = 0, int TBATCH = 0, bool ACCUMULATE = true>
|
||||
inline void SmemPAHcurlMassApply3D(
|
||||
const int NE, const bool symmetric, const bool scalar_coeff,
|
||||
const Array<real_t> &bo, const Array<real_t> &bc, const Array<real_t> &bot,
|
||||
const Array<real_t> &bct, const Vector &pa_data, const Vector &x, Vector &y,
|
||||
const int d1d = 0, const int = 0, const int q1d = 0)
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void SmemPAHcurlMassApply3D(const int d1d,
|
||||
const int q1d,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bot,
|
||||
const Array<real_t> &bct,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_VERIFY(T_D1D || d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
|
||||
"Error: d1d > HCURL_MAX_D1D");
|
||||
MFEM_VERIFY(T_Q1D || q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
|
||||
"Error: q1d > HCURL_MAX_Q1D");
|
||||
MFEM_ASSERT(Q1D >= D1D, "Expected Q1D >= D1D");
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const int dataSize = symmetric ? 6 : 9;
|
||||
|
||||
// assume trial space == test space
|
||||
auto Bo = bo.Read();
|
||||
auto Bc = bc.Read();
|
||||
auto op =
|
||||
Reshape(pa_data.Read(), Q1D, Q1D, Q1D, dataSize, NE);
|
||||
auto X_ = Reshape(x.Read(), 3 * (D1D - 1) * D1D * D1D, NE);
|
||||
auto y_ = y.ReadWrite();
|
||||
auto Bo = Reshape(bo.Read(), Q1D, D1D-1);
|
||||
auto Bc = Reshape(bc.Read(), Q1D, D1D);
|
||||
auto op = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, dataSize, NE);
|
||||
auto X = Reshape(x.Read(), 3*(D1D-1)*D1D*D1D, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), 3*(D1D-1)*D1D*D1D, NE);
|
||||
|
||||
constexpr int MD_ = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
|
||||
constexpr int MQ_ = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
|
||||
constexpr int MDQ_ = std::max(MD_, MQ_);
|
||||
constexpr int MB_ = TBATCH ? TBATCH : 1;
|
||||
|
||||
mfem::forall_2D_batch<MDQ_ * MDQ_ * MDQ_ * MB_>(
|
||||
NE, MDQ_ * MDQ_ * MDQ_, 1, MB_, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
|
||||
constexpr int nbz = TBATCH ? TBATCH : 1;
|
||||
int tidz = MFEM_THREAD_ID(z);
|
||||
#else
|
||||
constexpr int nbz = 1;
|
||||
constexpr int tidz = 0;
|
||||
#endif
|
||||
|
||||
constexpr int VDIM = 3;
|
||||
constexpr int MD1D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
|
||||
constexpr int MQ1D = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
|
||||
constexpr int MDQ = std::max(MD1D, MQ1D);
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
// nvcc limit work-around: can't have Y_ be captured first in
|
||||
// if constexpr, so capture y_ and construct Y_ locally
|
||||
// only works on GPU
|
||||
auto Y = Reshape(y_, VDIM * (D1D - 1) * D1D * D1D, NE);
|
||||
MFEM_SHARED real_t sBo[MQ1D][MD1D];
|
||||
MFEM_SHARED real_t sBc[MQ1D][MD1D];
|
||||
|
||||
MFEM_SHARED real_t sBo[MDQ * (MD1D - 1)];
|
||||
MFEM_SHARED real_t sBc[MDQ * MD1D];
|
||||
auto BO = Reshape(sBo, Q1D, D1D - 1);
|
||||
auto BC = Reshape(sBc, Q1D, D1D);
|
||||
real_t op9[9];
|
||||
MFEM_SHARED real_t sop[9*MQ1D*MQ1D];
|
||||
MFEM_SHARED real_t mass[MQ1D][MQ1D][3];
|
||||
|
||||
MFEM_SHARED real_t sX[nbz * VDIM * (MD1D - 1) * MD1D * MD1D];
|
||||
MFEM_SHARED real_t sm0[nbz * VDIM * MDQ * MDQ * MDQ];
|
||||
MFEM_SHARED real_t sm1[nbz * VDIM * MDQ * MDQ * MDQ];
|
||||
MFEM_SHARED real_t sX[MD1D][MD1D][MD1D];
|
||||
|
||||
real_t(*X)[nbz][(MD1D - 1) * MD1D * MD1D] =
|
||||
(real_t(*)[nbz][(MD1D - 1) * MD1D * MD1D])(sX);
|
||||
// shapes of buffers always use MQ1D to mitigate shared memory bank
|
||||
// conflicts
|
||||
real_t(*DDQ)[nbz][MQ1D][MQ1D][MQ1D] =
|
||||
(real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm0);
|
||||
real_t(*DQQ)[nbz][MQ1D][MQ1D][MQ1D] =
|
||||
(real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm1);
|
||||
real_t(*QQQ)[nbz][MQ1D][MQ1D][MQ1D] =
|
||||
(real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm0);
|
||||
real_t(*QQD)[nbz][MQ1D][MQ1D][MQ1D] =
|
||||
(real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm1);
|
||||
real_t(*QDD)[nbz][MQ1D][MQ1D][MQ1D] =
|
||||
(real_t(*)[nbz][MQ1D][MQ1D][MQ1D])(sm0);
|
||||
|
||||
// load dofs into smem
|
||||
const int offset = (D1D - 1) * D1D * D1D;
|
||||
MFEM_FOREACH_THREAD_DIRECT(ix, x, offset)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
for (int dim = 0; dim < VDIM; ++dim)
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
X[dim][tidz][ix] = X_(ix + dim * offset, e);
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
for (int i=0; i<dataSize; ++i)
|
||||
{
|
||||
op9[i] = op(qx,qy,qz,i,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// load basis functions data
|
||||
|
||||
const int tidx = MFEM_THREAD_ID(x);
|
||||
const int tidy = MFEM_THREAD_ID(y);
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(ix, x, D1D * Q1D) { sBc[ix] = Bc[ix]; }
|
||||
MFEM_FOREACH_THREAD_DIRECT(ix, x, (D1D - 1) * Q1D)
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
sBo[ix] = Bo[ix];
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
sBc[q][d] = Bc(q,d);
|
||||
if (d < D1D-1)
|
||||
{
|
||||
sBo[q][d] = Bo(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int dim0 = 0; dim0 < VDIM; ++dim0)
|
||||
for (int qz=0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_SYNC_THREAD;
|
||||
// sum factor to QQQ = Q_{dim0,dim1} B X_{dim1}
|
||||
for (int dim1 = 0; dim1 < VDIM; ++dim1)
|
||||
int osc = 0;
|
||||
for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
|
||||
{
|
||||
const int D1Dz = (dim1 == 2) ? D1D - 1 : D1D;
|
||||
const int D1Dy = (dim1 == 1) ? D1D - 1 : D1D;
|
||||
const int D1Dx = (dim1 == 0) ? D1D - 1 : D1D;
|
||||
const int D1Dz = (c == 2) ? D1D - 1 : D1D;
|
||||
const int D1Dy = (c == 1) ? D1D - 1 : D1D;
|
||||
const int D1Dx = (c == 0) ? D1D - 1 : D1D;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, Q1D, D1Dy, D1Dz,
|
||||
Q1D, Q1D, Q1D)
|
||||
MFEM_FOREACH_THREAD(dz,z,D1Dz)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < D1Dx; ++dx)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1Dy)
|
||||
{
|
||||
real_t b;
|
||||
if (dim1 == 0)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1Dx)
|
||||
{
|
||||
b = BO(qx, dx);
|
||||
sX[dz][dy][dx] = X(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BC(qx, dx);
|
||||
}
|
||||
u += X[dim1][tidz][dx + (dy + dz * D1Dy) * D1Dx] * b;
|
||||
}
|
||||
DDQ[dim1][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
for (int dim1 = 0; dim1 < VDIM; ++dim1)
|
||||
{
|
||||
const int D1Dz = (dim1 == 2) ? D1D - 1 : D1D;
|
||||
const int D1Dy = (dim1 == 1) ? D1D - 1 : D1D;
|
||||
// const int D1Dx = (dim1 == 0) ? D1D - 1 : D1D;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, Q1D, Q1D, D1Dz,
|
||||
Q1D, Q1D, Q1D)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < D1Dy; ++dy)
|
||||
{
|
||||
real_t b;
|
||||
if (dim1 == 1)
|
||||
{
|
||||
b = BO(qy, dy);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BC(qy, dy);
|
||||
}
|
||||
u += DDQ[dim1][tidz][dz][dy][qx] * b;
|
||||
}
|
||||
DQQ[dim1][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
for (int dim1 = 0; dim1 < VDIM; ++dim1)
|
||||
{
|
||||
const int D1Dz = (dim1 == 2) ? D1D - 1 : D1D;
|
||||
// const int D1Dy = (dim1 == 1) ? D1D - 1 : D1D;
|
||||
// const int D1Dx = (dim1 == 0) ? D1D - 1 : D1D;
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D(qx, qy, qz, x, Q1D, Q1D, Q1D)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < D1Dz; ++dz)
|
||||
{
|
||||
real_t b;
|
||||
if (dim1 == 2)
|
||||
{
|
||||
b = BO(qz, dz);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BC(qz, dz);
|
||||
}
|
||||
u += DQQ[dim1][tidz][dz][qy][qx] * b;
|
||||
}
|
||||
// pa_data is row major
|
||||
int idx;
|
||||
if (symmetric)
|
||||
{
|
||||
int row;
|
||||
int col;
|
||||
if (dim0 > dim1)
|
||||
{
|
||||
row = dim1;
|
||||
col = dim0;
|
||||
}
|
||||
else
|
||||
{
|
||||
row = dim0;
|
||||
col = dim1;
|
||||
}
|
||||
idx = col + VDIM * row - row * (row + 1) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = dim0 * VDIM + dim1;
|
||||
}
|
||||
QQQ[dim1][tidz][qz][qy][qx] = op(qx, qy, qz, idx, e) * u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// sum factor back to Y
|
||||
// Assume bot and bct == bo^t and bc^t respectively (i.e. test ==
|
||||
// trial functions), skip loading them again.
|
||||
{
|
||||
const int D1Dz = (dim0 == 2) ? D1D - 1 : D1D;
|
||||
const int D1Dy = (dim0 == 1) ? D1D - 1 : D1D;
|
||||
const int D1Dx = (dim0 == 0) ? D1D - 1 : D1D;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, D1Dz, Q1D, Q1D,
|
||||
Q1D, Q1D, Q1D)
|
||||
{
|
||||
for (int dim1 = 0; dim1 < VDIM; ++dim1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
real_t b = 0;
|
||||
if (dim0 == 2)
|
||||
{
|
||||
b = BO(qz, dz);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BC(qz, dz);
|
||||
}
|
||||
u += QQQ[dim1][tidz][qz][qy][qx] * b;
|
||||
}
|
||||
QQD[dim1][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, D1Dy, D1Dz, Q1D,
|
||||
Q1D, Q1D, Q1D)
|
||||
{
|
||||
for (int dim1 = 0; dim1 < VDIM; ++dim1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
real_t b;
|
||||
if (dim0 == 1)
|
||||
{
|
||||
b = BO(qy, dy);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BC(qy, dy);
|
||||
}
|
||||
u += QQD[dim1][tidz][qy][qx][dz] * b;
|
||||
}
|
||||
QDD[dim1][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D(dx, dy, dz, x, D1Dx, D1Dy, D1Dz)
|
||||
if (tidz == qz)
|
||||
{
|
||||
int ix = dx + D1Dx * (dy + D1Dy * dz);
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
for (int i=0; i<dataSize; ++i)
|
||||
{
|
||||
real_t b;
|
||||
if (dim0 == 0)
|
||||
{
|
||||
b = BO(qx, dx);
|
||||
}
|
||||
else
|
||||
{
|
||||
b = BC(qx, dx);
|
||||
}
|
||||
for (int dim1 = 0; dim1 < VDIM; ++dim1)
|
||||
{
|
||||
u += QDD[dim1][tidz][qx][dz][dy] * b;
|
||||
}
|
||||
sop[i + (dataSize*tidx) + (dataSize*Q1D*tidy)] = op9[i];
|
||||
}
|
||||
if constexpr (ACCUMULATE)
|
||||
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
Y(ix + dim0 * offset, e) += u;
|
||||
}
|
||||
else
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
|
||||
for (int dz = 0; dz < D1Dz; ++dz)
|
||||
{
|
||||
const real_t wz = (c == 2) ? sBo[qz][dz] : sBc[qz][dz];
|
||||
for (int dy = 0; dy < D1Dy; ++dy)
|
||||
{
|
||||
const real_t wy = (c == 1) ? sBo[qy][dy] : sBc[qy][dy];
|
||||
for (int dx = 0; dx < D1Dx; ++dx)
|
||||
{
|
||||
const real_t t = sX[dz][dy][dx];
|
||||
const real_t wx = (c == 0) ? sBo[qx][dx] : sBc[qx][dx];
|
||||
u += t * wx * wy * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mass[qy][qx][c] = u;
|
||||
} // qx
|
||||
} // qy
|
||||
} // tidz == qz
|
||||
|
||||
osc += D1Dx * D1Dy * D1Dz;
|
||||
MFEM_SYNC_THREAD;
|
||||
} // c
|
||||
|
||||
MFEM_SYNC_THREAD; // Sync mass[qy][qx][d] and sop
|
||||
|
||||
osc = 0;
|
||||
for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
|
||||
{
|
||||
const int D1Dz = (c == 2) ? D1D - 1 : D1D;
|
||||
const int D1Dy = (c == 1) ? D1D - 1 : D1D;
|
||||
const int D1Dx = (c == 0) ? D1D - 1 : D1D;
|
||||
|
||||
real_t dxyz = 0.0;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz,z,D1Dz)
|
||||
{
|
||||
const real_t wz = (c == 2) ? sBo[qz][dz] : sBc[qz][dz];
|
||||
|
||||
MFEM_FOREACH_THREAD(dy,y,D1Dy)
|
||||
{
|
||||
Y(ix + dim0 * offset, e) = u;
|
||||
MFEM_FOREACH_THREAD(dx,x,D1Dx)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const real_t wy = (c == 1) ? sBo[qy][dy] : sBc[qy][dy];
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const int os = (dataSize*qx) + (dataSize*Q1D*qy);
|
||||
const int id1 = os + ((c == 0) ? 0 : ((c == 1) ? (symmetric ? 1 : 3) :
|
||||
(symmetric ? 2 : 6))); // O11, O21, O31
|
||||
const int id2 = os + ((c == 0) ? 1 : ((c == 1) ? (symmetric ? 3 : 4) :
|
||||
(symmetric ? 4 : 7))); // O12, O22, O32
|
||||
const int id3 = os + ((c == 0) ? 2 : ((c == 1) ? (symmetric ? 4 : 5) :
|
||||
(symmetric ? 5 : 8))); // O13, O23, O33
|
||||
|
||||
const real_t m_c = (sop[id1] * mass[qy][qx][0]) + (sop[id2] * mass[qy][qx][1]) +
|
||||
(sop[id3] * mass[qy][qx][2]);
|
||||
|
||||
const real_t wx = (c == 0) ? sBo[qx][dx] : sBc[qx][dx];
|
||||
dxyz += m_c * wx * wy * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz,z,D1Dz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1Dy)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1Dx)
|
||||
{
|
||||
Y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += dxyz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osc += D1Dx * D1Dy * D1Dz;
|
||||
} // c loop
|
||||
} // qz
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
|
||||
@@ -62,30 +62,6 @@ void PAHcurlHdivMassApply2D(const int D1D,
|
||||
const Vector &x_,
|
||||
Vector &y_);
|
||||
|
||||
/// H(curl) test, H(div) trial
|
||||
inline void
|
||||
PAHcurlHdivMassApply2D(const int NE, const bool, const bool scalarCoeff,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int D1D, const int D1Dtest, const int Q1D)
|
||||
{
|
||||
return PAHcurlHdivMassApply2D(D1D, D1Dtest, Q1D, NE, scalarCoeff, false,
|
||||
false, Bo_, Bc_, Bot_, Bct_, op_, x_, y_);
|
||||
}
|
||||
|
||||
/// H(div) test, H(curl) trial
|
||||
inline void
|
||||
PAHdivHcurlMassApply2D(const int NE, const bool, const bool scalarCoeff,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int D1D, const int D1Dtest, const int Q1D)
|
||||
{
|
||||
return PAHcurlHdivMassApply2D(D1D, D1Dtest, Q1D, NE, scalarCoeff, true,
|
||||
false, Bo_, Bc_, Bot_, Bct_, op_, x_, y_);
|
||||
}
|
||||
|
||||
// PA H(curl)-H(div) Mass Apply 3D kernel
|
||||
void PAHcurlHdivMassApply3D(const int D1D,
|
||||
const int D1Dtest,
|
||||
@@ -102,30 +78,6 @@ void PAHcurlHdivMassApply3D(const int D1D,
|
||||
const Vector &x_,
|
||||
Vector &y_);
|
||||
|
||||
/// H(curl) test, H(div) trial
|
||||
inline void
|
||||
PAHcurlHdivMassApply3D(const int NE, const bool, const bool scalarCoeff,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int D1D, const int D1Dtest, const int Q1D)
|
||||
{
|
||||
PAHcurlHdivMassApply3D(D1D, D1Dtest, Q1D, NE, scalarCoeff, false, false, Bo_,
|
||||
Bc_, Bot_, Bct_, op_, x_, y_);
|
||||
}
|
||||
|
||||
/// H(div) test, H(curl) trial
|
||||
inline void
|
||||
PAHdivHcurlMassApply3D(const int NE, const bool, const bool scalarCoeff,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int D1D, const int D1Dtest, const int Q1D)
|
||||
{
|
||||
PAHcurlHdivMassApply3D(D1D, D1Dtest, Q1D, NE, scalarCoeff, true, false, Bo_,
|
||||
Bc_, Bot_, Bct_, op_, x_, y_);
|
||||
}
|
||||
|
||||
// PA H(curl)-H(div) Curl Apply 3D kernel
|
||||
template<int T_D1D = 0, int T_D1D_TEST = 0, int T_Q1D = 0>
|
||||
inline void PAHcurlHdivApply3D(const int d1d,
|
||||
@@ -864,656 +816,8 @@ inline void PAHcurlHdivApplyTranspose3D(const int d1d,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
namespace curlinterp
|
||||
{
|
||||
constexpr int NBZ3D(int ndof_o, int nquad_o, int mdq)
|
||||
{
|
||||
if (ndof_o <= 0 || nquad_o <= 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
int ndof_c = ndof_o + 1;
|
||||
int nquad_c = nquad_o + 1;
|
||||
// z dimension is capped at 64 on nvidia and amd gpus
|
||||
int tmp =
|
||||
std::min((128 + mdq * mdq * (mdq - 1) - 1) / (mdq * mdq * (mdq - 1)), 64);
|
||||
int smem_req =
|
||||
sizeof(mfem::real_t) *
|
||||
((3 * ndof_c * ndof_c * ndof_o + 2 * 2 * mdq * mdq * mdq) * tmp +
|
||||
ndof_c * nquad_o + ndof_c * nquad_c + ndof_o * nquad_o);
|
||||
// assume GPU has at least 48k shared memory
|
||||
return std::max(std::min(tmp, (48 * 1024 + smem_req - 1) / smem_req), 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <int T_NDOF_O, int T_NQUAD_O>
|
||||
void CurlInterpolatorApply3DSmem(const int ne, const int ndof_o,
|
||||
const int nquad_o, const Vector &pa,
|
||||
const Vector &x_, Vector &y_)
|
||||
{
|
||||
constexpr int mnd_o = T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
|
||||
constexpr int mnq_o =
|
||||
T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
|
||||
constexpr int mndq = std::max(mnd_o + 1, mnq_o + 1);
|
||||
constexpr int tbatch = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, mndq);
|
||||
MFEM_VERIFY(ndof_o <= mnd_o, "Error: H(curl) order larger than supported");
|
||||
MFEM_VERIFY(nquad_o <= mnq_o, "Error: H(div) order larger than supported");
|
||||
int mnq = std::max(ndof_o + 1, nquad_o + 1);
|
||||
auto pa_data = pa.Read();
|
||||
auto x_d = x_.Read();
|
||||
auto y_d = y_.ReadWrite();
|
||||
mfem::forall_2D_batch<mndq * mndq * (mndq - 1) * tbatch>(
|
||||
ne, mnq * mnq * (mnq - 1), 1, tbatch, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MND_O =
|
||||
T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
|
||||
constexpr int MNQ_O =
|
||||
T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
|
||||
constexpr int MNDQ = std::max(MND_O + 1, MNQ_O + 1);
|
||||
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
|
||||
constexpr int nbz = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, MNDQ);
|
||||
int tidz = MFEM_THREAD_ID(z);
|
||||
// Make mnq a local variable since capturing would result in different
|
||||
// captures between host/device versions, and spuriously fails
|
||||
int mnq = std::max(ndof_o + 1, nquad_o + 1);
|
||||
#else
|
||||
constexpr int nbz = 1;
|
||||
constexpr int tidz = 0;
|
||||
#endif
|
||||
const int NDOF_O = T_NDOF_O ? T_NDOF_O : ndof_o;
|
||||
const int NQUAD_O = T_NQUAD_O ? T_NQUAD_O : nquad_o;
|
||||
const int NDOF_C = NDOF_O + 1;
|
||||
const int NQUAD_C = NQUAD_O + 1;
|
||||
MFEM_SHARED real_t
|
||||
sBG[(MND_O + 1) * MNQ_O + (MND_O + 1) * (MNQ_O + 1) + MND_O * MNQ_O];
|
||||
auto X_ = Reshape(x_d, 3 * NDOF_C * NDOF_C * NDOF_O, ne);
|
||||
auto Y = Reshape(y_d, 3 * NQUAD_C * NQUAD_O * NQUAD_O, ne);
|
||||
auto Gco = Reshape(sBG, NQUAD_O, NDOF_C);
|
||||
auto Bcc = Reshape(sBG + NDOF_C * NQUAD_O, NQUAD_C, NDOF_C);
|
||||
auto Boo =
|
||||
Reshape(sBG + NDOF_C * NQUAD_O + NDOF_C * NQUAD_C, NQUAD_O, NDOF_O);
|
||||
MFEM_SHARED real_t X[3][nbz][MND_O * (MND_O + 1) * (MND_O + 1)];
|
||||
MFEM_SHARED real_t sm0[nbz * 2 * MNDQ * MNDQ * MNDQ];
|
||||
MFEM_SHARED real_t sm1[nbz * 2 * MNDQ * MNDQ * MNDQ];
|
||||
|
||||
// shapes of buffers always use MNDQ to mitigate shared memory bank
|
||||
// conflicts
|
||||
real_t(*DDQ)[nbz][MNDQ][MNDQ][MNDQ] =
|
||||
(real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
|
||||
real_t(*DQQ)[nbz][MNDQ][MNDQ][MNDQ] =
|
||||
(real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm1);
|
||||
real_t(*QQQ)[nbz][MNDQ][MNDQ][MNDQ] =
|
||||
(real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
|
||||
const int offset = NDOF_O * NDOF_C * NDOF_C;
|
||||
const int offsetq = NQUAD_C * NQUAD_O * NQUAD_O;
|
||||
MFEM_FOREACH_THREAD_DIRECT(ix, x, offset)
|
||||
{
|
||||
for (int dim = 0; dim < 3; ++dim)
|
||||
{
|
||||
X[dim][tidz][ix] = X_(ix + dim * offset, e);
|
||||
}
|
||||
}
|
||||
// load basis functions data
|
||||
if (tidz == 0)
|
||||
{
|
||||
auto npts = NDOF_C * NQUAD_O + NDOF_C * NQUAD_C + NDOF_O * NQUAD_O;
|
||||
MFEM_FOREACH_THREAD(ix, x, npts) { sBG[ix] = pa_data[ix]; }
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// x: Vz Bcc Gco Boo - Vy Bcc Boo Gco
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_C, NDOF_C,
|
||||
NDOF_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < NDOF_C; ++dx)
|
||||
{
|
||||
u += X[2][tidz][dx + (dy + dz * NDOF_C) * NDOF_C] * Bcc(qx, dx);
|
||||
}
|
||||
DDQ[0][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_C, NDOF_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < NDOF_C; ++dx)
|
||||
{
|
||||
u += X[1][tidz][dx + (dy + dz * NDOF_O) * NDOF_C] * Bcc(qx, dx);
|
||||
}
|
||||
DDQ[1][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_C, NQUAD_O,
|
||||
NDOF_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < NDOF_C; ++dy)
|
||||
{
|
||||
u += DDQ[0][tidz][dz][dy][qx] * Gco(qy, dy);
|
||||
}
|
||||
DQQ[0][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_C, NQUAD_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < NDOF_O; ++dy)
|
||||
{
|
||||
u += DDQ[1][tidz][dz][dy][qx] * Boo(qy, dy);
|
||||
}
|
||||
DQQ[1][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_C, NQUAD_O,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < NDOF_O; ++dz)
|
||||
{
|
||||
u += DQQ[0][tidz][dz][qy][qx] * Boo(qz, dz);
|
||||
}
|
||||
QQQ[0][tidz][qz][qy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_C, NQUAD_O,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < NDOF_C; ++dz)
|
||||
{
|
||||
u += DQQ[1][tidz][dz][qy][qx] * Gco(qz, dz);
|
||||
}
|
||||
Y(qx + (qy + qz * NQUAD_O) * NQUAD_C, e) =
|
||||
QQQ[0][tidz][qz][qy][qx] - u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// y: Vx Boo Bcc Gco - Vz Gco Bcc Boo
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_C,
|
||||
NDOF_C, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < NDOF_O; ++dx)
|
||||
{
|
||||
u += X[0][tidz][dx + (dy + dz * NDOF_C) * NDOF_O] * Boo(qx, dx);
|
||||
}
|
||||
DDQ[0][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_C,
|
||||
NDOF_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < NDOF_C; ++dx)
|
||||
{
|
||||
u += X[2][tidz][dx + (dy + dz * NDOF_C) * NDOF_C] * Gco(qx, dx);
|
||||
}
|
||||
DDQ[1][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_C,
|
||||
NDOF_C, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < NDOF_C; ++dy)
|
||||
{
|
||||
u += DDQ[0][tidz][dz][dy][qx] * Bcc(qy, dy);
|
||||
}
|
||||
DQQ[0][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_C,
|
||||
NDOF_O, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < NDOF_C; ++dy)
|
||||
{
|
||||
u += DDQ[1][tidz][dz][dy][qx] * Bcc(qy, dy);
|
||||
}
|
||||
DQQ[1][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < NDOF_C; ++dz)
|
||||
{
|
||||
u += DQQ[0][tidz][dz][qy][qx] * Gco(qz, dz);
|
||||
}
|
||||
QQQ[0][tidz][qz][qy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < NDOF_O; ++dz)
|
||||
{
|
||||
u += DQQ[1][tidz][dz][qy][qx] * Boo(qz, dz);
|
||||
}
|
||||
Y(qx + (qy + qz * NQUAD_C) * NQUAD_O + offsetq, e) =
|
||||
QQQ[0][tidz][qz][qy][qx] - u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// z: Vy Gco Boo Bcc - Vx Boo Gco Bcc
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < NDOF_C; ++dx)
|
||||
{
|
||||
u += X[1][tidz][dx + (dy + dz * NDOF_O) * NDOF_C] * Gco(qx, dx);
|
||||
}
|
||||
DDQ[0][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, dy, dz, x, NQUAD_O, NDOF_C,
|
||||
NDOF_C, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dx = 0; dx < NDOF_O; ++dx)
|
||||
{
|
||||
u += X[0][tidz][dx + (dy + dz * NDOF_C) * NDOF_O] * Boo(qx, dx);
|
||||
}
|
||||
DDQ[1][tidz][dz][dy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < NDOF_O; ++dy)
|
||||
{
|
||||
u += DDQ[0][tidz][dz][dy][qx] * Boo(qy, dy);
|
||||
}
|
||||
DQQ[0][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, dz, x, NQUAD_O, NQUAD_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dy = 0; dy < NDOF_C; ++dy)
|
||||
{
|
||||
u += DDQ[1][tidz][dz][dy][qx] * Gco(qy, dy);
|
||||
}
|
||||
DQQ[1][tidz][dz][qy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_O,
|
||||
NQUAD_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < NDOF_C; ++dz)
|
||||
{
|
||||
u += DQQ[0][tidz][dz][qy][qx] * Bcc(qz, dz);
|
||||
}
|
||||
QQQ[0][tidz][qz][qy][qx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(qx, qy, qz, x, NQUAD_O, NQUAD_O,
|
||||
NQUAD_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int dz = 0; dz < NDOF_C; ++dz)
|
||||
{
|
||||
u += DQQ[1][tidz][dz][qy][qx] * Bcc(qz, dz);
|
||||
}
|
||||
Y(qx + (qy + qz * NQUAD_O) * NQUAD_O + 2 * offsetq, e) =
|
||||
QQQ[0][tidz][qz][qy][qx] - u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_NDOF_O, int T_NQUAD_O>
|
||||
void CurlInterpolatorTApply3DSmem(const int ne, const int ndof_o,
|
||||
const int nquad_o, const Vector &pa,
|
||||
const Vector &x_, Vector &y_)
|
||||
{
|
||||
constexpr int mnd_o = T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
|
||||
constexpr int mnq_o =
|
||||
T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
|
||||
constexpr int mndq = std::max(mnd_o + 1, mnq_o + 1);
|
||||
constexpr int tbatch = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, mndq);
|
||||
MFEM_VERIFY(ndof_o <= mnd_o, "Error: H(curl) order larger than supported");
|
||||
MFEM_VERIFY(nquad_o <= mnq_o, "Error: H(div) order larger than supported");
|
||||
int mnq = std::max(ndof_o + 1, nquad_o + 1);
|
||||
auto pa_data = pa.Read();
|
||||
auto x_d = x_.Read();
|
||||
auto y_d = y_.ReadWrite();
|
||||
mfem::forall_2D_batch<mndq * mndq * (mndq - 1) * tbatch>(
|
||||
ne, mnq * mnq * (mnq - 1), 1, tbatch, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MND_O =
|
||||
T_NDOF_O ? T_NDOF_O : DofQuadLimits::HCURL_MAX_D1D - 1;
|
||||
constexpr int MNQ_O =
|
||||
T_NQUAD_O ? T_NQUAD_O : DofQuadLimits::HDIV_MAX_D1D - 1;
|
||||
constexpr int MNDQ = std::max(MND_O + 1, MNQ_O + 1);
|
||||
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
|
||||
constexpr int nbz = curlinterp::NBZ3D(T_NDOF_O, T_NQUAD_O, MNDQ);
|
||||
int tidz = MFEM_THREAD_ID(z);
|
||||
// Make mnq a local variable since capturing would result in different
|
||||
// captures between host/device versions, and spuriously fails
|
||||
int mnq = std::max(ndof_o + 1, nquad_o + 1);
|
||||
#else
|
||||
constexpr int nbz = 1;
|
||||
constexpr int tidz = 0;
|
||||
#endif
|
||||
const int NDOF_O = T_NDOF_O ? T_NDOF_O : ndof_o;
|
||||
const int NQUAD_O = T_NQUAD_O ? T_NQUAD_O : nquad_o;
|
||||
const int NDOF_C = NDOF_O + 1;
|
||||
const int NQUAD_C = NQUAD_O + 1;
|
||||
MFEM_SHARED real_t
|
||||
sBG[(MND_O + 1) * MNQ_O + (MND_O + 1) * (MNQ_O + 1) + MND_O * MNQ_O];
|
||||
auto X_ = Reshape(x_d, 3 * NQUAD_C * NQUAD_O * NQUAD_O, ne);
|
||||
auto Y = Reshape(y_d, 3 * NDOF_C * NDOF_C * NDOF_O, ne);
|
||||
auto Gco = Reshape(sBG, NQUAD_O, NDOF_C);
|
||||
auto Bcc = Reshape(sBG + NDOF_C * NQUAD_O, NQUAD_C, NDOF_C);
|
||||
auto Boo =
|
||||
Reshape(sBG + NDOF_C * NQUAD_O + NDOF_C * NQUAD_C, NQUAD_O, NDOF_O);
|
||||
MFEM_SHARED real_t X[3][nbz][MNQ_O * MNQ_O * (MNQ_O + 1)];
|
||||
MFEM_SHARED real_t sm0[nbz * 2 * MNDQ * MNDQ * MNDQ];
|
||||
MFEM_SHARED real_t sm1[nbz * 2 * MNDQ * MNDQ * MNDQ];
|
||||
|
||||
// shapes of buffers always use MNDQ to mitigate shared memory bank
|
||||
// conflicts
|
||||
real_t(*QQD)[nbz][MNDQ][MNDQ][MNDQ] =
|
||||
(real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
|
||||
real_t(*QDD)[nbz][MNDQ][MNDQ][MNDQ] =
|
||||
(real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm1);
|
||||
real_t(*DDD)[nbz][MNDQ][MNDQ][MNDQ] =
|
||||
(real_t(*)[nbz][MNDQ][MNDQ][MNDQ])(sm0);
|
||||
const int offset = NDOF_O * NDOF_C * NDOF_C;
|
||||
const int offsetq = NQUAD_C * NQUAD_O * NQUAD_O;
|
||||
MFEM_FOREACH_THREAD_DIRECT(ix, x, offsetq)
|
||||
{
|
||||
for (int dim = 0; dim < 3; ++dim)
|
||||
{
|
||||
X[dim][tidz][ix] = X_(ix + dim * offsetq, e);
|
||||
}
|
||||
}
|
||||
// load basis functions data
|
||||
if (tidz == 0)
|
||||
{
|
||||
auto npts = NDOF_C * NQUAD_O + NDOF_C * NQUAD_C + NDOF_O * NQUAD_O;
|
||||
MFEM_FOREACH_THREAD(ix, x, npts) { sBG[ix] = pa_data[ix]; }
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// x: Vy Boo Bcc Gco - Vz Boo Gco Bcc
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_O,
|
||||
NQUAD_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < NQUAD_O; ++qz)
|
||||
{
|
||||
u += X[1][tidz][qx + (qy + qz * NQUAD_C) * NQUAD_O] * Gco(qz, dz);
|
||||
}
|
||||
QQD[0][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_O,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < NQUAD_C; ++qz)
|
||||
{
|
||||
u += X[2][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_O] * Bcc(qz, dz);
|
||||
}
|
||||
QQD[1][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < NQUAD_C; ++qy)
|
||||
{
|
||||
u += QQD[0][tidz][qy][qx][dz] * Bcc(qy, dy);
|
||||
}
|
||||
QDD[0][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < NQUAD_O; ++qy)
|
||||
{
|
||||
u += QQD[1][tidz][qy][qx][dz] * Gco(qy, dy);
|
||||
}
|
||||
QDD[1][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_O, NDOF_C,
|
||||
NDOF_C, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < NQUAD_O; ++qx)
|
||||
{
|
||||
u += QDD[0][tidz][qx][dz][dy] * Boo(qx, dx);
|
||||
}
|
||||
DDD[0][tidz][dz][dy][dx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_O, NDOF_C,
|
||||
NDOF_C, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < NQUAD_O; ++qx)
|
||||
{
|
||||
u += QDD[1][tidz][qx][dz][dy] * Boo(qx, dx);
|
||||
}
|
||||
Y(dx + (dy + dz * NDOF_C) * NDOF_O, e) =
|
||||
DDD[0][tidz][dz][dy][dx] - u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// y: Vz Gco Boo Bcc - Vx Bcc Boo Gco
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_O,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < NQUAD_C; ++qz)
|
||||
{
|
||||
u += X[2][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_O] * Bcc(qz, dz);
|
||||
}
|
||||
QQD[0][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_C, NQUAD_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < NQUAD_O; ++qz)
|
||||
{
|
||||
u += X[0][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_C] * Gco(qz, dz);
|
||||
}
|
||||
QQD[1][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_O, NDOF_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < NQUAD_O; ++qy)
|
||||
{
|
||||
u += QQD[0][tidz][qy][qx][dz] * Boo(qy, dy);
|
||||
}
|
||||
QDD[0][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_O, NDOF_C,
|
||||
NQUAD_C, mnq - 1, mnq, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < NQUAD_O; ++qy)
|
||||
{
|
||||
u += QQD[1][tidz][qy][qx][dz] * Boo(qy, dy);
|
||||
}
|
||||
QDD[1][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < NQUAD_O; ++qx)
|
||||
{
|
||||
u += QDD[0][tidz][qx][dz][dy] * Gco(qx, dx);
|
||||
}
|
||||
DDD[0][tidz][dz][dy][dx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_O,
|
||||
NDOF_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < NQUAD_C; ++qx)
|
||||
{
|
||||
u += QDD[1][tidz][qx][dz][dy] * Bcc(qx, dx);
|
||||
}
|
||||
Y(dx + (dy + dz * NDOF_O) * NDOF_C + offset, e) =
|
||||
DDD[0][tidz][dz][dy][dx] - u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// z: Vx Bcc Gco Boo - Vy Gco Bcc Boo
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_O, NQUAD_C,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < NQUAD_O; ++qz)
|
||||
{
|
||||
u += X[0][tidz][qx + (qy + qz * NQUAD_O) * NQUAD_C] * Boo(qz, dz);
|
||||
}
|
||||
QQD[0][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dz, qx, qy, x, NDOF_O, NQUAD_O,
|
||||
NQUAD_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qz = 0; qz < NQUAD_O; ++qz)
|
||||
{
|
||||
u += X[1][tidz][qx + (qy + qz * NQUAD_C) * NQUAD_O] * Boo(qz, dz);
|
||||
}
|
||||
QQD[1][tidz][qy][qx][dz] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_O,
|
||||
NQUAD_C, mnq, mnq - 1, mnq)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < NQUAD_O; ++qy)
|
||||
{
|
||||
u += QQD[0][tidz][qy][qx][dz] * Gco(qy, dy);
|
||||
}
|
||||
QDD[0][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dy, dz, qx, x, NDOF_C, NDOF_O,
|
||||
NQUAD_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qy = 0; qy < NQUAD_C; ++qy)
|
||||
{
|
||||
u += QQD[1][tidz][qy][qx][dz] * Bcc(qy, dy);
|
||||
}
|
||||
QDD[1][tidz][qx][dz][dy] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_C,
|
||||
NDOF_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < NQUAD_C; ++qx)
|
||||
{
|
||||
u += QDD[0][tidz][qx][dz][dy] * Bcc(qx, dx);
|
||||
}
|
||||
DDD[0][tidz][dz][dy][dx] = u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
// threads assigned to mitigate bank conflicts
|
||||
MFEM_FOREACH_THREAD_DIRECT_3D_OFFSET(dx, dy, dz, x, NDOF_C, NDOF_C,
|
||||
NDOF_O, mnq, mnq, mnq - 1)
|
||||
{
|
||||
real_t u = 0;
|
||||
for (int qx = 0; qx < NQUAD_O; ++qx)
|
||||
{
|
||||
u += QDD[1][tidz][qx][dz][dy] * Gco(qx, dx);
|
||||
}
|
||||
Y(dx + (dy + dz * NDOF_C) * NDOF_C + 2 * offset, e) =
|
||||
DDD[0][tidz][dz][dy][dx] - u;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <int DIM, int NDOF_O, int NQUAD_O>
|
||||
CurlInterpolator::ApplyKernelType
|
||||
CurlInterpolator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::CurlInterpolatorApply3DSmem<NDOF_O, NQUAD_O>;
|
||||
}
|
||||
MFEM_ABORT("Bad dimension!");
|
||||
}
|
||||
|
||||
template <int DIM, int NDOF_O, int NQUAD_O>
|
||||
CurlInterpolator::ApplyKernelType
|
||||
CurlInterpolator::ApplyTPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::CurlInterpolatorTApply3DSmem<NDOF_O, NQUAD_O>;
|
||||
}
|
||||
MFEM_ABORT("Bad dimension!");
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
@@ -197,21 +197,15 @@ static void EAHdivAssemble3D(const int NE,
|
||||
// Assemble (one row per thread)
|
||||
MFEM_FOREACH_THREAD(idx_i, x, NDOF)
|
||||
{
|
||||
// NOTE: due to an llvm backend bug, usage of the modulus operator
|
||||
// has been removed from this foreach section.
|
||||
const int ic = idx_i / NDOF_C;
|
||||
const int idx_ii = idx_i - ic * NDOF_C; // idx_i % NDOF_C
|
||||
const int idx_ii = idx_i % NDOF_C;
|
||||
|
||||
const int nx_i = (ic == 0) ? D1D : D1D-1;
|
||||
const int ny_i = (ic == 1) ? D1D : D1D-1;
|
||||
|
||||
const int qx_i = idx_ii / nx_i;
|
||||
const int ix = idx_ii - qx_i * nx_i; // idx_ii % nx_i
|
||||
|
||||
const int qy_i = qx_i / ny_i;
|
||||
const int iy = qx_i - qy_i * ny_i; // (idx_ii / nx_i) % ny_i
|
||||
|
||||
const int iz = qy_i; // (idx_ii / nx_i) / ny_i
|
||||
const int ix = idx_ii % nx_i;
|
||||
const int iy = (idx_ii / nx_i) % ny_i;
|
||||
const int iz = (idx_ii / nx_i) / ny_i;
|
||||
|
||||
const real_t (&Bi1)[MQ1][MD1] = (ic == 0) ? r_Bc : r_Bo;
|
||||
const real_t (&Bi2)[MQ1][MD1] = (ic == 1) ? r_Bc : r_Bo;
|
||||
@@ -220,18 +214,14 @@ static void EAHdivAssemble3D(const int NE,
|
||||
for (int idx_j = 0; idx_j < NDOF; ++idx_j)
|
||||
{
|
||||
const int jc = idx_j / NDOF_C;
|
||||
const int idx_jj = idx_j - jc * NDOF_C; // idx_j % NDOF_C
|
||||
const int idx_jj = idx_j % NDOF_C;
|
||||
|
||||
const int nx_j = (jc == 0) ? D1D : D1D-1;
|
||||
const int ny_j = (jc == 1) ? D1D : D1D-1;
|
||||
|
||||
const int qx_j = idx_jj / nx_j;
|
||||
const int jx = idx_jj - qx_j * nx_j; // idx_jj % nx_j
|
||||
|
||||
const int qy_j = qx_j / ny_j;
|
||||
const int jy = qx_j - qy_j * ny_j; // (idx_jj / nx_j) % ny_j
|
||||
|
||||
const int jz = qy_j; // (idx_jj / nx_j) / ny_j
|
||||
const int jx = idx_jj % nx_j;
|
||||
const int jy = (idx_jj / nx_j) % ny_j;
|
||||
const int jz = (idx_jj / nx_j) / ny_j;
|
||||
|
||||
const real_t (&Bj1)[MQ1][MD1] = (jc == 0) ? r_Bc : r_Bo;
|
||||
const real_t (&Bj2)[MQ1][MD1] = (jc == 1) ? r_Bc : r_Bo;
|
||||
|
||||
@@ -294,14 +294,61 @@ void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
void PAHdivMassApply2D(const int NE, const bool symmetric, const bool,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int D1D, const int TestD1D, const int Q1D)
|
||||
void PAHdivMassApply(const int dim,
|
||||
const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo,
|
||||
const Array<real_t> &Bc,
|
||||
const Array<real_t> &Bot,
|
||||
const Array<real_t> &Bct,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
const int id = (D1D << 4) | Q1D;
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x22: return SmemPAHdivMassApply2D<2,2>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x33: return SmemPAHdivMassApply2D<3,3>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x44: return SmemPAHdivMassApply2D<4,4>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x55: return SmemPAHdivMassApply2D<5,5>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
default: // fallback
|
||||
return PAHdivMassApply2D(D1D,Q1D,NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0x23: return SmemPAHdivMassApply3D<2,3>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x34: return SmemPAHdivMassApply3D<3,4>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x45: return SmemPAHdivMassApply3D<4,5>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x56: return SmemPAHdivMassApply3D<5,6>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x67: return SmemPAHdivMassApply3D<6,7>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
case 0x78: return SmemPAHdivMassApply3D<7,8>(NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
default: // fallback
|
||||
return PAHdivMassApply3D(D1D,Q1D,NE,symmetric,Bo,Bc,Bot,Bct,op,x,y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PAHdivMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_)
|
||||
{
|
||||
MFEM_VERIFY(D1D == TestD1D,
|
||||
"Trial and test spaces must have same number of dofs");
|
||||
auto Bo = Reshape(Bo_.Read(), Q1D, D1D-1);
|
||||
auto Bc = Reshape(Bc_.Read(), Q1D, D1D);
|
||||
auto Bot = Reshape(Bot_.Read(), D1D-1, Q1D);
|
||||
@@ -421,14 +468,18 @@ void PAHdivMassApply2D(const int NE, const bool symmetric, const bool,
|
||||
}); // end of element loop
|
||||
}
|
||||
|
||||
void PAHdivMassApply3D(const int NE, const bool symmetric, const bool,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int D1D, const int TestD1D, const int Q1D)
|
||||
void PAHdivMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_)
|
||||
{
|
||||
MFEM_VERIFY(D1D == TestD1D,
|
||||
"Trial and test spaces must have same number of dofs");
|
||||
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
|
||||
"Error: D1D > HDIV_MAX_D1D");
|
||||
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
|
||||
|
||||
@@ -66,29 +66,58 @@ void PAHdivMassAssembleDiagonal3D(const int D1D,
|
||||
const Vector &op_,
|
||||
Vector &diag_);
|
||||
|
||||
void PAHdivMassApply(const int dim,
|
||||
const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo,
|
||||
const Array<real_t> &Bc,
|
||||
const Array<real_t> &Bot,
|
||||
const Array<real_t> &Bct,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y);
|
||||
|
||||
// PA H(div) Mass Apply 2D kernel
|
||||
void PAHdivMassApply2D(const int NE, const bool symmetric,
|
||||
const bool scalar_coeff, const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_, const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_, const Vector &op_,
|
||||
const Vector &x_, Vector &y_, const int D1D,
|
||||
const int TestD1D, const int Q1D);
|
||||
void PAHdivMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_);
|
||||
|
||||
// PA H(div) Mass Apply 3D kernel
|
||||
void PAHdivMassApply3D(const int NE, const bool symmetric,
|
||||
const bool scalar_coeff, const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_, const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_, const Vector &op_,
|
||||
const Vector &x_, Vector &y_, const int D1D,
|
||||
const int TestD1D, const int Q1D);
|
||||
void PAHdivMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_);
|
||||
|
||||
// Shared memory PA H(div) Mass Apply 2D kernel
|
||||
template <int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void SmemPAHdivMassApply2D(
|
||||
const int NE, const bool symmetric, const bool, const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_, const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_, const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int d1d = 0, const int = 0, const int q1d = 0)
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void SmemPAHdivMassApply2D(const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(Bot_);
|
||||
MFEM_CONTRACT_VAR(Bct_);
|
||||
@@ -251,13 +280,18 @@ inline void SmemPAHdivMassApply2D(
|
||||
}
|
||||
|
||||
// Shared memory PA H(div) Mass Apply 3D kernel
|
||||
template <int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void
|
||||
SmemPAHdivMassApply3D(const int NE, const bool symmetric, const bool,
|
||||
const Array<real_t> &Bo_, const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_, const Array<real_t> &Bct_,
|
||||
const Vector &op_, const Vector &x_, Vector &y_,
|
||||
const int d1d = 0, const int = 0, const int q1d = 0)
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void SmemPAHdivMassApply3D(const int NE,
|
||||
const bool symmetric,
|
||||
const Array<real_t> &Bo_,
|
||||
const Array<real_t> &Bc_,
|
||||
const Array<real_t> &Bot_,
|
||||
const Array<real_t> &Bct_,
|
||||
const Vector &op_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(Bot_);
|
||||
MFEM_CONTRACT_VAR(Bct_);
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "../gridfunc.hpp"
|
||||
#include "../qfunction.hpp"
|
||||
|
||||
#include "bilininteg_hcurlhdiv_kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -1952,138 +1950,4 @@ void IdentityInterpolator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void CurlInterpolator::AssemblePA(const FiniteElementSpace &dom_fes,
|
||||
const FiniteElementSpace &ran_fes)
|
||||
{
|
||||
// TODO: 1D and 2D meshes
|
||||
Mesh *mesh = dom_fes.GetMesh();
|
||||
const VectorTensorFiniteElement *dom_el =
|
||||
dynamic_cast<const VectorTensorFiniteElement *>(dom_fes.GetTypicalFE());
|
||||
const VectorTensorFiniteElement *ran_el =
|
||||
dynamic_cast<const VectorTensorFiniteElement *>(ran_fes.GetTypicalFE());
|
||||
MFEM_VERIFY(dom_el != NULL, "Only VectorTensorFiniteElement is supported!");
|
||||
MFEM_VERIFY(ran_el != NULL, "Only VectorTensorFiniteElement is supported!");
|
||||
// only supports H(curl) -> H(div) because of discontinuity requirements
|
||||
MFEM_VERIFY(dom_el->GetDerivType() == FiniteElement::CURL,
|
||||
"Domain space must be H(curl)");
|
||||
MFEM_VERIFY(ran_el->GetDerivType() == FiniteElement::DIV,
|
||||
"Range space must be H(div)");
|
||||
|
||||
const int dims = dom_el->GetDim();
|
||||
MFEM_VERIFY(dims == 3, "");
|
||||
dim = mesh->Dimension();
|
||||
|
||||
ne = dom_fes.GetNE();
|
||||
ndof_o = dom_el->GetOrder();
|
||||
int ndof_c = ndof_o + 1;
|
||||
nquad_o = ran_el->GetOrder();
|
||||
int nquad_c = nquad_o + 1;
|
||||
|
||||
// extract the tensor product range dof locations
|
||||
std::vector<real_t> qc(nquad_c);
|
||||
std::vector<real_t> qo(nquad_o);
|
||||
{
|
||||
const IntegrationRule &ran_nodes = ran_el->GetNodes();
|
||||
const Array<int> &quad_map = ran_el->GetDofMap();
|
||||
for (int i = 0; i < nquad_c; ++i)
|
||||
{
|
||||
int idx = UnsignIndex(quad_map[i]);
|
||||
qc[i] = ran_nodes.IntPoint(idx).x;
|
||||
}
|
||||
int offset = ndof_c * ndof_o * ndof_o;
|
||||
for (int i = 0; i < nquad_o; ++i)
|
||||
{
|
||||
int idx = UnsignIndex(quad_map[i + offset]);
|
||||
qo[i] = ran_nodes.IntPoint(idx).x;
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate closed/open 1D basis (and their derivatives) at closed and
|
||||
// open quads
|
||||
// storage order: GCO, BCC, BOO
|
||||
pa_data.SetSize(ndof_c * nquad_o + ndof_c * nquad_c + ndof_o * nquad_o);
|
||||
auto ptr = pa_data.HostWrite();
|
||||
auto &cbasis1d = dom_el->GetBasis1D();
|
||||
auto &obasis1d = dom_el->GetOpenBasis1D();
|
||||
Vector b, g;
|
||||
b.SetSize(ndof_c);
|
||||
g.SetSize(ndof_c);
|
||||
for (int j = 0; j < nquad_o; ++j)
|
||||
{
|
||||
cbasis1d.Eval(qo[j], b, g);
|
||||
for (int i = 0; i < ndof_c; ++i)
|
||||
{
|
||||
ptr[j + i * nquad_o] = g[i];
|
||||
}
|
||||
}
|
||||
ptr += nquad_o * ndof_c;
|
||||
|
||||
for (int j = 0; j < nquad_c; ++j)
|
||||
{
|
||||
cbasis1d.Eval(qc[j], b);
|
||||
for (int i = 0; i < ndof_c; ++i)
|
||||
{
|
||||
ptr[j + i * nquad_c] = b[i];
|
||||
}
|
||||
}
|
||||
ptr += ndof_c * nquad_c;
|
||||
|
||||
b.SetSize(ndof_o);
|
||||
for (int j = 0; j < nquad_o; ++j)
|
||||
{
|
||||
obasis1d.Eval(qo[j], b);
|
||||
for (int i = 0; i < ndof_o; ++i)
|
||||
{
|
||||
ptr[j + i * nquad_o] = b[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurlInterpolator::Kernels::Kernels()
|
||||
{
|
||||
CurlInterpolator::AddSpecialization<3, 1, 1>();
|
||||
CurlInterpolator::AddSpecialization<3, 2, 2>();
|
||||
CurlInterpolator::AddSpecialization<3, 3, 3>();
|
||||
CurlInterpolator::AddSpecialization<3, 4, 4>();
|
||||
CurlInterpolator::AddSpecialization<3, 5, 5>();
|
||||
}
|
||||
|
||||
CurlInterpolator::CurlInterpolator() { static Kernels kernels{}; }
|
||||
|
||||
void CurlInterpolator::AddMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
ApplyPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x,
|
||||
y);
|
||||
}
|
||||
|
||||
void CurlInterpolator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
ApplyTPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x,
|
||||
y);
|
||||
}
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
|
||||
CurlInterpolator::ApplyKernelType
|
||||
CurlInterpolator::ApplyPAKernels::Fallback(int DIM, int, int)
|
||||
{
|
||||
if (DIM == 3)
|
||||
{
|
||||
return internal::CurlInterpolatorApply3DSmem<0, 0>;
|
||||
}
|
||||
MFEM_ABORT("Bad dimension!");
|
||||
}
|
||||
|
||||
CurlInterpolator::ApplyKernelType
|
||||
CurlInterpolator::ApplyTPAKernels::Fallback(int DIM, int, int)
|
||||
{
|
||||
if (DIM == 3)
|
||||
{
|
||||
return internal::CurlInterpolatorTApply3DSmem<0, 0>;
|
||||
}
|
||||
MFEM_ABORT("Bad dimension!");
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "bilininteg_mass_kernels.hpp"
|
||||
#include "bilininteg_mass_pa_simplices.hpp" // IWYU pragma: keep
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -40,10 +39,8 @@ MassIntegrator::Kernels::Kernels()
|
||||
MassIntegrator::AddSpecialization<2,9,10>();
|
||||
// others
|
||||
MassIntegrator::AddSpecialization<2,2,4>();
|
||||
MassIntegrator::AddSpecialization<2,2,5>();
|
||||
MassIntegrator::AddSpecialization<2,3,6>();
|
||||
MassIntegrator::AddSpecialization<2,4,6>();
|
||||
|
||||
// 3D
|
||||
// Q=P+1
|
||||
MassIntegrator::AddSpecialization<3,1,1>();
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#include "../../linalg/vector.hpp"
|
||||
#include "../bilininteg.hpp"
|
||||
|
||||
#include "bilininteg_mass_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -183,12 +181,6 @@ constexpr int NBZ(int D1D)
|
||||
{
|
||||
return ipow(2, D(D1D) >= 0 ? D(D1D) : 0);
|
||||
}
|
||||
constexpr int NBZ3D(int MDQ)
|
||||
{
|
||||
return MDQ > 0 ? std::min<int>(
|
||||
(128 + MDQ * MDQ * MDQ - 1) / (MDQ * MDQ * MDQ), 64)
|
||||
: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Shared memory PA Mass Diagonal 2D kernel
|
||||
@@ -812,23 +804,19 @@ void PAMassApply3D_Element(const int e,
|
||||
}
|
||||
}
|
||||
|
||||
template <int T_D1D, int T_Q1D, int TBATCH, bool ACCUMULATE = true>
|
||||
MFEM_HOST_DEVICE inline void
|
||||
SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
const real_t *d_, const real_t *x_, real_t *y_,
|
||||
int d1d = 0, int q1d = 0)
|
||||
template<int T_D1D, int T_Q1D, bool ACCUMULATE = true>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void SmemPAMassApply3D_Element(const int e,
|
||||
const int NE,
|
||||
const real_t *b_,
|
||||
const real_t *d_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
static_assert(TBATCH > 0, "TBATCH must be positive");
|
||||
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
|
||||
constexpr int tbatch = TBATCH;
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
#else
|
||||
// host always batch size 1
|
||||
constexpr int tbatch = 1;
|
||||
constexpr int tidz = 0;
|
||||
#endif
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int D1D = T_D1D ? T_D1D : d1d;
|
||||
constexpr int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
|
||||
@@ -841,37 +829,33 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
MFEM_SHARED real_t sDQ[MQ1*MD1];
|
||||
real_t (*B)[MD1] = (real_t (*)[MD1]) sDQ;
|
||||
real_t (*Bt)[MQ1] = (real_t (*)[MQ1]) sDQ;
|
||||
MFEM_SHARED real_t sm0[tbatch][MDQ*MDQ*MDQ];
|
||||
MFEM_SHARED real_t sm1[tbatch][MDQ*MDQ*MDQ];
|
||||
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+tidz);
|
||||
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) (sm1+tidz);
|
||||
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm0+tidz);
|
||||
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm1+tidz);
|
||||
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) (sm0+tidz);
|
||||
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm1+tidz);
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
MFEM_SHARED real_t sm0[MDQ*MDQ*MDQ];
|
||||
MFEM_SHARED real_t sm1[MDQ*MDQ*MDQ];
|
||||
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm0;
|
||||
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) sm1;
|
||||
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm0;
|
||||
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm1;
|
||||
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) sm0;
|
||||
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm1;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx, dy, dz, e);
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
}
|
||||
}
|
||||
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
|
||||
}
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
|
||||
B[dx][dy] = b(dx,dy);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, Q1D)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -896,9 +880,9 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy, y, Q1D)
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, Q1D)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -923,9 +907,9 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy, y, Q1D)
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, Q1D)
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
real_t u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -945,22 +929,22 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; qz++)
|
||||
{
|
||||
QQQ[qz][qy][qx] = u[qz] * d(qx, qy, qz, e);
|
||||
QQQ[qz][qy][qx] = u[qz] * d(qx,qy,qz,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (tidz == 0)
|
||||
MFEM_FOREACH_THREAD(di,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(di, y, D1D)
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q, x, Q1D) { Bt[di][q] = b(q, di); }
|
||||
Bt[di][q] = b(q,di);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy, y, Q1D)
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -985,9 +969,9 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -1012,9 +996,9 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1036,11 +1020,11 @@ SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
{
|
||||
if (ACCUMULATE)
|
||||
{
|
||||
y(dx, dy, dz, e) += u[dz];
|
||||
y(dx,dy,dz,e) += u[dz];
|
||||
}
|
||||
else
|
||||
{
|
||||
y(dx, dy, dz, e) = u[dz];
|
||||
y(dx,dy,dz,e) = u[dz];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1131,8 +1115,8 @@ inline void PAMassApply3D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
// Shared memory PA Mass Apply 3D kernel
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int TBATCH=1>
|
||||
// Shared memory PA Mass Apply 2D kernel
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void SmemPAMassApply3D(const int NE,
|
||||
const Array<real_t> &b_,
|
||||
const Array<real_t> &bt_,
|
||||
@@ -1142,9 +1126,6 @@ inline void SmemPAMassApply3D(const int NE,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
static_assert(T_D1D > 0, "T_D1D must be positive");
|
||||
static_assert(T_Q1D > 0, "T_Q1D must be positive");
|
||||
static_assert(TBATCH > 0, "TBATCH must be positive");
|
||||
MFEM_CONTRACT_VAR(bt_);
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
@@ -1156,11 +1137,9 @@ inline void SmemPAMassApply3D(const int NE,
|
||||
const auto d = d_.Read();
|
||||
const auto x = x_.Read();
|
||||
auto y = y_.ReadWrite();
|
||||
mfem::forall_2D_batch<T_Q1D * T_Q1D * TBATCH>(NE, Q1D, Q1D, TBATCH,
|
||||
[=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
internal::SmemPAMassApply3D_Element<T_D1D, T_Q1D, TBATCH>(e, NE, b, d, x,
|
||||
y, d1d, q1d);
|
||||
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1410,57 +1389,42 @@ using ApplyKernelType = MassIntegrator::ApplyKernelType;
|
||||
using DiagonalKernelType = MassIntegrator::DiagonalKernelType;
|
||||
}
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::PAMassApply1D; }
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
constexpr int MDQ = D1D >= Q1D ? D1D : Q1D;
|
||||
// max 64 threads in z limit in cuda and hip
|
||||
if constexpr (MDQ > 0)
|
||||
{
|
||||
return internal::SmemPAMassApply3D<D1D, Q1D,
|
||||
internal::mass::NBZ3D(MDQ)>;
|
||||
}
|
||||
}
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPAMassApply3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
inline ApplyKernelType MassIntegrator::ApplyPAKernels::Fallback(
|
||||
int dim, int, int)
|
||||
int DIM, int, int)
|
||||
{
|
||||
if (dim == 1) { return internal::PAMassApply1D; }
|
||||
else if (dim == 2) { return internal::PAMassApply2D; }
|
||||
else if (dim == 3) { return internal::PAMassApply3D; }
|
||||
if (DIM == 1) { return internal::PAMassApply1D; }
|
||||
else if (DIM == 2) { return internal::PAMassApply2D; }
|
||||
else if (DIM == 3) { return internal::PAMassApply3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
DiagonalKernelType MassIntegrator::DiagonalPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<T_D1D, T_Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
inline DiagonalKernelType MassIntegrator::DiagonalPAKernels::Fallback(
|
||||
int dim, int, int)
|
||||
int DIM, int, int)
|
||||
{
|
||||
if (dim == 1) { return internal::PAMassAssembleDiagonal1D; }
|
||||
else if (dim == 2) { return internal::PAMassAssembleDiagonal2D; }
|
||||
else if (dim == 3) { return internal::PAMassAssembleDiagonal3D; }
|
||||
if (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
|
||||
else if (DIM == 2) { return internal::PAMassAssembleDiagonal2D; }
|
||||
else if (DIM == 3) { return internal::PAMassAssembleDiagonal3D; }
|
||||
else { MFEM_ABORT(""); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../qfunction.hpp"
|
||||
#include "../ceed/integrators/mass/mass.hpp"
|
||||
#include "bilininteg_mass_kernels.hpp"
|
||||
#include "bilininteg_mass_pa_simplices.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -30,11 +29,9 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
// Assuming the same element type
|
||||
fespace = &fes;
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
dim = mesh->Dimension();
|
||||
const FiniteElement &el = *fes.GetTypicalFE();
|
||||
ElementTransformation *T0 = mesh->GetTypicalElementTransformation();
|
||||
const bool stroud = fes.UsesRaggedTensorBasis();
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0, stroud);
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0);
|
||||
if (DeviceCanUseCeed())
|
||||
{
|
||||
delete ceedOp;
|
||||
@@ -51,25 +48,17 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
return;
|
||||
}
|
||||
int map_type = el.GetMapType();
|
||||
dim = mesh->Dimension();
|
||||
ne = fes.GetMesh()->GetNE();
|
||||
nq = ir->GetNPoints();
|
||||
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS, mt);
|
||||
if (stroud)
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
}
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
pa_data.SetSize(ne*nq, mt);
|
||||
|
||||
QuadratureSpace qs(*mesh, *ir);
|
||||
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
|
||||
// QuadratureSpace expects ir defined in reference simplex for Bernstein
|
||||
// elements with partial assembly
|
||||
{
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
@@ -158,10 +147,9 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
const int D1D = dofs1D;
|
||||
const int Q1D = quad1D;
|
||||
const Vector &D = pa_data;
|
||||
const Array<real_t> &B = maps->B;
|
||||
const Array<real_t> &Bt = maps->Bt;
|
||||
|
||||
const Vector &D = pa_data;
|
||||
#ifdef MFEM_USE_OCCA
|
||||
if (DeviceCanUseOcca())
|
||||
{
|
||||
@@ -176,31 +164,7 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
|
||||
}
|
||||
#endif // MFEM_USE_OCCA
|
||||
|
||||
if (fespace->UsesRaggedTensorBasis())
|
||||
{
|
||||
const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
|
||||
|
||||
const Array<real_t> &Ba1 = rmaps->Ba1;
|
||||
const Array<real_t> &Ba2 = rmaps->Ba2;
|
||||
const Array<real_t> &Ba3 = rmaps->Ba3;
|
||||
const Array<real_t> &Ba1t = rmaps->Ba1t;
|
||||
const Array<real_t> &Ba2t = rmaps->Ba2t;
|
||||
const Array<real_t> &Ba3t = rmaps->Ba3t;
|
||||
const Array<int> &lex_map = rmaps->lex_map;
|
||||
const Array<int> &forward_map2d = rmaps->forward_map2d_mass;
|
||||
const Array<int> &inverse_map2d = rmaps->inverse_map2d_mass;
|
||||
const Array<int> &forward_map3d = rmaps->forward_map3d_mass;
|
||||
const Array<int> &inverse_map3d = rmaps->inverse_map3d_mass;
|
||||
ApplySimplexPAKernels::Run(dim, D1D, Q1D, ne, lex_map, forward_map2d,
|
||||
inverse_map2d,
|
||||
forward_map3d, inverse_map3d, Ba1, Ba2, Ba3, Ba1t, Ba2t, Ba3t,
|
||||
D, x, y, D1D, Q1D);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyPAKernels::Run(dim, D1D, Q1D, ne, B, Bt, D, x, y, D1D, Q1D);
|
||||
}
|
||||
ApplyPAKernels::Run(dim, D1D, Q1D, ne, B, Bt, D, x, y, D1D, Q1D);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,8 +177,6 @@ void MassIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(!fespace->UsesRaggedTensorBasis(),
|
||||
"AbsMultPA not implemented for ragged tensor basis");
|
||||
Vector abs_pa_data(pa_data);
|
||||
abs_pa_data.Abs();
|
||||
Array<real_t> absB(maps->B);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,779 +18,6 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void PAHcurlDotSetup2D(const int q1d,
|
||||
const int ne,
|
||||
const bool test_map_integral,
|
||||
const Array<real_t> &w,
|
||||
const Vector &jacobians,
|
||||
const Vector &coeff,
|
||||
Vector &op)
|
||||
{
|
||||
auto W = Reshape(w.Read(), q1d, q1d);
|
||||
auto J = Reshape(jacobians.Read(), q1d, q1d, 2, 2, ne);
|
||||
auto C = Reshape(coeff.Read(), 2, q1d, q1d, ne);
|
||||
auto O = Reshape(op.Write(), 2, q1d, q1d, ne);
|
||||
|
||||
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
const real_t J11 = J(qx, qy, 0, 0, e);
|
||||
const real_t J12 = J(qx, qy, 1, 0, e);
|
||||
const real_t J21 = J(qx, qy, 0, 1, e);
|
||||
const real_t J22 = J(qx, qy, 1, 1, e);
|
||||
const real_t detJ = (J11 * J22) - (J21 * J12);
|
||||
const real_t scale = W(qx, qy) * (test_map_integral ? 1.0 / detJ : 1.0);
|
||||
const real_t Vx = C(0, qx, qy, e);
|
||||
const real_t Vy = C(1, qx, qy, e);
|
||||
|
||||
O(0, qx, qy, e) = scale * ( J22 * Vx - J12 * Vy);
|
||||
O(1, qx, qy, e) = scale * (-J21 * Vx + J11 * Vy);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PAHcurlDotSetup3D(const int q1d,
|
||||
const int ne,
|
||||
const bool test_map_integral,
|
||||
const Array<real_t> &w,
|
||||
const Vector &jacobians,
|
||||
const Vector &coeff,
|
||||
Vector &op)
|
||||
{
|
||||
auto W = Reshape(w.Read(), q1d, q1d, q1d);
|
||||
auto J = Reshape(jacobians.Read(), q1d, q1d, q1d, 3, 3, ne);
|
||||
auto C = Reshape(coeff.Read(), 3, q1d, q1d, q1d, ne);
|
||||
auto O = Reshape(op.Write(), 3, q1d, q1d, q1d, ne);
|
||||
|
||||
mfem::forall_3D(ne, q1d, q1d, q1d, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
const real_t J11 = J(qx, qy, qz, 0, 0, e);
|
||||
const real_t J12 = J(qx, qy, qz, 0, 1, e);
|
||||
const real_t J13 = J(qx, qy, qz, 0, 2, e);
|
||||
const real_t J21 = J(qx, qy, qz, 1, 0, e);
|
||||
const real_t J22 = J(qx, qy, qz, 1, 1, e);
|
||||
const real_t J23 = J(qx, qy, qz, 1, 2, e);
|
||||
const real_t J31 = J(qx, qy, qz, 2, 0, e);
|
||||
const real_t J32 = J(qx, qy, qz, 2, 1, e);
|
||||
const real_t J33 = J(qx, qy, qz, 2, 2, e);
|
||||
const real_t detJ = J11 * (J22 * J33 - J32 * J23)
|
||||
- J21 * (J12 * J33 - J32 * J13)
|
||||
+ J31 * (J12 * J23 - J22 * J13);
|
||||
const real_t scale = W(qx, qy, qz) *
|
||||
(test_map_integral ? 1.0 / detJ : 1.0);
|
||||
const real_t Vx = C(0, qx, qy, qz, e);
|
||||
const real_t Vy = C(1, qx, qy, qz, e);
|
||||
const real_t Vz = C(2, qx, qy, qz, e);
|
||||
|
||||
O(0, qx, qy, qz, e) = scale *
|
||||
((J22 * J33 - J23 * J32) * Vx +
|
||||
(J13 * J32 - J12 * J33) * Vy +
|
||||
(J12 * J23 - J13 * J22) * Vz);
|
||||
O(1, qx, qy, qz, e) = scale *
|
||||
((J23 * J31 - J21 * J33) * Vx +
|
||||
(J11 * J33 - J13 * J31) * Vy +
|
||||
(J13 * J21 - J11 * J23) * Vz);
|
||||
O(2, qx, qy, qz, e) = scale *
|
||||
((J21 * J32 - J22 * J31) * Vx +
|
||||
(J12 * J31 - J11 * J32) * Vy +
|
||||
(J11 * J22 - J12 * J21) * Vz);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PAHcurlDotApply2D(const int d1d,
|
||||
const int d1d_test,
|
||||
const int q1d,
|
||||
const int ne,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bt,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D, "");
|
||||
MFEM_VERIFY(d1d_test <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D, "");
|
||||
|
||||
auto Bo = Reshape(bo.Read(), q1d, d1d - 1);
|
||||
auto Bc = Reshape(bc.Read(), q1d, d1d);
|
||||
auto Bt = Reshape(bt.Read(), d1d_test, q1d);
|
||||
auto O = Reshape(pa_data.Read(), 2, q1d, q1d, ne);
|
||||
auto X = Reshape(x.Read(), 2 * (d1d - 1) * d1d, ne);
|
||||
auto Y = Reshape(y.ReadWrite(), d1d_test, d1d_test, ne);
|
||||
|
||||
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MAX_D1D = DofQuadLimits::MAX_D1D;
|
||||
constexpr int MAX_Q1D = DofQuadLimits::HCURL_MAX_Q1D;
|
||||
|
||||
real_t u0[MAX_Q1D][MAX_Q1D];
|
||||
real_t u1[MAX_Q1D][MAX_Q1D];
|
||||
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
u0[qy][qx] = 0.0;
|
||||
u1[qy][qx] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
int osc = 0;
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
real_t mass_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d - 1; ++dx)
|
||||
{
|
||||
const real_t t = X(dx + (dy * (d1d - 1)) + osc, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bo(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = Bc(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { u0[qy][qx] += mass_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
osc += (d1d - 1) * d1d;
|
||||
for (int dy = 0; dy < d1d - 1; ++dy)
|
||||
{
|
||||
real_t mass_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d; ++dx)
|
||||
{
|
||||
const real_t t = X(dx + (dy * d1d) + osc, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bc(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = Bo(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { u1[qy][qx] += mass_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t sol_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d_test; ++dx) { sol_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(0, qx, qy, e) * u0[qy][qx]
|
||||
+ O(1, qx, qy, e) * u1[qy][qx];
|
||||
for (int dx = 0; dx < d1d_test; ++dx)
|
||||
{
|
||||
sol_x[dx] += s * Bt(dx, qx);
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < d1d_test; ++dy)
|
||||
{
|
||||
const real_t wy = Bt(dy, qy);
|
||||
for (int dx = 0; dx < d1d_test; ++dx)
|
||||
{
|
||||
Y(dx, dy, e) += sol_x[dx] * wy;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PAHcurlDotApplyTranspose2D(const int d1d,
|
||||
const int d1d_test,
|
||||
const int q1d,
|
||||
const int ne,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &b,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D, "");
|
||||
MFEM_VERIFY(d1d_test <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D, "");
|
||||
|
||||
auto Bo = Reshape(bo.Read(), q1d, d1d - 1);
|
||||
auto Bc = Reshape(bc.Read(), q1d, d1d);
|
||||
auto B = Reshape(b.Read(), q1d, d1d_test);
|
||||
auto O = Reshape(pa_data.Read(), 2, q1d, q1d, ne);
|
||||
auto X = Reshape(x.Read(), d1d_test, d1d_test, ne);
|
||||
auto Y = Reshape(y.ReadWrite(), 2 * (d1d - 1) * d1d, ne);
|
||||
|
||||
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MAX_D1D = DofQuadLimits::MAX_D1D;
|
||||
constexpr int MAX_Q1D = DofQuadLimits::HCURL_MAX_Q1D;
|
||||
|
||||
real_t mass[MAX_Q1D][MAX_Q1D];
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
mass[qy][qx] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int dy = 0; dy < d1d_test; ++dy)
|
||||
{
|
||||
real_t sol_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { sol_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d_test; ++dx)
|
||||
{
|
||||
const real_t t = X(dx, dy, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { sol_x[qx] += t * B(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = B(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass[qy][qx] += sol_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
int osc = 0;
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t mass_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d - 1; ++dx) { mass_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(0, qx, qy, e) * mass[qy][qx];
|
||||
for (int dx = 0; dx < d1d - 1; ++dx) { mass_x[dx] += s * Bo(qx, dx); }
|
||||
}
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
const real_t wy = Bc(qy, dy);
|
||||
for (int dx = 0; dx < d1d - 1; ++dx)
|
||||
{
|
||||
Y(dx + (dy * (d1d - 1)) + osc, e) += mass_x[dx] * wy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osc += (d1d - 1) * d1d;
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t mass_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(1, qx, qy, e) * mass[qy][qx];
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_x[dx] += s * Bc(qx, dx); }
|
||||
}
|
||||
for (int dy = 0; dy < d1d - 1; ++dy)
|
||||
{
|
||||
const real_t wy = Bo(qy, dy);
|
||||
for (int dx = 0; dx < d1d; ++dx)
|
||||
{
|
||||
Y(dx + (dy * d1d) + osc, e) += mass_x[dx] * wy;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PAHcurlDotApply3D(const int d1d,
|
||||
const int d1d_test,
|
||||
const int q1d,
|
||||
const int ne,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &bt,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D, "");
|
||||
MFEM_VERIFY(d1d_test <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D, "");
|
||||
|
||||
auto Bo = Reshape(bo.Read(), q1d, d1d - 1);
|
||||
auto Bc = Reshape(bc.Read(), q1d, d1d);
|
||||
auto Bt = Reshape(bt.Read(), d1d_test, q1d);
|
||||
auto O = Reshape(pa_data.Read(), 3, q1d, q1d, q1d, ne);
|
||||
auto X = Reshape(x.Read(), 3 * (d1d - 1) * d1d * d1d, ne);
|
||||
auto Y = Reshape(y.ReadWrite(), d1d_test, d1d_test, d1d_test, ne);
|
||||
|
||||
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MAX_D1D = DofQuadLimits::MAX_D1D;
|
||||
constexpr int MAX_Q1D = DofQuadLimits::HCURL_MAX_Q1D;
|
||||
|
||||
real_t u[MAX_Q1D][MAX_Q1D][MAX_Q1D][3];
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
for (int c = 0; c < 3; ++c) { u[qz][qy][qx][c] = 0.0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int osc = 0;
|
||||
for (int dz = 0; dz < d1d; ++dz)
|
||||
{
|
||||
real_t mass_xy[MAX_Q1D][MAX_Q1D];
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
real_t mass_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d - 1; ++dx)
|
||||
{
|
||||
const real_t t = X(dx + ((dy + (dz * d1d)) * (d1d - 1)) + osc, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bo(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = Bc(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] += mass_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
const real_t wz = Bc(qz, dz);
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { u[qz][qy][qx][0] += mass_xy[qy][qx] * wz; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osc += (d1d - 1) * d1d * d1d;
|
||||
for (int dz = 0; dz < d1d; ++dz)
|
||||
{
|
||||
real_t mass_xy[MAX_Q1D][MAX_Q1D];
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int dy = 0; dy < d1d - 1; ++dy)
|
||||
{
|
||||
real_t mass_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d; ++dx)
|
||||
{
|
||||
const real_t t = X(dx + ((dy + (dz * (d1d - 1))) * d1d) + osc, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bc(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = Bo(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] += mass_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
const real_t wz = Bc(qz, dz);
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { u[qz][qy][qx][1] += mass_xy[qy][qx] * wz; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osc += (d1d - 1) * d1d * d1d;
|
||||
for (int dz = 0; dz < d1d - 1; ++dz)
|
||||
{
|
||||
real_t mass_xy[MAX_Q1D][MAX_Q1D];
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
real_t mass_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d; ++dx)
|
||||
{
|
||||
const real_t t = X(dx + ((dy + (dz * d1d)) * d1d) + osc, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bc(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = Bc(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] += mass_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
const real_t wz = Bo(qz, dz);
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { u[qz][qy][qx][2] += mass_xy[qy][qx] * wz; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
real_t mass_xy[MAX_D1D][MAX_D1D];
|
||||
for (int dy = 0; dy < d1d_test; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d_test; ++dx) { mass_xy[dy][dx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t mass_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d_test; ++dx) { mass_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(0, qx, qy, qz, e) * u[qz][qy][qx][0]
|
||||
+ O(1, qx, qy, qz, e) * u[qz][qy][qx][1]
|
||||
+ O(2, qx, qy, qz, e) * u[qz][qy][qx][2];
|
||||
for (int dx = 0; dx < d1d_test; ++dx) { mass_x[dx] += s * Bt(dx, qx); }
|
||||
}
|
||||
for (int dy = 0; dy < d1d_test; ++dy)
|
||||
{
|
||||
const real_t wy = Bt(dy, qy);
|
||||
for (int dx = 0; dx < d1d_test; ++dx) { mass_xy[dy][dx] += mass_x[dx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < d1d_test; ++dz)
|
||||
{
|
||||
const real_t wz = Bt(dz, qz);
|
||||
for (int dy = 0; dy < d1d_test; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d_test; ++dx)
|
||||
{
|
||||
Y(dx, dy, dz, e) += mass_xy[dy][dx] * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PAHcurlDotApplyTranspose3D(const int d1d,
|
||||
const int d1d_test,
|
||||
const int q1d,
|
||||
const int ne,
|
||||
const Array<real_t> &bo,
|
||||
const Array<real_t> &bc,
|
||||
const Array<real_t> &b,
|
||||
const Vector &pa_data,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D, "");
|
||||
MFEM_VERIFY(d1d_test <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D, "");
|
||||
|
||||
auto Bo = Reshape(bo.Read(), q1d, d1d - 1);
|
||||
auto Bc = Reshape(bc.Read(), q1d, d1d);
|
||||
auto B = Reshape(b.Read(), q1d, d1d_test);
|
||||
auto O = Reshape(pa_data.Read(), 3, q1d, q1d, q1d, ne);
|
||||
auto X = Reshape(x.Read(), d1d_test, d1d_test, d1d_test, ne);
|
||||
auto Y = Reshape(y.ReadWrite(), 3 * (d1d - 1) * d1d * d1d, ne);
|
||||
|
||||
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MAX_D1D = DofQuadLimits::MAX_D1D;
|
||||
constexpr int MAX_Q1D = DofQuadLimits::HCURL_MAX_Q1D;
|
||||
|
||||
real_t mass[MAX_Q1D][MAX_Q1D][MAX_Q1D];
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass[qz][qy][qx] = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < d1d_test; ++dz)
|
||||
{
|
||||
real_t mass_xy[MAX_Q1D][MAX_Q1D];
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int dy = 0; dy < d1d_test; ++dy)
|
||||
{
|
||||
real_t mass_x[MAX_Q1D];
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
|
||||
for (int dx = 0; dx < d1d_test; ++dx)
|
||||
{
|
||||
const real_t t = X(dx, dy, dz, e);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * B(qx, dx); }
|
||||
}
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
const real_t wy = B(qy, dy);
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass_xy[qy][qx] += mass_x[qx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
const real_t wz = B(qz, dz);
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < q1d; ++qx) { mass[qz][qy][qx] += mass_xy[qy][qx] * wz; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int osc = 0;
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
real_t mass_xy[MAX_D1D][MAX_D1D];
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d - 1; ++dx) { mass_xy[dy][dx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t mass_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d - 1; ++dx) { mass_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(0, qx, qy, qz, e) * mass[qz][qy][qx];
|
||||
for (int dx = 0; dx < d1d - 1; ++dx) { mass_x[dx] += s * Bo(qx, dx); }
|
||||
}
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
const real_t wy = Bc(qy, dy);
|
||||
for (int dx = 0; dx < d1d - 1; ++dx) { mass_xy[dy][dx] += mass_x[dx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < d1d; ++dz)
|
||||
{
|
||||
const real_t wz = Bc(qz, dz);
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d - 1; ++dx)
|
||||
{
|
||||
Y(dx + ((dy + (dz * d1d)) * (d1d - 1)) + osc, e) += mass_xy[dy][dx] * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osc += (d1d - 1) * d1d * d1d;
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
real_t mass_xy[MAX_D1D][MAX_D1D];
|
||||
for (int dy = 0; dy < d1d - 1; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_xy[dy][dx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t mass_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(1, qx, qy, qz, e) * mass[qz][qy][qx];
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_x[dx] += s * Bc(qx, dx); }
|
||||
}
|
||||
for (int dy = 0; dy < d1d - 1; ++dy)
|
||||
{
|
||||
const real_t wy = Bo(qy, dy);
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_xy[dy][dx] += mass_x[dx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < d1d; ++dz)
|
||||
{
|
||||
const real_t wz = Bc(qz, dz);
|
||||
for (int dy = 0; dy < d1d - 1; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d; ++dx)
|
||||
{
|
||||
Y(dx + ((dy + (dz * (d1d - 1))) * d1d) + osc, e) += mass_xy[dy][dx] * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osc += (d1d - 1) * d1d * d1d;
|
||||
for (int qz = 0; qz < q1d; ++qz)
|
||||
{
|
||||
real_t mass_xy[MAX_D1D][MAX_D1D];
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_xy[dy][dx] = 0.0; }
|
||||
}
|
||||
|
||||
for (int qy = 0; qy < q1d; ++qy)
|
||||
{
|
||||
real_t mass_x[MAX_D1D];
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_x[dx] = 0.0; }
|
||||
for (int qx = 0; qx < q1d; ++qx)
|
||||
{
|
||||
const real_t s = O(2, qx, qy, qz, e) * mass[qz][qy][qx];
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_x[dx] += s * Bc(qx, dx); }
|
||||
}
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
const real_t wy = Bc(qy, dy);
|
||||
for (int dx = 0; dx < d1d; ++dx) { mass_xy[dy][dx] += mass_x[dx] * wy; }
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < d1d - 1; ++dz)
|
||||
{
|
||||
const real_t wz = Bo(qz, dz);
|
||||
for (int dy = 0; dy < d1d; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < d1d; ++dx)
|
||||
{
|
||||
Y(dx + ((dy + (dz * d1d)) * d1d) + osc, e) += mass_xy[dy][dx] * wz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MixedDotProductIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes)
|
||||
{
|
||||
Mesh *mesh = trial_fes.GetMesh();
|
||||
const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
|
||||
const FiniteElement *test_fel = test_fes.GetTypicalFE();
|
||||
|
||||
const VectorTensorFiniteElement *trial_el =
|
||||
dynamic_cast<const VectorTensorFiniteElement*>(trial_fel);
|
||||
MFEM_VERIFY(trial_el != NULL, "Only VectorTensorFiniteElement is supported!");
|
||||
|
||||
const TensorBasisElement *test_tensor_el =
|
||||
dynamic_cast<const TensorBasisElement*>(test_fel);
|
||||
MFEM_VERIFY(test_tensor_el != NULL,
|
||||
"Only tensor-product scalar test elements are supported!");
|
||||
|
||||
MFEM_VERIFY(trial_el->GetDerivType() == mfem::FiniteElement::CURL,
|
||||
"Only H(curl) trial spaces are supported!");
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == nullptr)
|
||||
{
|
||||
const int order = trial_fel->GetOrder() + test_fel->GetOrder()
|
||||
+ mesh->GetTypicalElementTransformation()->OrderW();
|
||||
ir = &IntRules.Get(trial_fel->GetGeomType(), order);
|
||||
}
|
||||
|
||||
dim = mesh->Dimension();
|
||||
MFEM_VERIFY(dim == 2 || dim == 3, "Unsupported dimension!");
|
||||
MFEM_VERIFY(trial_el->GetDim() == dim && test_fel->GetDim() == dim,
|
||||
"Trial/test dimension mismatch.");
|
||||
|
||||
ne = trial_fes.GetNE();
|
||||
MFEM_VERIFY(ne == test_fes.GetNE(),
|
||||
"Different meshes for test and trial spaces");
|
||||
|
||||
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
|
||||
mapsC = &trial_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
mapsO = &trial_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
|
||||
mapsTest = &test_fel->GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
|
||||
dofs1D = mapsC->ndof;
|
||||
dofs1Dtest = mapsTest->ndof;
|
||||
quad1D = mapsC->nqpt;
|
||||
test_map_integral = (test_fel->GetMapType() == FiniteElement::INTEGRAL);
|
||||
|
||||
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
|
||||
MFEM_VERIFY(quad1D == mapsTest->nqpt, "Trial/test quadrature mismatch");
|
||||
MFEM_VERIFY(dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D, "");
|
||||
MFEM_VERIFY(dofs1Dtest <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(quad1D <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D, "");
|
||||
|
||||
const int nq = ir->GetNPoints();
|
||||
if (dim == 2) { MFEM_VERIFY(nq == quad1D * quad1D, ""); }
|
||||
else { MFEM_VERIFY(nq == quad1D * quad1D * quad1D, ""); }
|
||||
|
||||
QuadratureSpace qs(*mesh, *ir);
|
||||
CoefficientVector coeff(*VQ, qs, CoefficientStorage::FULL);
|
||||
MFEM_VERIFY(coeff.GetVDim() == dim, "Vector coefficient dimension mismatch.");
|
||||
|
||||
pa_data.SetSize(dim * nq * ne, Device::GetMemoryType());
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
PAHcurlDotSetup2D(quad1D, ne, test_map_integral, ir->GetWeights(),
|
||||
geom->J, coeff, pa_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
PAHcurlDotSetup3D(quad1D, ne, test_map_integral, ir->GetWeights(),
|
||||
geom->J, coeff, pa_data);
|
||||
}
|
||||
}
|
||||
|
||||
void MixedDotProductIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
PAHcurlDotApply2D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
mapsO->B, mapsC->B, mapsTest->Bt, pa_data, x, y);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
PAHcurlDotApply3D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
mapsO->B, mapsC->B, mapsTest->Bt, pa_data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported dimension!");
|
||||
}
|
||||
}
|
||||
|
||||
void MixedDotProductIntegrator::AddMultTransposePA(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
PAHcurlDotApplyTranspose2D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
mapsO->B, mapsC->B, mapsTest->B,
|
||||
pa_data, x, y);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
PAHcurlDotApplyTranspose3D(dofs1D, dofs1Dtest, quad1D, ne,
|
||||
mapsO->B, mapsC->B, mapsTest->B,
|
||||
pa_data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported dimension!");
|
||||
}
|
||||
}
|
||||
|
||||
void MixedScalarCurlIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes)
|
||||
{
|
||||
@@ -1093,7 +320,6 @@ void MixedVectorWeakCurlIntegrator::AssemblePA(const FiniteElementSpace
|
||||
CoefficientVector coeff(qs, CoefficientStorage::FULL);
|
||||
if (Q) { coeff.Project(*Q); }
|
||||
else if (DQ) { coeff.Project(*DQ); }
|
||||
else if (MQ) { MFEM_ABORT("Not implemented."); }
|
||||
else { coeff.SetConstant(1.0); }
|
||||
|
||||
if (trialType == mfem::FiniteElement::CURL && dim == 3)
|
||||
|
||||
@@ -1,131 +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_BILININTEG_VECTORFEMASS_KERNELS_HPP
|
||||
#define MFEM_BILININTEG_VECTORFEMASS_KERNELS_HPP
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
#include "../../general/array.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../linalg/vector.hpp"
|
||||
#include "../bilininteg.hpp"
|
||||
|
||||
#include "bilininteg_diffusion_kernels.hpp"
|
||||
#include "bilininteg_hcurl_kernels.hpp"
|
||||
#include "bilininteg_hdiv_kernels.hpp"
|
||||
#include "bilininteg_hcurlhdiv_kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
namespace internal
|
||||
{
|
||||
namespace hcurlmass
|
||||
{
|
||||
constexpr int NBZ3D(int d1d, int q1d)
|
||||
{
|
||||
if (d1d <= 1 || q1d <= 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
// assume q1d >= d1d
|
||||
// z dimension is capped at 64 on nvidia and amd gpus
|
||||
int tmp = std::min((128 + q1d * q1d * q1d - 1) / (q1d * q1d * q1d), 64);
|
||||
int smem_req =
|
||||
sizeof(mfem::real_t) *
|
||||
(3 * ((d1d - 1) * d1d * d1d + 2 * q1d * q1d * q1d) * tmp +
|
||||
q1d * (d1d - 1) + q1d * d1d);
|
||||
// assume GPU has at least 48k shared memory
|
||||
return std::max(std::min(tmp, (48 * 1024 + smem_req - 1) / smem_req), 1);
|
||||
}
|
||||
} // namespace hcurlmass
|
||||
} // namespace internal
|
||||
|
||||
template <FiniteElement::DerivType TrialType, FiniteElement::DerivType TestType,
|
||||
int DIM, int TrialD1D, int TestD1D, int Q1D>
|
||||
VectorFEMassIntegrator::ApplyKernelType
|
||||
VectorFEMassIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
constexpr bool trial_curl = (TrialType == mfem::FiniteElement::CURL);
|
||||
constexpr bool trial_div = (TrialType == mfem::FiniteElement::DIV);
|
||||
constexpr bool test_curl = (TestType == mfem::FiniteElement::CURL);
|
||||
constexpr bool test_div = (TestType == mfem::FiniteElement::DIV);
|
||||
|
||||
if constexpr (DIM == 3)
|
||||
{
|
||||
if constexpr (trial_curl && test_curl)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
// assume TrialD1D == TestD1D
|
||||
return internal::SmemPAHcurlMassApply3D<
|
||||
TrialD1D, Q1D, internal::hcurlmass::NBZ3D(TrialD1D, Q1D)>;
|
||||
}
|
||||
else
|
||||
{
|
||||
return internal::PAHcurlMassApply3D;
|
||||
}
|
||||
}
|
||||
else if constexpr (trial_div && test_div)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
// assumes TrialD1D == TestD1D
|
||||
return internal::SmemPAHdivMassApply3D<TrialD1D, Q1D>;
|
||||
}
|
||||
else
|
||||
{
|
||||
return internal::PAHdivMassApply3D;
|
||||
}
|
||||
}
|
||||
else if constexpr (trial_curl && test_div)
|
||||
{
|
||||
return internal::PAHdivHcurlMassApply3D;
|
||||
}
|
||||
else if constexpr (trial_div && test_curl)
|
||||
{
|
||||
return internal::PAHcurlHdivMassApply3D;
|
||||
}
|
||||
}
|
||||
else if constexpr (DIM == 2) // 2D
|
||||
{
|
||||
if constexpr (trial_curl && test_curl)
|
||||
{
|
||||
return internal::PAHcurlMassApply2D;
|
||||
}
|
||||
else if constexpr (trial_div && test_div)
|
||||
{
|
||||
if (Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
// assumes TrialD1D == TestD1D
|
||||
return internal::SmemPAHdivMassApply2D<TrialD1D, Q1D>;
|
||||
}
|
||||
else
|
||||
{
|
||||
return internal::PAHdivMassApply2D;
|
||||
}
|
||||
}
|
||||
else if constexpr (trial_curl && test_div)
|
||||
{
|
||||
return internal::PAHdivHcurlMassApply2D;
|
||||
}
|
||||
else if constexpr (trial_div && test_curl)
|
||||
{
|
||||
return internal::PAHcurlHdivMassApply2D;
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user