Compare commits
87
Commits
gpu-maxwell
...
bubble
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc7ebccc54 | ||
|
|
9e423f2f8e | ||
|
|
c14938cd1c | ||
|
|
419758be1e | ||
|
|
4444d8ed70 | ||
|
|
48670b9f87 | ||
|
|
bddb52ace5 | ||
|
|
2d99e1e2de | ||
|
|
fdad993654 | ||
|
|
7f51024345 | ||
|
|
0d00e79cd5 | ||
|
|
b406cdaf79 | ||
|
|
8adba4e1bb | ||
|
|
2b86c7300b | ||
|
|
7357a9b4bf | ||
|
|
b44728af9e | ||
|
|
8d002d09c8 | ||
|
|
fed8e6bc1b | ||
|
|
0aa0ac0637 | ||
|
|
77ef843c2e | ||
|
|
51bc8037d4 | ||
|
|
523c208d87 | ||
|
|
90e0e8e289 | ||
|
|
3f41665e4f | ||
|
|
6f07de9114 | ||
|
|
9f74ee130a | ||
|
|
0a7eb2c39e | ||
|
|
d85723ce29 | ||
|
|
b33340edab | ||
|
|
9c1bf9704d | ||
|
|
c58816905d | ||
|
|
ded65cf364 | ||
|
|
422f42ec0d | ||
|
|
acac245260 | ||
|
|
ed0b39b732 | ||
|
|
d609bee2cc | ||
|
|
421f3f03ba | ||
|
|
842f88a7b3 | ||
|
|
abf97587d4 | ||
|
|
355a2cd570 | ||
|
|
e7e184a24d | ||
|
|
616eaec18e | ||
|
|
5c0711f334 | ||
|
|
999e4c4f46 | ||
|
|
214750edc8 | ||
|
|
4cf708d4fa | ||
|
|
91a40a1d1a | ||
|
|
dffe36f382 | ||
|
|
49363859ee | ||
|
|
9f47892f62 | ||
|
|
1c286184be | ||
|
|
6657cf2760 | ||
|
|
731224d5e8 | ||
|
|
a8a85c68fb | ||
|
|
591cc1ca41 | ||
|
|
6b1c2644e6 | ||
|
|
7742ad8355 | ||
|
|
275e98264c | ||
|
|
cd4d7c292f | ||
|
|
e130ae7dd8 | ||
|
|
e026e15fe3 | ||
|
|
493b5a942e | ||
|
|
f27a13cbad | ||
|
|
559d0e42c7 | ||
|
|
5ba64a774e | ||
|
|
896e251d3a | ||
|
|
9b0c9d3f6e | ||
|
|
c90d6f9d60 | ||
|
|
6f2b8b82d1 | ||
|
|
90cf6af2bb | ||
|
|
cd6bfc7de8 | ||
|
|
b42ad0a57c | ||
|
|
8e67185297 | ||
|
|
456c236cc5 | ||
|
|
f80902b776 | ||
|
|
4c9f6edef0 | ||
|
|
45ec9d451d | ||
|
|
988ab81e5f | ||
|
|
3a7b1d7c67 | ||
|
|
411ffcfef6 | ||
|
|
27d79fc463 | ||
|
|
898337f772 | ||
|
|
d3063a0982 | ||
|
|
15853215ff | ||
|
|
8b0e9ff064 | ||
|
|
03973ad244 | ||
|
|
88c04a6e45 |
@@ -25,7 +25,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
if: ${{env.DEBUG == 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
|
||||
@@ -36,7 +36,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v5
|
||||
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
|
||||
@@ -23,7 +23,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
|
||||
- uses: actions/cache/restore@v5 # Cache for LLVM libcxx
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
@@ -32,14 +32,14 @@ runs:
|
||||
- uses: ./.github/actions/sanitize/mpi
|
||||
if: ${{inputs.par == 'true'}}
|
||||
|
||||
- uses: actions/cache/restore@v4 # Cache for Hypre
|
||||
- uses: actions/cache/restore@v5 # Cache for Hypre
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
|
||||
- uses: actions/cache/restore@v4 # Cache for Metis
|
||||
- uses: actions/cache/restore@v5 # Cache for Metis
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
@@ -51,13 +51,13 @@ runs:
|
||||
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
|
||||
- uses: actions/cache/restore@v5 # Cache for LSAN suppression file
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
- uses: actions/checkout@v4 # Checkout the repository
|
||||
- uses: actions/checkout@v6 # Checkout the repository
|
||||
with:
|
||||
path: mfem
|
||||
# ref: ${{env.BRANCH}}
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
remove-docker-images: 'true'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# It's easier to reference named variables than indexes of the matrix
|
||||
- name: Set Environment
|
||||
|
||||
@@ -153,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@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ env.MFEM_TOP_DIR }}
|
||||
# Fetch the complete history for codecov to access commits ID
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
- name: cache hypre
|
||||
id: hypre-cache
|
||||
if: matrix.mpi == 'par'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
@@ -255,7 +255,7 @@ jobs:
|
||||
- name: cache metis
|
||||
id: metis-cache
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
@@ -270,7 +270,7 @@ jobs:
|
||||
- name: cache vcpkg (Windows)
|
||||
id: vcpkg-cache
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: vcpkg_cache
|
||||
key: ${{ runner.os }}-${{ matrix.mpi }}-vcpkg-v1
|
||||
|
||||
@@ -40,11 +40,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v4
|
||||
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@v2
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
# ℹ️ 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@v2
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout MFEM
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
- name: Cache Metis Install
|
||||
id: metis-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- 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@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- 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@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: get doxygen and graphviz
|
||||
run: |
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 2.19.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
|
||||
@@ -27,13 +27,13 @@ jobs:
|
||||
llvm_use_sanitizer: "Undefined"
|
||||
name: ${{matrix.sanitizer}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
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@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 4.0.3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/mfem
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
env:
|
||||
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -165,7 +165,7 @@ jobs:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
np: ${{inputs.par && '_np=2' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
|
||||
@@ -8,6 +8,22 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.10 (development)
|
||||
==========================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- 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 & Vincent, Comput. Math. Appl. 69(10):1232-1241,
|
||||
2015.
|
||||
Tet rules (d=1-13): Witherden & Vincent (ibid).
|
||||
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
|
||||
2022.
|
||||
|
||||
|
||||
Version 4.9.1 (development)
|
||||
===========================
|
||||
|
||||
|
||||
+5
-1
@@ -652,6 +652,8 @@ foreach(TPL IN LISTS MFEM_TPLS)
|
||||
endif()
|
||||
endforeach(TPL)
|
||||
|
||||
# reverse to remove the first instance of entries in TPL_LIBRARIES
|
||||
# so later duplicates are kept (for dependency ordering)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES TPL_LIBRARIES)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
@@ -1015,5 +1017,7 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
|
||||
# define install rules for 'config.mk' and 'test.mk'
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
|
||||
option(MFEM_EXPORT_GPU_CONFIG "Export config.mk for GPU-enabled downstream packages" ON)
|
||||
endif()
|
||||
mfem_export_mk_files()
|
||||
|
||||
@@ -701,7 +701,6 @@ endfunction(mfem_find_library)
|
||||
# Extract compile and link options needed by the given target.
|
||||
#
|
||||
function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
|
||||
if (NOT TARGET ${Target})
|
||||
return()
|
||||
endif()
|
||||
@@ -799,7 +798,12 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
# message(STATUS "Lib = ${Lib}")
|
||||
# Filter-out generator expressions
|
||||
if (NOT ("${Lib}" MATCHES "^\\$"))
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
if(NOT ("${Lib}" STREQUAL "dl"))
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
else()
|
||||
# for some reason libdl doesn't include the "-l"
|
||||
list(APPEND LinkOpts "-ldl")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
mfem_get_target_options(${Lib} COpts LOpts)
|
||||
@@ -888,9 +892,18 @@ function(mfem_export_mk_files)
|
||||
set(${var} NO)
|
||||
endif()
|
||||
endforeach()
|
||||
# TODO: Add support for MFEM_USE_CUDA=YES
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${MFEM_CXX})
|
||||
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
|
||||
set(MFEM_CXX ${CMAKE_CUDA_COMPILER})
|
||||
if(MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_HOST_CXX ${CMAKE_CUDA_HOST_COMPILER})
|
||||
else()
|
||||
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
else()
|
||||
# mfem doesn't use enable_language(HIP)
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
set(MFEM_CPPFLAGS "")
|
||||
get_target_property(cxx_std mfem CXX_STANDARD)
|
||||
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
|
||||
@@ -900,6 +913,50 @@ function(mfem_export_mk_files)
|
||||
string(STRIP
|
||||
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
|
||||
MFEM_CXXFLAGS)
|
||||
if(MFEM_EXPORT_GPU_CONFIG)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} ${CMAKE_CUDA_FLAGS}")
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_CXXFLAGS "-x=cu ${MFEM_CXXFLAGS} -ccbin ${CMAKE_CXX_COMPILER} --forward-unknown-to-host-compiler")
|
||||
# The following intentionally hides CUDA deprecation warnings
|
||||
foreach(ENTRY IN LISTS CUDAToolkit_INCLUDE_DIRS)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -isystem ${ENTRY}")
|
||||
endforeach()
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
|
||||
# architecture flags not part of CMAKE_CUDA_FLAGS
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -arch=${CMAKE_CUDA_ARCHITECTURES}")
|
||||
else()
|
||||
foreach (ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS
|
||||
"${MFEM_CXXFLAGS} -gencode arch=compute_${ENTRY},code=sm_${ENTRY}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xcuda --cuda-path=${CUDAToolkit_LIBRARY_ROOT}")
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
|
||||
# architecture flags not part of CMAKE_CUDA_FLAGS
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
# TODO: not supported
|
||||
else()
|
||||
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS "-cuda-gpu-arch=sm_${ENTRY} ${MFEM_CXXFLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xhip")
|
||||
foreach(ENTRY IN LISTS CMAKE_HIP_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS "--offload-arch=${ENTRY} ${MFEM_CXXFLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
set(MFEM_TPLFLAGS "")
|
||||
foreach(dir ${TPL_INCLUDE_DIRS})
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} -I${dir}")
|
||||
@@ -930,6 +987,9 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_SHARED NO)
|
||||
set(MFEM_STATIC YES)
|
||||
endif()
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} -lcudart")
|
||||
endif()
|
||||
set(MFEM_BUILD_TAG "${CMAKE_SYSTEM}")
|
||||
set(MFEM_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
# For the next 4 variables, these are the values for the build-tree version of
|
||||
@@ -938,8 +998,15 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_LIB_DIR "${PROJECT_BINARY_DIR}")
|
||||
set(MFEM_TEST_MK "${PROJECT_SOURCE_DIR}/config/test.mk")
|
||||
set(MFEM_CONFIG_EXTRA "MFEM_BUILD_DIR ?= ${PROJECT_BINARY_DIR}")
|
||||
# TODO: CUDA/HIP support:
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_XLINKER "-Xlinker=")
|
||||
else()
|
||||
set(MFEM_XLINKER "${CMAKE_CUDA_LINKER_WRAPPER_FLAG}")
|
||||
endif()
|
||||
else()
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
endif()
|
||||
set(MFEM_MPIEXEC ${MPIEXEC})
|
||||
if (NOT MFEM_MPIEXEC)
|
||||
set(MFEM_MPIEXEC "mpirun")
|
||||
@@ -987,16 +1054,21 @@ function(mfem_export_mk_files)
|
||||
# handle interfaces (e.g., SCOREC::apf)
|
||||
if ("${lib}" MATCHES "SCOREC::.*" OR "${lib}" MATCHES "Ginkgo::.*" OR "${lib}" MATCHES "ParMoonolith::.*")
|
||||
elseif (TARGET "${lib}")
|
||||
mfem_get_target_options(${lib} CompileOpts LinkOpts)
|
||||
mfem_get_target_options(${lib} CompileOpts2 LinkOpts2)
|
||||
# remove generator expressions
|
||||
string(GENEX_STRIP "${CompileOpts2}" CompileOpts)
|
||||
string(GENEX_STRIP "${LinkOpts2}" LinkOpts)
|
||||
# Removing duplicates may lead to issues:
|
||||
# list(REMOVE_DUPLICATES CompileOpts)
|
||||
# list(REMOVE_DUPLICATES LinkOpts)
|
||||
string(REPLACE ";" " " COpts "${CompileOpts}")
|
||||
string(REPLACE ";" " " LOpts "${LinkOpts}")
|
||||
# message(STATUS "${lib}[COpts]: '${COpts}'")
|
||||
# message(STATUS "${lib}[LOpts]: '${LOpts}'")
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpts}")
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpts}")
|
||||
# message(WARNING "${lib}[LinkOpts]: ${LinkOpts}")
|
||||
# message(WARNING "${lib}[CompileOpts]: ${CompileOpts}")
|
||||
foreach(LOpt IN LISTS LinkOpts)
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpt}")
|
||||
endforeach()
|
||||
foreach(COpt IN LISTS CompileOpts)
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpt}")
|
||||
endforeach()
|
||||
# message(FATAL_ERROR "***** interface lib found ... exiting *****")
|
||||
# handle static and shared libs
|
||||
elseif ("${suffix}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
@@ -1004,7 +1076,7 @@ function(mfem_export_mk_files)
|
||||
get_filename_component(fullLibName ${lib} NAME_WE)
|
||||
string(REGEX REPLACE "^lib" "" libname ${fullLibName})
|
||||
set(MFEM_EXT_LIBS
|
||||
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
|
||||
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
|
||||
else()
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${lib}")
|
||||
endif()
|
||||
@@ -1013,7 +1085,7 @@ function(mfem_export_mk_files)
|
||||
# Create the build-tree version of 'config.mk'
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk")
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk" @ONLY)
|
||||
# Copy 'test.mk' from the source-tree to the build-tree
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/test.mk"
|
||||
@@ -1031,7 +1103,7 @@ function(mfem_export_mk_files)
|
||||
# Create the install-tree version of 'config.mk'
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk")
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk" @ONLY)
|
||||
|
||||
# Install rules for 'config.mk' and 'test.mk'
|
||||
install(FILES ${PROJECT_SOURCE_DIR}/config/test.mk
|
||||
|
||||
+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 462 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 464 -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 81
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 82
|
||||
// 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
|
||||
|
||||
+27
-9
@@ -302,15 +302,21 @@ 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
|
||||
@@ -534,15 +540,21 @@ 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)
|
||||
{
|
||||
@@ -551,15 +563,21 @@ 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)
|
||||
{
|
||||
|
||||
+2
-8
@@ -97,13 +97,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
args.ParseCheck();
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
const char *mesh_file = "../data/disc-nurbs.mesh";
|
||||
@@ -128,7 +122,7 @@ int main(int argc, char *argv[])
|
||||
*nodes /= scale;
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
H1_FECollection H1fec(order+1, dim);
|
||||
H1Bubble_FECollection H1fec(order, order - 1, dim);
|
||||
FiniteElementSpace H1fes(&mesh, &H1fec);
|
||||
|
||||
L2_FECollection L2fec(order-1, dim);
|
||||
|
||||
+2
-14
@@ -103,19 +103,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
args.ParseCheck();
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
const char *mesh_file = "../data/disc-nurbs.mesh";
|
||||
@@ -143,7 +131,7 @@ int main(int argc, char *argv[])
|
||||
mesh.Clear();
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
H1_FECollection H1fec(order+1, dim);
|
||||
H1Bubble_FECollection H1fec(order, order - 1, dim);
|
||||
ParFiniteElementSpace H1fes(&pmesh, &H1fec);
|
||||
|
||||
L2_FECollection L2fec(order-1, dim);
|
||||
|
||||
+3
-1
@@ -73,6 +73,7 @@ set(SRCS
|
||||
fe/fe_base.cpp
|
||||
fe/fe_fixed_order.cpp
|
||||
fe/fe_h1.cpp
|
||||
fe/fe_h1_bubble.cpp
|
||||
fe/fe_l2.cpp
|
||||
fe/fe_nd.cpp
|
||||
fe/fe_nurbs.cpp
|
||||
@@ -133,7 +134,7 @@ set(SRCS
|
||||
tmop/assemble/diag2.cpp
|
||||
tmop/assemble/grad2_limit.cpp
|
||||
tmop/assemble/grad2.cpp
|
||||
tmop/assemble/diag3_limit.cpp
|
||||
tmop/assemble/diag3_limit.cpp
|
||||
tmop/assemble/diag3.cpp
|
||||
tmop/assemble/grad3_limit.cpp
|
||||
tmop/assemble/grad3.cpp
|
||||
@@ -221,6 +222,7 @@ set(HDRS
|
||||
fe/fe_base.hpp
|
||||
fe/fe_fixed_order.hpp
|
||||
fe/fe_h1.hpp
|
||||
fe/fe_h1_bubble.hpp
|
||||
fe/fe_l2.hpp
|
||||
fe/fe_nd.hpp
|
||||
fe/fe_nurbs.hpp
|
||||
|
||||
@@ -729,7 +729,8 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
tr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (tr != NULL)
|
||||
{
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
mfem::DofTransformation doftrans;
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
|
||||
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
|
||||
@@ -743,6 +744,7 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -1723,6 +1725,7 @@ 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);
|
||||
@@ -1731,8 +1734,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
ftr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (ftr != NULL)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs, ran_dof_trans);
|
||||
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
|
||||
@@ -1748,6 +1751,7 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2710,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:
|
||||
|
||||
@@ -82,6 +82,25 @@ public:
|
||||
/// underlying #fes
|
||||
int VectorDim() const;
|
||||
|
||||
/// Copy assignment. Only the data of the base class Vector is copied.
|
||||
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
|
||||
have the same size.
|
||||
|
||||
@note Defining this method overwrites the implicitly defined copy
|
||||
assignment operator. */
|
||||
ComplexGridFunction &operator=(const ComplexGridFunction &rhs)
|
||||
{ return operator=((const Vector &)rhs); }
|
||||
|
||||
/// Copy the data from @a v.
|
||||
/** The size of @a v must be equal to double of the size of the associated
|
||||
FiniteElementSpace #fes. */
|
||||
ComplexGridFunction &operator=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(fes && v.Size() == 2*fes->GetVSize(), "");
|
||||
Vector::operator=(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assign constant values to the ComplexGridFunction data.
|
||||
ComplexGridFunction &operator=(const std::complex<real_t> & value)
|
||||
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
|
||||
|
||||
+11
-8
@@ -90,8 +90,8 @@ void map_quadrature_data_to_fields_impl(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +169,9 @@ void map_quadrature_data_to_fields_tensor_impl_1d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
"for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,8 +307,9 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,8 +494,9 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "fe/fe_base.hpp"
|
||||
#include "fe/fe_fixed_order.hpp"
|
||||
#include "fe/fe_h1.hpp"
|
||||
#include "fe/fe_h1_bubble.hpp"
|
||||
#include "fe/fe_nd.hpp"
|
||||
#include "fe/fe_rt.hpp"
|
||||
#include "fe/fe_l2.hpp"
|
||||
|
||||
+82
-5
@@ -1044,9 +1044,50 @@ void VectorFiniteElement::SetDerivMembers()
|
||||
switch (map_type)
|
||||
{
|
||||
case H_DIV:
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
switch (dim)
|
||||
{
|
||||
case 3: // div: 3D H_DIV -> 3D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
case 2: // div: 2D H_DIV -> 2D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_DIV_R2D:
|
||||
switch (dim)
|
||||
{
|
||||
case 2: // div: 2D H_DIV_R2D -> 2D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
case 1: // div: 1D H_DIV_R2D -> 1D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_DIV_R1D:
|
||||
switch (dim)
|
||||
{
|
||||
case 1: // div: 1D H_DIV_R1D -> 1D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_CURL:
|
||||
switch (dim)
|
||||
@@ -1064,13 +1105,49 @@ void VectorFiniteElement::SetDerivMembers()
|
||||
break;
|
||||
case 1:
|
||||
deriv_type = NONE;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
deriv_range_type = UNKNOWN_RANGE_TYPE;
|
||||
deriv_map_type = UNKNOWN_MAP_TYPE;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_CURL_R2D:
|
||||
switch (dim)
|
||||
{
|
||||
case 2:
|
||||
// curl: 2D H_CURL_R2D -> H_DIV_R2D
|
||||
deriv_type = CURL;
|
||||
deriv_range_type = VECTOR;
|
||||
deriv_map_type = H_DIV_R2D;
|
||||
break;
|
||||
case 1:
|
||||
// curl: 1D H_CURL_R2D -> H_DIV_R2D
|
||||
deriv_type = CURL;
|
||||
deriv_range_type = VECTOR;
|
||||
deriv_map_type = H_DIV_R2D;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_CURL_R1D:
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
// curl: 1D H_CURL_R1D -> H_DIV_R1D
|
||||
deriv_type = CURL;
|
||||
deriv_range_type = VECTOR;
|
||||
deriv_map_type = H_DIV_R1D;
|
||||
break;
|
||||
case 0:
|
||||
deriv_type = NONE;
|
||||
deriv_range_type = UNKNOWN_RANGE_TYPE;
|
||||
deriv_map_type = UNKNOWN_MAP_TYPE;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid MapType = " << map_type);
|
||||
}
|
||||
|
||||
+31
-3
@@ -295,10 +295,20 @@ public:
|
||||
$ u(x) = (1/w) \hat u(\hat x) $ */
|
||||
H_DIV, /**< For vector fields; preserves surface integrals of the
|
||||
normal component $ u(x) = (J/w) \hat u(\hat x) $ */
|
||||
H_CURL /**< For vector fields; preserves line integrals of the
|
||||
H_CURL, /**< For vector fields; preserves line integrals of the
|
||||
tangential component
|
||||
$ u(x) = J^{-t} \hat u(\hat x) $ (square J),
|
||||
$ u(x) = J(J^t J)^{-1} \hat u(\hat x) $ (general J) */
|
||||
H_DIV_R2D, /**< For 3-component vector fields in 2D; equivalent to a
|
||||
direct sum of an H_DIV basis and an INTEGRAL basis */
|
||||
H_CURL_R2D,/**< For 3-component vector fields in 2D; equivalent to a
|
||||
direct sum of an H_CURL basis and a VALUE basis */
|
||||
H_DIV_R1D, /**< For 3-component vector fields in 1D; equivalent to a
|
||||
direct sum of a VALUE basis and a pair of INTEGRAL
|
||||
bases */
|
||||
H_CURL_R1D /**< For 3-component vector fields in 1D; equivalent to a
|
||||
direct sum of an INTEGRAL basis and a pair of VALUE
|
||||
bases */
|
||||
};
|
||||
|
||||
/** @brief Enumeration for DerivType: defines which derivative method
|
||||
@@ -330,12 +340,28 @@ public:
|
||||
int GetDim() const { return dim; }
|
||||
|
||||
/** @brief Returns the vector dimension for vector-valued finite elements,
|
||||
which is also the dimension of the interpolation operation. */
|
||||
which is also the dimension of the interpolation operation and the
|
||||
width of the DenseMatrix argument in
|
||||
CalcVShape(const IntegrationPoint &ip, DenseMatrix &shape). */
|
||||
int GetRangeDim() const { return vdim; }
|
||||
|
||||
/// Returns the dimension of the curl for vector-valued finite elements.
|
||||
/** @brief Returns the vector dimension, in physical space, for
|
||||
vector-valued finite elements, which is also the width of the
|
||||
DenseMatrix argument in
|
||||
CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape). */
|
||||
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
|
||||
CalcCurlShape(const IntegrationPoint &ip, DenseMatrix &curl_shape). */
|
||||
int GetCurlDim() const { return cdim; }
|
||||
|
||||
/** Returns the dimension, in physical space, of the curl for vector-valued
|
||||
finite elements, which is also the width of the DenseMatrix argument in
|
||||
CalcPhysCurlShape(ElementTransformation &Trans, DenseMatrix &curl_shape).
|
||||
*/
|
||||
int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
|
||||
|
||||
/// Returns the Geometry::Type of the reference element.
|
||||
Geometry::Type GetGeomType() const { return geom_type; }
|
||||
|
||||
@@ -990,6 +1016,8 @@ protected:
|
||||
public:
|
||||
VectorFiniteElement(int D, Geometry::Type G, int Do, int O, int M,
|
||||
int F = FunctionSpace::Pk);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const { return space_dim; }
|
||||
};
|
||||
|
||||
/// @brief Class for computing 1D special polynomials and their associated basis
|
||||
|
||||
@@ -0,0 +1,973 @@
|
||||
// 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.
|
||||
|
||||
// H1 Finite Element classes
|
||||
|
||||
#include "fe_h1_bubble.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
H1Bubble_TriangleElement::H1Bubble_TriangleElement(int p, int q, int btype)
|
||||
: NodalFiniteElement(2, Geometry::TRIANGLE, 3*p + ((q+1)*(q+2))/2,
|
||||
max(p, 3 + q), FunctionSpace::Pk),
|
||||
base_order(p), bubble_order(q)
|
||||
{
|
||||
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
|
||||
const real_t *cp2 = poly1d.ClosedPoints(
|
||||
q + 3, VerifyNodal(VerifyClosed(btype)));
|
||||
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(n1d);
|
||||
shape_y.SetSize(n1d);
|
||||
shape_l.SetSize(n1d);
|
||||
dshape_x.SetSize(n1d);
|
||||
dshape_y.SetSize(n1d);
|
||||
dshape_l.SetSize(n1d);
|
||||
u.SetSize(npq);
|
||||
du.SetSize(npq, dim);
|
||||
#endif
|
||||
|
||||
// vertices
|
||||
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
|
||||
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
|
||||
Nodes.IntPoint(2).Set2(cp[0], cp[p]);
|
||||
|
||||
// edges
|
||||
int o = 3;
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[p-i], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
|
||||
}
|
||||
|
||||
// Interior P_{q+3} nodes
|
||||
for (int j = 1; j < q + 3; j++)
|
||||
{
|
||||
for (int i = 1; i + j < q + 3; i++)
|
||||
{
|
||||
const real_t w = cp2[i] + cp2[j] + cp2[q+3-i-j];
|
||||
Nodes.IntPoint(o++).Set2(cp2[i]/w, cp2[j]/w);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d);
|
||||
#endif
|
||||
|
||||
DenseMatrix Tt(dof, npq);
|
||||
for (int k = 0; k < dof; ++k)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(k);
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l);
|
||||
o = 0;
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i + j <= p; i++)
|
||||
{
|
||||
Tt(k, o++) = shape_x[i]*shape_y[j]*shape_l[p-i-j];
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
poly1d.CalcBasis(q, 1. - ip.x - ip.y, shape_l);
|
||||
const real_t b_T = ip.x * ip.y * (1 - ip.x - ip.y);
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i + j <= q; i++)
|
||||
{
|
||||
Tt(k, o++) = b_T*shape_x[i]*shape_y[j]*shape_l[q-i-j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute left inverse of T (given Tt = T^T).
|
||||
DenseMatrix TtT(dof, dof);
|
||||
MultAAt(Tt, TtT);
|
||||
|
||||
DenseMatrixInverse TtT_inv(TtT);
|
||||
T_pinv.SetSize(dof, dof);
|
||||
TtT_inv.Mult(Tt, T_pinv);
|
||||
}
|
||||
|
||||
void H1Bubble_TriangleElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d), u(npq);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l);
|
||||
|
||||
int o = 0;
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i + j <= p; i++)
|
||||
{
|
||||
u(o++) = shape_x[i]*shape_y[j]*shape_l[p-i-j];
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
poly1d.CalcBasis(q, 1. - ip.x - ip.y, shape_l);
|
||||
const real_t b_T = ip.x * ip.y * (1 - ip.x - ip.y);
|
||||
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i + j <= q; i++)
|
||||
{
|
||||
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_l[q-i-j];
|
||||
}
|
||||
}
|
||||
|
||||
T_pinv.Mult(u, shape);
|
||||
}
|
||||
|
||||
void H1Bubble_TriangleElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d);
|
||||
Vector dshape_x(n1d), dshape_y(n1d), dshape_l(n1d);
|
||||
DenseMatrix du(npq, dim);
|
||||
#endif
|
||||
|
||||
const real_t lambda = 1.0 - ip.x - ip.y;
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
|
||||
poly1d.CalcBasis(p, lambda, shape_l, dshape_l);
|
||||
|
||||
int o = 0;
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i + j <= p; i++)
|
||||
{
|
||||
int k = p - i - j;
|
||||
du(o,0) = (dshape_x[i]*shape_l[k] - shape_x[i]*dshape_l[k])*shape_y[j];
|
||||
du(o,1) = (dshape_y[j]* shape_l[k] - shape_y[j]*dshape_l[k])*shape_x[i];
|
||||
o++;
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
|
||||
poly1d.CalcBasis(q, lambda, shape_l, dshape_l);
|
||||
const real_t b_T = ip.x * ip.y * lambda;
|
||||
const real_t dxb_T = ip.y * (lambda - ip.x);
|
||||
const real_t dyb_T = ip.x * (lambda - ip.y);
|
||||
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i + j <= q; i++)
|
||||
{
|
||||
int k = q - i - j;
|
||||
du(o,0) = shape_y[j]*(dxb_T*shape_x[i]*shape_l[k]
|
||||
+ b_T*dshape_x[i]*shape_l[k]
|
||||
- b_T*shape_x[i]*dshape_l[k]);
|
||||
du(o,1) = shape_x[i]*(dyb_T*shape_y[j]*shape_l[k]
|
||||
+ b_T*dshape_y[j]*shape_l[k]
|
||||
- b_T*shape_y[j]*dshape_l[k]);
|
||||
o++;
|
||||
}
|
||||
}
|
||||
|
||||
Mult(T_pinv, du, dshape);
|
||||
}
|
||||
|
||||
H1Bubble_QuadrilateralElement::H1Bubble_QuadrilateralElement(
|
||||
int p, int q, int btype)
|
||||
: NodalFiniteElement(2, Geometry::SQUARE, 4*p + (q+1)*(q+1),
|
||||
max(p, 2 + q), FunctionSpace::Qk),
|
||||
base_order(p), bubble_order(q)
|
||||
{
|
||||
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
|
||||
const real_t *cp2 = poly1d.ClosedPoints(
|
||||
q + 2, VerifyNodal(VerifyClosed(btype)));
|
||||
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(n1d);
|
||||
shape_y.SetSize(n1d);
|
||||
dshape_x.SetSize(n1d);
|
||||
dshape_y.SetSize(n1d);
|
||||
|
||||
u.SetSize(npq);
|
||||
du.SetSize(npq, dim);
|
||||
#endif
|
||||
|
||||
// vertices
|
||||
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
|
||||
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
|
||||
Nodes.IntPoint(2).Set2(cp[p], cp[p]);
|
||||
Nodes.IntPoint(3).Set2(cp[0], cp[p]);
|
||||
|
||||
// edges
|
||||
int o = 4;
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[p], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[p-i], cp[p]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
|
||||
}
|
||||
|
||||
// interior P_{q+2} nodes
|
||||
for (int j = 1; j < q+2; j++)
|
||||
{
|
||||
for (int i = 1; i < q+2; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set2(cp2[i], cp2[j]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(n1d), shape_y(n1d);
|
||||
#endif
|
||||
|
||||
DenseMatrix Tt(dof, npq);
|
||||
for (int k = 0; k < dof; ++k)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(k);
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
|
||||
o = 0;
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
Tt(k, o++) = shape_x[i]*shape_y[j];
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i <= q; i++)
|
||||
{
|
||||
Tt(k, o++) = b_T*shape_x[i]*shape_y[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute left inverse of T (given Tt = T^T).
|
||||
DenseMatrix TtT(dof, dof);
|
||||
MultAAt(Tt, TtT);
|
||||
|
||||
DenseMatrixInverse TtT_inv(TtT);
|
||||
T_pinv.SetSize(dof, dof);
|
||||
TtT_inv.Mult(Tt, T_pinv);
|
||||
}
|
||||
|
||||
void H1Bubble_QuadrilateralElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
|
||||
Vector shape_x(n1d), shape_y(n1d), u(npq);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
|
||||
int o = 0;
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
u(o++) = shape_x[i]*shape_y[j];
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
|
||||
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i <= q; i++)
|
||||
{
|
||||
u(o++) = b_T*shape_x[i]*shape_y[j];
|
||||
}
|
||||
}
|
||||
|
||||
T_pinv.Mult(u, shape);
|
||||
}
|
||||
|
||||
void H1Bubble_QuadrilateralElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
|
||||
Vector shape_x(n1d), shape_y(n1d), dshape_x(n1d), dshape_y(n1d);
|
||||
DenseMatrix du(npq, dim);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
|
||||
|
||||
int o = 0;
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
du(o,0) = dshape_x[i]*shape_y[j];
|
||||
du(o,1) = shape_x[i]*dshape_y[j];
|
||||
o += 1;
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
|
||||
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
|
||||
const real_t dxb_T = (1.0 - 2*ip.x)*ip.y*(1.0 - ip.y);
|
||||
const real_t dyb_T = ip.x*(1.0 - ip.x)*(1.0 - 2*ip.y);
|
||||
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i <= q; i++)
|
||||
{
|
||||
du(o,0) = (dxb_T*shape_x[i] + b_T*dshape_x[i])*shape_y[j];
|
||||
du(o,1) = (dyb_T*shape_y[j] + b_T*dshape_y[j])*shape_x[i];
|
||||
o += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Mult(T_pinv, du, dshape);
|
||||
}
|
||||
|
||||
H1Bubble_TetrahedronElement::H1Bubble_TetrahedronElement(
|
||||
int p, int q, int btype)
|
||||
: NodalFiniteElement(3, Geometry::TETRAHEDRON,
|
||||
2*(p*p + 1) + ((q+1)*(q+2)*(q+3))/6,
|
||||
max(p, 4 + q), FunctionSpace::Pk),
|
||||
base_order(p), bubble_order(q)
|
||||
{
|
||||
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
|
||||
const real_t *cp2 = poly1d.ClosedPoints(
|
||||
q + 4, VerifyNodal(VerifyClosed(btype)));
|
||||
|
||||
const int n1d = max(p+1, q+1);
|
||||
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(n1d);
|
||||
shape_y.SetSize(n1d);
|
||||
shape_z.SetSize(n1d);
|
||||
shape_l.SetSize(n1d);
|
||||
dshape_x.SetSize(n1d);
|
||||
dshape_y.SetSize(n1d);
|
||||
dshape_z.SetSize(n1d);
|
||||
dshape_l.SetSize(n1d);
|
||||
u.SetSize(npq);
|
||||
du.SetSize(npq, dim);
|
||||
#else
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d);
|
||||
#endif
|
||||
|
||||
// vertices
|
||||
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
|
||||
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
|
||||
Nodes.IntPoint(2).Set3(cp[0], cp[p], cp[0]);
|
||||
Nodes.IntPoint(3).Set3(cp[0], cp[0], cp[p]);
|
||||
|
||||
// edges (see Tetrahedron::edges in mesh/tetrahedron.cpp)
|
||||
int o = 4;
|
||||
for (int i = 1; i < p; i++) // (0,1)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,2)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,3)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,2)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,3)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (2,3)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
|
||||
}
|
||||
|
||||
// faces (see Mesh::GenerateFaces in mesh/mesh.cpp)
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i + j < p; i++) // (1,2,3)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, cp[i]/w, cp[j]/w);
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i + j < p; i++) // (0,3,2)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i + j < p; i++) // (0,1,3)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i + j < p; i++) // (0,2,1)
|
||||
{
|
||||
real_t w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[j]/w, cp[i]/w, cp[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Interior P_{q+4} nodes
|
||||
for (int k = 1; k < q + 4; k++)
|
||||
{
|
||||
for (int j = 1; j + k < q + 4; j++)
|
||||
{
|
||||
for (int i = 1; i + j + k < q + 4; i++)
|
||||
{
|
||||
real_t w = cp2[i] + cp2[j] + cp2[k] + cp2[q+4-i-j-k];
|
||||
Nodes.IntPoint(o++).Set3(cp2[i]/w, cp2[j]/w, cp2[k]/w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix Tt(dof, npq);
|
||||
for (int m = 0; m < dof; ++m)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
poly1d.CalcBasis(p, ip.z, shape_z);
|
||||
poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l);
|
||||
|
||||
o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
for (int i = 0; i + j + k <= p; i++)
|
||||
{
|
||||
Tt(m, o++) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
poly1d.CalcBasis(q, ip.z, shape_z);
|
||||
poly1d.CalcBasis(q, 1. - ip.x - ip.y - ip.z, shape_l);
|
||||
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
|
||||
|
||||
for (int k = 0; k <= q; k++)
|
||||
{
|
||||
for (int j = 0; j + k <= q; j++)
|
||||
{
|
||||
for (int i = 0; i + j + k <= q; i++)
|
||||
{
|
||||
Tt(m, o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k]*shape_l[q-i-j-k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute left inverse of T (given Tt = T^T).
|
||||
DenseMatrix TtT(dof, dof);
|
||||
MultAAt(Tt, TtT);
|
||||
|
||||
DenseMatrixInverse TtT_inv(TtT);
|
||||
T_pinv.SetSize(dof, dof);
|
||||
TtT_inv.Mult(Tt, T_pinv);
|
||||
}
|
||||
|
||||
void H1Bubble_TetrahedronElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d), u(npq);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
poly1d.CalcBasis(p, ip.z, shape_z);
|
||||
poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l);
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
for (int i = 0; i + j + k <= p; i++)
|
||||
{
|
||||
u[o++] = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
poly1d.CalcBasis(q, ip.z, shape_z);
|
||||
poly1d.CalcBasis(q, 1. - ip.x - ip.y - ip.z, shape_l);
|
||||
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
|
||||
|
||||
for (int k = 0; k <= q; k++)
|
||||
{
|
||||
for (int j = 0; j + k <= q; j++)
|
||||
{
|
||||
for (int i = 0; i + j + k <= q; i++)
|
||||
{
|
||||
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k]*shape_l[q-i-j-k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T_pinv.Mult(u, shape);
|
||||
}
|
||||
|
||||
void H1Bubble_TetrahedronElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p+1, q+1);
|
||||
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
|
||||
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d);
|
||||
Vector dshape_x(n1d), dshape_y(n1d), dshape_z(n1d), dshape_l(n1d);
|
||||
DenseMatrix du(npq, dim);
|
||||
#endif
|
||||
|
||||
const real_t lambda = 1.0 - ip.x - ip.y - ip.z;
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
|
||||
poly1d.CalcBasis(p, ip.z, shape_z, dshape_z);
|
||||
poly1d.CalcBasis(p, lambda, shape_l, dshape_l);
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
for (int i = 0; i + j + k <= p; i++)
|
||||
{
|
||||
int l = p - i - j - k;
|
||||
du(o,0) = (dshape_x[i]*shape_l[l] - shape_x[i]*dshape_l[l])
|
||||
*shape_y[j]*shape_z[k];
|
||||
du(o,1) = (dshape_y[j]*shape_l[l] - shape_y[j]*dshape_l[l])
|
||||
*shape_x[i]*shape_z[k];
|
||||
du(o,2) = (dshape_z[k]*shape_l[l] - shape_z[k]*dshape_l[l])
|
||||
*shape_x[i]*shape_y[j];
|
||||
o++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
|
||||
poly1d.CalcBasis(q, ip.z, shape_z, dshape_z);
|
||||
poly1d.CalcBasis(q, lambda, shape_l, dshape_l);
|
||||
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
|
||||
const real_t dxb_T = ip.y * ip.z * (lambda - ip.x);
|
||||
const real_t dyb_T = ip.x * ip.z * (lambda - ip.y);
|
||||
const real_t dzb_T = ip.x * ip.y * (lambda - ip.z);
|
||||
|
||||
for (int k = 0; k <= q; k++)
|
||||
{
|
||||
for (int j = 0; j + k <= q; j++)
|
||||
{
|
||||
for (int i = 0; i + j + k <= q; i++)
|
||||
{
|
||||
int l = q - i - j - k;
|
||||
du(o,0) = shape_y[j]*shape_z[k]*(dxb_T*shape_x[i]*shape_l[l]
|
||||
+ b_T*dshape_x[i]*shape_l[l]
|
||||
- b_T*shape_x[i]*dshape_l[l]);
|
||||
du(o,1) = shape_x[i]*shape_z[k]*(dyb_T*shape_y[j]*shape_l[l]
|
||||
+ b_T*dshape_y[j]*shape_l[l]
|
||||
- b_T*shape_y[j]*dshape_l[l]);
|
||||
du(o,2) = shape_x[i]*shape_y[j]*(dzb_T*shape_z[k]*shape_l[l]
|
||||
+ b_T*dshape_z[k]*shape_l[l]
|
||||
- b_T*shape_z[k]*dshape_l[l]);
|
||||
o++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mult(T_pinv, du, dshape);
|
||||
}
|
||||
|
||||
H1Bubble_HexahedronElement::H1Bubble_HexahedronElement(
|
||||
int p, int q, int btype)
|
||||
: NodalFiniteElement(3, Geometry::CUBE, (2 + 6*p*p) + (q+1)*(q+1)*(q+1),
|
||||
max(p, 2 + q), FunctionSpace::Qk),
|
||||
base_order(p), bubble_order(q)
|
||||
{
|
||||
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
|
||||
const real_t *cp2 = poly1d.ClosedPoints(
|
||||
q + 2, VerifyNodal(VerifyClosed(btype)));
|
||||
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
shape_x.SetSize(n1d);
|
||||
shape_y.SetSize(n1d);
|
||||
shape_z.SetSize(n1d);
|
||||
dshape_x.SetSize(n1d);
|
||||
dshape_y.SetSize(n1d);
|
||||
dshape_z.SetSize(n1d);
|
||||
|
||||
u.SetSize(npq);
|
||||
du.SetSize(npq, dim);
|
||||
#endif
|
||||
|
||||
// vertices
|
||||
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
|
||||
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
|
||||
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
|
||||
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
|
||||
|
||||
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
|
||||
Nodes.IntPoint(5).Set3(cp[p], cp[0], cp[p]);
|
||||
Nodes.IntPoint(6).Set3(cp[p], cp[p], cp[p]);
|
||||
Nodes.IntPoint(7).Set3(cp[0], cp[p], cp[p]);
|
||||
|
||||
int o = 8;
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]); // (0,1)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]); // (1,2)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]); // (3,2)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]); // (0,3)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[p]); // (4,5)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[p]); // (5,6)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[p]); // (7,6)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[p]); // (4,7)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]); // (0,4)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[0], cp[i]); // (1,5)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[p], cp[i]); // (2,6)
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p], cp[i]); // (3,7)
|
||||
}
|
||||
|
||||
// faces
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[p-j], cp[0]); // (3,2,1,0)
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[j]); // (0,1,5,4)
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[j]); // (1,2,6,5)
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p], cp[j]); // (2,3,7,6)
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[j]); // (3,0,4,7)
|
||||
}
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
{
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[j], cp[p]); // (4,5,6,7)
|
||||
}
|
||||
}
|
||||
|
||||
// interior P_{q+2} nodes
|
||||
for (int k = 1; k < q+2; k++)
|
||||
{
|
||||
for (int j = 1; j < q+2; j++)
|
||||
{
|
||||
for (int i = 1; i < q+2; i++)
|
||||
{
|
||||
Nodes.IntPoint(o++).Set3(cp2[i], cp2[j], cp2[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d);
|
||||
#endif
|
||||
|
||||
DenseMatrix Tt(dof, npq);
|
||||
for (int m = 0; m < dof; ++m)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(m);
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
poly1d.CalcBasis(p, ip.z, shape_z);
|
||||
|
||||
o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
Tt(m, o++) = shape_x[i]*shape_y[j]*shape_z[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
poly1d.CalcBasis(q, ip.z, shape_z);
|
||||
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
|
||||
for (int k = 0; k <= q; k++)
|
||||
{
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i <= q; i++)
|
||||
{
|
||||
Tt(m, o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute left inverse of T (given Tt = T^T).
|
||||
DenseMatrix TtT(dof, dof);
|
||||
MultAAt(Tt, TtT);
|
||||
|
||||
DenseMatrixInverse TtT_inv(TtT);
|
||||
T_pinv.SetSize(dof, dof);
|
||||
TtT_inv.Mult(Tt, T_pinv);
|
||||
}
|
||||
|
||||
void H1Bubble_HexahedronElement::CalcShape(const IntegrationPoint &ip,
|
||||
Vector &shape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), u(npq);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y);
|
||||
poly1d.CalcBasis(p, ip.z, shape_z);
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
u(o++) = shape_x[i]*shape_y[j]*shape_z[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y);
|
||||
poly1d.CalcBasis(q, ip.z, shape_z);
|
||||
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
|
||||
|
||||
for (int k = 0; k <= q; k++)
|
||||
{
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i <= q; i++)
|
||||
{
|
||||
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T_pinv.Mult(u, shape);
|
||||
}
|
||||
|
||||
void H1Bubble_HexahedronElement::CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const
|
||||
{
|
||||
const int p = base_order;
|
||||
const int q = bubble_order;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
const int n1d = max(p + 1, q + 1);
|
||||
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
|
||||
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), dshape_x(n1d),
|
||||
dshape_y(n1d), dshape_z(n1d);
|
||||
DenseMatrix du(npq, dim);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
|
||||
poly1d.CalcBasis(p, ip.z, shape_z, dshape_z);
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= p; k++)
|
||||
{
|
||||
for (int j = 0; j <= p; j++)
|
||||
{
|
||||
for (int i = 0; i <= p; i++)
|
||||
{
|
||||
du(o,0) = dshape_x[i]*shape_y[j]*shape_z[k];
|
||||
du(o,1) = shape_x[i]*dshape_y[j]*shape_z[k];
|
||||
du(o,2) = shape_x[i]*shape_y[j]*dshape_z[k];
|
||||
o += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
|
||||
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
|
||||
poly1d.CalcBasis(q, ip.z, shape_z, dshape_z);
|
||||
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
|
||||
const real_t dxb_T = (1.0 - 2*ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
|
||||
const real_t dyb_T = ip.x*(1.0 - ip.x)*(1.0 - 2*ip.y)*ip.z*(1.0 - ip.z);
|
||||
const real_t dzb_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*(1.0 - 2*ip.z);
|
||||
|
||||
for (int k = 0; k <= q; k++)
|
||||
{
|
||||
for (int j = 0; j <= q; j++)
|
||||
{
|
||||
for (int i = 0; i <= q; i++)
|
||||
{
|
||||
du(o,0) = (dxb_T*shape_x[i] + b_T*dshape_x[i])*shape_y[j]*shape_z[k];
|
||||
du(o,1) = (dyb_T*shape_y[j] + b_T*dshape_y[j])*shape_x[i]*shape_z[k];
|
||||
du(o,2) = (dzb_T*shape_z[k] + b_T*dshape_z[k])*shape_x[i]*shape_y[j];
|
||||
o += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mult(T_pinv, du, dshape);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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_FE_H1_BUBBLE
|
||||
#define MFEM_FE_H1_BUBBLE
|
||||
|
||||
#include "fe_base.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Arbitrary order H1 plus bubble elements in 2D on a triangle
|
||||
class H1Bubble_TriangleElement : public NodalFiniteElement
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, shape_l, dshape_x, dshape_y, dshape_l, u;
|
||||
mutable DenseMatrix du;
|
||||
#endif
|
||||
int base_order;
|
||||
int bubble_order;
|
||||
DenseMatrix T_pinv;
|
||||
|
||||
public:
|
||||
/// @brief Construct the triangular bubble element with degree-p polynomials,
|
||||
/// enriched with cubic bubble times degree q polynomial.
|
||||
H1Bubble_TriangleElement(int p, int q, int btype = BasisType::GaussLobatto);
|
||||
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
/// Arbitrary order H1 plus bubble elements in 2D on a quadrilateral
|
||||
class H1Bubble_QuadrilateralElement : public NodalFiniteElement
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, dshape_x, dshape_y, u;
|
||||
mutable DenseMatrix du;
|
||||
#endif
|
||||
int base_order;
|
||||
int bubble_order;
|
||||
DenseMatrix T_pinv;
|
||||
|
||||
public:
|
||||
/// @brief Construct the quadrilateral bubble element with degree-p
|
||||
/// polynomials, enriched with biquadratic bubble times degree q polynomial.
|
||||
H1Bubble_QuadrilateralElement(
|
||||
int p, int q, int btype = BasisType::GaussLobatto);
|
||||
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
/// Arbitrary order H1 plus bubble elements in 3D on a tetrahedron
|
||||
class H1Bubble_TetrahedronElement : public NodalFiniteElement
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, shape_z, shape_l;
|
||||
mutable Vector dshape_x, dshape_y, dshape_z, dshape_l, u;
|
||||
mutable DenseMatrix du;
|
||||
#endif
|
||||
int base_order;
|
||||
int bubble_order;
|
||||
DenseMatrix T_pinv;
|
||||
|
||||
public:
|
||||
/// @brief Construct the tetrahedral bubble element with degree-p
|
||||
/// polynomials, enriched with quartic bubble times degree q polynomial.
|
||||
H1Bubble_TetrahedronElement(int p, int q, int btype = BasisType::GaussLobatto);
|
||||
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
/// Arbitrary order H1 plus bubble elements in 3D on a hexahedron
|
||||
class H1Bubble_HexahedronElement : public NodalFiniteElement
|
||||
{
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable Vector shape_x, shape_y, shape_z;
|
||||
mutable Vector dshape_x, dshape_y, dshape_z, u;
|
||||
mutable DenseMatrix du;
|
||||
#endif
|
||||
int base_order;
|
||||
int bubble_order;
|
||||
DenseMatrix T_pinv;
|
||||
|
||||
public:
|
||||
/// @brief Construct the hexahedral bubble element with degree-p polynomials,
|
||||
/// enriched with triquadratic bubble times degree q polynomial.
|
||||
H1Bubble_HexahedronElement(int p, int q, int btype = BasisType::GaussLobatto);
|
||||
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+4
-4
@@ -2531,7 +2531,7 @@ void ND_FuentesPyramidElement::calcCurlBasis(const int p,
|
||||
|
||||
ND_R1D_PointElement::ND_R1D_PointElement(int p)
|
||||
: VectorFiniteElement(1, Geometry::POINT, 2, p,
|
||||
H_CURL, FunctionSpace::Pk)
|
||||
H_CURL_R1D, FunctionSpace::Pk)
|
||||
{
|
||||
// VectorFiniteElement::SetDerivMembers doesn't support 0D H_CURL elements
|
||||
// so we mimic a 1D element and then correct the dimension here.
|
||||
@@ -2562,7 +2562,7 @@ ND_R1D_SegmentElement::ND_R1D_SegmentElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 2, p,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
H_CURL_R1D, FunctionSpace::Pk),
|
||||
dof2tk(dof),
|
||||
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
|
||||
@@ -2839,7 +2839,7 @@ ND_R2D_SegmentElement::ND_R2D_SegmentElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, 2 * p + 1, p,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
H_CURL_R2D, FunctionSpace::Pk),
|
||||
dof2tk(dof),
|
||||
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
|
||||
@@ -3023,7 +3023,7 @@ void ND_R2D_SegmentElement::Project(VectorCoefficient &vc,
|
||||
ND_R2D_FiniteElement::ND_R2D_FiniteElement(int p, Geometry::Type G, int Do,
|
||||
const real_t *tk_fe)
|
||||
: VectorFiniteElement(2, G, Do, p,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
H_CURL_R2D, FunctionSpace::Pk),
|
||||
tk(tk_fe),
|
||||
dof_map(dof),
|
||||
dof2tk(dof)
|
||||
|
||||
@@ -663,6 +663,9 @@ public:
|
||||
const int cb_type = BasisType::GaussLobatto,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
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;
|
||||
|
||||
@@ -705,6 +708,9 @@ private:
|
||||
DenseMatrix &I) const;
|
||||
|
||||
public:
|
||||
int GetPhysRangeDim(int space_dim) const { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 3; }
|
||||
|
||||
using FiniteElement::CalcVShape;
|
||||
using FiniteElement::CalcPhysCurlShape;
|
||||
|
||||
|
||||
+3
-3
@@ -2006,7 +2006,7 @@ RT_R1D_SegmentElement::RT_R1D_SegmentElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 4, p + 1,
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
H_DIV_R1D, FunctionSpace::Pk),
|
||||
dof2nk(dof),
|
||||
cbasis1d(poly1d.GetBasis(p + 1, VerifyClosed(cb_type))),
|
||||
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
|
||||
@@ -2281,7 +2281,7 @@ const real_t RT_R2D_SegmentElement::nk[2] = { 0.,1.};
|
||||
RT_R2D_SegmentElement::RT_R2D_SegmentElement(const int p,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, p + 1, p + 1,
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
H_DIV_R2D, FunctionSpace::Pk),
|
||||
dof2nk(dof),
|
||||
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
|
||||
{
|
||||
@@ -2392,7 +2392,7 @@ void RT_R2D_SegmentElement::LocalInterpolation(const VectorFiniteElement &cfe,
|
||||
RT_R2D_FiniteElement::RT_R2D_FiniteElement(int p, Geometry::Type G, int Do,
|
||||
const real_t *nk_fe)
|
||||
: VectorFiniteElement(2, G, Do, p + 1,
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
H_DIV_R2D, FunctionSpace::Pk),
|
||||
nk(nk_fe),
|
||||
dof_map(dof),
|
||||
dof2nk(dof)
|
||||
|
||||
@@ -510,6 +510,9 @@ public:
|
||||
RT_R2D_SegmentElement(const int p,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
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;
|
||||
|
||||
@@ -547,6 +550,9 @@ private:
|
||||
DenseMatrix &I) const;
|
||||
|
||||
public:
|
||||
int GetPhysRangeDim(int space_dim) const { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 0; }
|
||||
|
||||
using FiniteElement::CalcVShape;
|
||||
|
||||
void CalcVShape(ElementTransformation &Trans,
|
||||
|
||||
+177
@@ -243,11 +243,21 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
|
||||
{
|
||||
fec = new H1Ser_FECollection(atoi(name + 10), atoi(name + 6));
|
||||
}
|
||||
else if (!strncmp(name, "H1Bubble_", 9))
|
||||
{
|
||||
fec = new H1Bubble_FECollection(atoi(name + 13), atoi(name + 16),
|
||||
atoi(name + 9));
|
||||
}
|
||||
else if (!strncmp(name, "H1@", 3))
|
||||
{
|
||||
fec = new H1_FECollection(atoi(name + 9), atoi(name + 5),
|
||||
BasisType::GetType(name[3]));
|
||||
}
|
||||
else if (!strncmp(name, "H1Bubble@", 9))
|
||||
{
|
||||
fec = new H1Bubble_FECollection(atoi(name + 15), atoi(name + 18),
|
||||
atoi(name + 11), BasisType::GetType(name[9]));
|
||||
}
|
||||
else if (!strncmp(name, "L2_T", 4))
|
||||
fec = new L2_FECollection(atoi(name + 10), atoi(name + 6),
|
||||
atoi(name + 4));
|
||||
@@ -2122,6 +2132,173 @@ H1_FECollection::~H1_FECollection()
|
||||
}
|
||||
}
|
||||
|
||||
static int GetBubbleSpaceOrder(int p, int q, int dim)
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 0: return 0;
|
||||
case 1: return std::max(p, q + 2);
|
||||
case 2: return std::max(p, q + 3);
|
||||
case 3: return std::max(p, q + 4);
|
||||
}
|
||||
MFEM_ABORT("Unsupported dimension.");
|
||||
}
|
||||
|
||||
H1Bubble_FECollection::H1Bubble_FECollection(const int p, const int q,
|
||||
const int dim, const int btype)
|
||||
: FiniteElementCollection(GetBubbleSpaceOrder(p, q, dim)),
|
||||
dim(dim),
|
||||
b_type(BasisType::Check(btype)),
|
||||
h1_order(p),
|
||||
bubble_order(q)
|
||||
{
|
||||
MFEM_VERIFY(p >= 1, "H1Bubble_FECollection requires order >= 1.");
|
||||
MFEM_VERIFY(dim >= 0 && dim <= 3, "Unsupported dimension.");
|
||||
|
||||
switch (btype)
|
||||
{
|
||||
case BasisType::GaussLobatto:
|
||||
{
|
||||
snprintf(fec_name, 32, "H1Bubble_%dD_P%d_P%d", dim, p, q);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
const int pt_type = BasisType::GetQuadrature1D(btype);
|
||||
MFEM_VERIFY(Quadrature1D::CheckClosed(pt_type) != Quadrature1D::Invalid,
|
||||
"unsupported BasisType: " << BasisType::Name(btype));
|
||||
snprintf(fec_name, 32, "H1Bubble@%c_%dD_P%d_P%d",
|
||||
(int)BasisType::GetChar(btype), dim, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
dofs[Geometry::POINT] = 1;
|
||||
elements[Geometry::POINT] = make_unique<PointFiniteElement>();
|
||||
|
||||
if (dim >= 1)
|
||||
{
|
||||
dofs[Geometry::SEGMENT] = p - 1;
|
||||
elements[Geometry::SEGMENT] = make_unique<H1_SegmentElement>(p, btype);
|
||||
}
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
dofs[Geometry::TRIANGLE] = ((q+1)*(q+2))/2;
|
||||
dofs[Geometry::SQUARE] = (q+1)*(q+1);
|
||||
|
||||
elements[Geometry::TRIANGLE] =
|
||||
make_unique<H1Bubble_TriangleElement>(p, q, btype);
|
||||
elements[Geometry::SQUARE] =
|
||||
make_unique<H1Bubble_QuadrilateralElement>(p, q, btype);
|
||||
}
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
dofs[Geometry::TRIANGLE] = ((p-1)*(p-2))/2;
|
||||
dofs[Geometry::SQUARE] = (p-1)*(p-1);
|
||||
dofs[Geometry::TETRAHEDRON] = ((q+1)*(q+2)*(q+3))/6;
|
||||
dofs[Geometry::CUBE] = (q+1)*(q+1)*(q+1);
|
||||
|
||||
elements[Geometry::TRIANGLE] = make_unique<H1_TriangleElement>(p, btype);
|
||||
elements[Geometry::SQUARE] = make_unique<H1_QuadrilateralElement>(p, btype);
|
||||
|
||||
elements[Geometry::TETRAHEDRON] =
|
||||
make_unique<H1Bubble_TetrahedronElement>(p, q, btype);
|
||||
elements[Geometry::CUBE] =
|
||||
make_unique<H1Bubble_HexahedronElement>(p, q, btype);
|
||||
}
|
||||
|
||||
// DOF orderings. Need only for lower-dimensional entities.
|
||||
// Segment DOF orderings in 2D.
|
||||
if (dim >= 2)
|
||||
{
|
||||
seg_dof_ord[0].resize(p - 1);
|
||||
seg_dof_ord[1].resize(p - 1);
|
||||
for (int i = 0; i < p - 1; i++)
|
||||
{
|
||||
seg_dof_ord[0][i] = i;
|
||||
seg_dof_ord[1][i] = p - 2 - i;
|
||||
}
|
||||
}
|
||||
|
||||
// Face (triangle or quadrilateral) DOF orderings in 3D.
|
||||
if (dim == 3)
|
||||
{
|
||||
const int n_tri_dof = dofs[Geometry::TRIANGLE];
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
tri_dof_ord[i].resize(n_tri_dof);
|
||||
}
|
||||
// see Mesh::GetTriOrientation in mesh/mesh.cpp
|
||||
const int pm1 = p - 1;
|
||||
const int pm2 = p - 2;
|
||||
for (int j = 0; j < pm2; j++)
|
||||
{
|
||||
for (int i = 0; i + j < pm2; i++)
|
||||
{
|
||||
int o = n_tri_dof - ((pm1 - j)*(pm2 - j))/2 + i;
|
||||
int k = (p - 3) - j - i;
|
||||
tri_dof_ord[0][o] = o; // (0,1,2)
|
||||
tri_dof_ord[1][o] = n_tri_dof - ((pm1-j)*(pm2-j))/2 + k; // (1,0,2)
|
||||
tri_dof_ord[2][o] = n_tri_dof - ((pm1-i)*(pm2-i))/2 + k; // (2,0,1)
|
||||
tri_dof_ord[3][o] = n_tri_dof - ((pm1-k)*(pm2-k))/2 + i; // (2,1,0)
|
||||
tri_dof_ord[4][o] = n_tri_dof - ((pm1-k)*(pm2-k))/2 + j; // (1,2,0)
|
||||
tri_dof_ord[5][o] = n_tri_dof - ((pm1-i)*(pm2-i))/2 + j; // (0,2,1)
|
||||
}
|
||||
}
|
||||
|
||||
const int n_quad_dof = dofs[Geometry::SQUARE];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
quad_dof_ord[i].resize(n_quad_dof);
|
||||
}
|
||||
for (int j = 0; j < pm1; j++)
|
||||
{
|
||||
for (int i = 0; i < pm1; i++)
|
||||
{
|
||||
int o = i + j*pm1;
|
||||
quad_dof_ord[0][o] = i + j*pm1; // (0,1,2,3)
|
||||
quad_dof_ord[1][o] = j + i*pm1; // (0,3,2,1)
|
||||
quad_dof_ord[2][o] = j + (pm2 - i)*pm1; // (1,2,3,0)
|
||||
quad_dof_ord[3][o] = (pm2 - i) + j*pm1; // (1,0,3,2)
|
||||
quad_dof_ord[4][o] = (pm2 - i) + (pm2 - j)*pm1; // (2,3,0,1)
|
||||
quad_dof_ord[5][o] = (pm2 - j) + (pm2 - i)*pm1; // (2,1,0,3)
|
||||
quad_dof_ord[6][o] = (pm2 - j) + i*pm1; // (3,0,1,2)
|
||||
quad_dof_ord[7][o] = i + (pm2 - j)*pm1; // (3,2,1,0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *
|
||||
H1Bubble_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
return elements[GeomType].get();
|
||||
}
|
||||
|
||||
const int *H1Bubble_FECollection::DofOrderForOrientation(
|
||||
Geometry::Type GeomType, int Or) const
|
||||
{
|
||||
if (GeomType == Geometry::SEGMENT)
|
||||
{
|
||||
return (Or > 0) ? seg_dof_ord[0].data() : seg_dof_ord[1].data();
|
||||
}
|
||||
else if (GeomType == Geometry::TRIANGLE)
|
||||
{
|
||||
return tri_dof_ord[Or%6].data();
|
||||
}
|
||||
else if (GeomType == Geometry::SQUARE)
|
||||
{
|
||||
return quad_dof_ord[Or%8].data();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FiniteElementCollection *H1Bubble_FECollection::GetTraceCollection() const
|
||||
{
|
||||
return (dim < 0) ? NULL : new H1_Trace_FECollection(h1_order, dim, b_type);
|
||||
}
|
||||
|
||||
|
||||
H1_Trace_FECollection::H1_Trace_FECollection(const int p, const int dim,
|
||||
const int btype)
|
||||
|
||||
@@ -111,6 +111,8 @@ public:
|
||||
| :------: | :---: | :---: | :-------: | :-----: | :---: |
|
||||
| H1_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
|
||||
| H1@[BTYPE]_[DIM]_[ORDER] | H1 | * | * | VALUE | H1 nodal elements |
|
||||
| H1Bubble_[DIM]_[ORDER]_[BUBBLE_ORDER] | H1 | * | 1 | VALUE | H1 nodal elements enriched with bubble functions |
|
||||
| H1Bubble@[BTYPE]_[DIM]_[ORDER]_[BUBBLE_ORDER] | H1 | * | 1 | VALUE | H1 nodal elements enriched with bubble functions |
|
||||
| H1Pos_[DIM]_[ORDER] | H1 | * | 2 | VALUE | H1 nodal elements |
|
||||
| H1Pos_Trace_[DIM]_[ORDER] | H^{1/2} | * | 2 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
@@ -317,6 +319,59 @@ public:
|
||||
virtual ~H1_FECollection();
|
||||
};
|
||||
|
||||
/// @brief Arbitrary order $H^1$-conforming (continuous) finite elements
|
||||
/// enriched with bubble functions.
|
||||
///
|
||||
/// The bubble space consists of the standard $P_p$ or $Q_p$ space, enriched
|
||||
/// with bubble functions, which are degree-$q$ polynomials times $b$, where $b$
|
||||
/// is the lowest-order bubble function.
|
||||
///
|
||||
/// The traces are the same as the standard $H^1$ traces.
|
||||
class H1Bubble_FECollection : public FiniteElementCollection
|
||||
{
|
||||
protected:
|
||||
int dim;
|
||||
int b_type;
|
||||
int h1_order;
|
||||
int bubble_order;
|
||||
|
||||
char fec_name[32];
|
||||
std::array<int, Geometry::NumGeom> dofs{}; // zero initialize
|
||||
std::array<std::unique_ptr<FiniteElement>, Geometry::NumGeom> elements;
|
||||
|
||||
std::array<std::vector<int>, 2> seg_dof_ord;
|
||||
std::array<std::vector<int>, 6> tri_dof_ord;
|
||||
std::array<std::vector<int>, 8> quad_dof_ord;
|
||||
std::array<std::vector<int>, 24> tet_dof_ord;
|
||||
|
||||
public:
|
||||
/// Construct the $H^1$ bubble collection consisting of degree-$p$
|
||||
/// polynomials enriched with the bubble function times degree-$q$
|
||||
/// polynomials.
|
||||
explicit H1Bubble_FECollection(const int p, const int q, const int dim = 3,
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
|
||||
const FiniteElement *
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const override;
|
||||
|
||||
int DofForGeometry(Geometry::Type GeomType) const override
|
||||
{ return dofs[GeomType]; }
|
||||
|
||||
const int *DofOrderForOrientation(Geometry::Type GeomType,
|
||||
int Or) const override;
|
||||
|
||||
const char *Name() const override { return fec_name; }
|
||||
|
||||
int GetContType() const override { return CONTINUOUS; }
|
||||
|
||||
int GetBasisType() const { return b_type; }
|
||||
|
||||
FiniteElementCollection *GetTraceCollection() const override;
|
||||
|
||||
FiniteElementCollection *Clone(int p) const override
|
||||
{ return new H1Bubble_FECollection(p, bubble_order, dim, b_type); }
|
||||
};
|
||||
|
||||
/** @brief Arbitrary order H1-conforming (continuous) finite elements with
|
||||
positive basis functions. */
|
||||
class H1Pos_FECollection : public H1_FECollection
|
||||
|
||||
+6
-3
@@ -3877,9 +3877,12 @@ const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_DEBUG
|
||||
// consistency check: fec->GetOrder() and FE->GetOrder() should return
|
||||
// the same value (for standard, constant-order spaces)
|
||||
if (!IsVariableOrder() && FE->GetDim() > 0)
|
||||
// Consistency check: fec->GetOrder() and FE->GetOrder() should return
|
||||
// the same value (for standard, constant-order spaces). Skip this check
|
||||
// even for constant-order bubble spaces, since the bubble functions on
|
||||
// different geometries have different orders.
|
||||
if (!IsVariableOrder() && FE->GetDim() > 0 &&
|
||||
dynamic_cast<const H1Bubble_FECollection*>(fec) == nullptr)
|
||||
{
|
||||
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
|
||||
"internal error: " <<
|
||||
|
||||
+42
-43
@@ -3137,52 +3137,29 @@ void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[],
|
||||
}
|
||||
|
||||
void GridFunction::ProjectBdrCoefficientNormal(
|
||||
VectorCoefficient &vcoeff, const Array<int> &bdr_attr)
|
||||
Coefficient *coeff, VectorCoefficient *vcoeff, const Array<int> &bdr_attr)
|
||||
{
|
||||
#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 (fes->GetNBE() > 0)
|
||||
{
|
||||
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);
|
||||
// TODO: Replace this by GetTypicalBdrElement() once implemented
|
||||
const FiniteElement *be = fes->GetBE(0);
|
||||
MFEM_VERIFY(be->GetRangeType() == FiniteElement::SCALAR &&
|
||||
be->GetMapType() == FiniteElement::INTEGRAL, "Not an RT FE space!");
|
||||
}
|
||||
#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;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
Vector vc, nor, lvec;
|
||||
DofTransformation doftrans;
|
||||
if (vcoeff)
|
||||
{
|
||||
const int dim = vcoeff->GetVDim();
|
||||
vc.SetSize(dim);
|
||||
nor.SetSize(dim);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
@@ -3198,15 +3175,22 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
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);
|
||||
}
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs, doftrans);
|
||||
doftrans.TransformPrimal(lvec);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GridFunction::ProjectBdrCoefficientTangent(
|
||||
@@ -5007,6 +4991,14 @@ 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)
|
||||
@@ -5057,10 +5049,17 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
return NULL;
|
||||
}
|
||||
FiniteElementSpace *solfes2d;
|
||||
// assuming sol is scalar
|
||||
solfes2d = new FiniteElementSpace(mesh2d, solfec2d);
|
||||
const int vdim = sol->FESpace()->GetVDim();
|
||||
solfes2d = new FiniteElementSpace(mesh2d, solfec2d, vdim);
|
||||
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);
|
||||
|
||||
+62
-9
@@ -532,6 +532,9 @@ 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,
|
||||
@@ -656,15 +659,26 @@ public:
|
||||
virtual void ProjectBdrCoefficient(Coefficient *coeff[],
|
||||
const Array<int> &attr);
|
||||
|
||||
/** 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. */
|
||||
/** @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. */
|
||||
void ProjectBdrCoefficientNormal(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr);
|
||||
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); }
|
||||
|
||||
/** @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 VectorFE GridFunction. */
|
||||
on the boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr
|
||||
are projected. Assumes ND-type vector finite element GridFunction. */
|
||||
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr);
|
||||
|
||||
@@ -1914,7 +1928,7 @@ real_t ComputeElementLpDistance(real_t p, int i,
|
||||
GridFunction& gf1, GridFunction& gf2);
|
||||
|
||||
|
||||
/// Class used for extruding scalar GridFunctions
|
||||
/// Class used for extruding a scalar coefficient
|
||||
class ExtrudeCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
@@ -1922,13 +1936,52 @@ 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) { }
|
||||
: n(n_), mesh_in(m), sol_in(s)
|
||||
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
|
||||
|
||||
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~ExtrudeCoefficient() { }
|
||||
};
|
||||
|
||||
/// Extrude a scalar 1D GridFunction, after extruding the mesh with Extrude1D.
|
||||
/// 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;
|
||||
|
||||
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
|
||||
*/
|
||||
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
GridFunction *sol, const int ny);
|
||||
|
||||
|
||||
@@ -197,15 +197,21 @@ 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 % NDOF_C;
|
||||
const int idx_ii = idx_i - ic * NDOF_C; // idx_i % NDOF_C
|
||||
|
||||
const int nx_i = (ic == 0) ? D1D : D1D-1;
|
||||
const int ny_i = (ic == 1) ? D1D : D1D-1;
|
||||
|
||||
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 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 real_t (&Bi1)[MQ1][MD1] = (ic == 0) ? r_Bc : r_Bo;
|
||||
const real_t (&Bi2)[MQ1][MD1] = (ic == 1) ? r_Bc : r_Bo;
|
||||
@@ -214,14 +220,18 @@ 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 % NDOF_C;
|
||||
const int idx_jj = idx_j - jc * NDOF_C; // idx_j % NDOF_C
|
||||
|
||||
const int nx_j = (jc == 0) ? D1D : D1D-1;
|
||||
const int ny_j = (jc == 1) ? D1D : D1D-1;
|
||||
|
||||
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 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 real_t (&Bj1)[MQ1][MD1] = (jc == 0) ? r_Bc : r_Bo;
|
||||
const real_t (&Bj2)[MQ1][MD1] = (jc == 1) ? r_Bc : r_Bo;
|
||||
|
||||
+811
-327
File diff suppressed because it is too large
Load Diff
+30
-27
@@ -125,18 +125,6 @@ private:
|
||||
void AddTriPoints3b(const int off, const real_t b, const real_t weight)
|
||||
{ AddTriPoints3(off, (1. - b)/2., b, weight); }
|
||||
|
||||
void AddTriPoints3R(const int off, const real_t a, const real_t b,
|
||||
const real_t c, const real_t weight)
|
||||
{
|
||||
IntPoint(off + 0).Set2w(a, b, weight);
|
||||
IntPoint(off + 1).Set2w(c, a, weight);
|
||||
IntPoint(off + 2).Set2w(b, c, weight);
|
||||
}
|
||||
|
||||
void AddTriPoints3R(const int off, const real_t a, const real_t b,
|
||||
const real_t weight)
|
||||
{ AddTriPoints3R(off, a, b, 1. - a - b, weight); }
|
||||
|
||||
void AddTriPoints6(const int off, const real_t a, const real_t b,
|
||||
const real_t c, const real_t weight)
|
||||
{
|
||||
@@ -183,14 +171,6 @@ private:
|
||||
AddTetPoints3(off + 1, a, 1. - 3.*a, weight);
|
||||
}
|
||||
|
||||
// given b, add the permutations of (a,a,a,b), where 3*a + b = 1
|
||||
void AddTetPoints4b(const int off, const real_t b, const real_t weight)
|
||||
{
|
||||
const real_t a = (1. - b)/3.;
|
||||
IntPoint(off).Set(a, a, a, weight);
|
||||
AddTetPoints3(off + 1, a, b, weight);
|
||||
}
|
||||
|
||||
// add the permutations of (a,a,b,b), 2*(a + b) = 1
|
||||
void AddTetPoints6(const int off, const real_t a, const real_t weight)
|
||||
{
|
||||
@@ -209,14 +189,37 @@ private:
|
||||
AddTetPoints6(off + 6, a, bc, cb, weight);
|
||||
}
|
||||
|
||||
// given (b,c), add the permutations of (a,a,b,c), 2*a + b + c = 1
|
||||
void AddTetPoints12bc(const int off, const real_t b, const real_t c,
|
||||
const real_t weight)
|
||||
// add all 24 permutations of (a,b,c,d) where a+b+c+d = 1, all distinct
|
||||
void AddTetPoints24(const int off, const real_t a, const real_t b,
|
||||
const real_t c, const real_t weight)
|
||||
{
|
||||
const real_t a = (1. - b - c)/2.;
|
||||
AddTetPoints3(off, a, b, weight);
|
||||
AddTetPoints3(off + 3, a, c, weight);
|
||||
AddTetPoints6(off + 6, a, b, c, weight);
|
||||
const real_t d = 1. - a - b - c;
|
||||
// all 24 permutations of 4 distinct barycentric coordinates
|
||||
// permuting which coordinate goes to x, y, z (4th is 1-x-y-z)
|
||||
IntPoint(off + 0).Set(a, b, c, weight);
|
||||
IntPoint(off + 1).Set(a, b, d, weight);
|
||||
IntPoint(off + 2).Set(a, c, b, weight);
|
||||
IntPoint(off + 3).Set(a, c, d, weight);
|
||||
IntPoint(off + 4).Set(a, d, b, weight);
|
||||
IntPoint(off + 5).Set(a, d, c, weight);
|
||||
IntPoint(off + 6).Set(b, a, c, weight);
|
||||
IntPoint(off + 7).Set(b, a, d, weight);
|
||||
IntPoint(off + 8).Set(b, c, a, weight);
|
||||
IntPoint(off + 9).Set(b, c, d, weight);
|
||||
IntPoint(off + 10).Set(b, d, a, weight);
|
||||
IntPoint(off + 11).Set(b, d, c, weight);
|
||||
IntPoint(off + 12).Set(c, a, b, weight);
|
||||
IntPoint(off + 13).Set(c, a, d, weight);
|
||||
IntPoint(off + 14).Set(c, b, a, weight);
|
||||
IntPoint(off + 15).Set(c, b, d, weight);
|
||||
IntPoint(off + 16).Set(c, d, a, weight);
|
||||
IntPoint(off + 17).Set(c, d, b, weight);
|
||||
IntPoint(off + 18).Set(d, a, b, weight);
|
||||
IntPoint(off + 19).Set(d, a, c, weight);
|
||||
IntPoint(off + 20).Set(d, b, a, weight);
|
||||
IntPoint(off + 21).Set(d, b, c, weight);
|
||||
IntPoint(off + 22).Set(d, c, a, weight);
|
||||
IntPoint(off + 23).Set(d, c, b, weight);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+3
-1
@@ -297,7 +297,8 @@ void LinearForm::Assemble()
|
||||
tr = mesh->GetBdrFaceTransformations(i);
|
||||
if (tr != NULL)
|
||||
{
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
mfem::DofTransformation doftrans;
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (boundary_face_integs_marker[k] &&
|
||||
@@ -307,6 +308,7 @@ void LinearForm::Assemble()
|
||||
boundary_face_integs[k]->
|
||||
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
|
||||
*tr, elemvect);
|
||||
doftrans.TransformDual(elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-5
@@ -321,12 +321,17 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int threshold = ndofs;
|
||||
const int nsdofs = pfes.GetFaceNbrVSize();
|
||||
const int nsdofs = pfes.GetFaceNbrVSize() / vd;
|
||||
auto d_indices1 = scatter_indices1.Read();
|
||||
auto d_indices2 = scatter_indices2.Read();
|
||||
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:nsdofs, t?nsdofs:vd);
|
||||
const int ne_shared = nsdofs / elem_dofs;
|
||||
const int nedof = elem_dofs;
|
||||
// Note: the shape of face_nbr_data, as determined by
|
||||
// ParFiniteElementSpace::ExchangeFaceNbrData, is (elem_dofs, vdim,
|
||||
// ne_shared), independent of the ordering (byNODES or byVDIM) of the finite
|
||||
// element space.
|
||||
auto d_x_shared = Reshape(face_nbr_data.Read(), elem_dofs, vd, ne_shared);
|
||||
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
|
||||
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
@@ -346,8 +351,9 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
}
|
||||
else if (idx2>=threshold) // shared boundary
|
||||
{
|
||||
d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
|
||||
t?(idx2-threshold):c);
|
||||
const int e_shared = (idx2 - threshold) / nedof;
|
||||
const int i_shared = (idx2 - threshold) % nedof;
|
||||
d_y(dof, c, 1, face) = d_x_shared(i_shared,c,e_shared);
|
||||
}
|
||||
else // true boundary
|
||||
{
|
||||
|
||||
+3
-6
@@ -1398,20 +1398,17 @@ void L2FaceRestriction::PermuteAndSetSharedFaceDofsScatterIndices2(
|
||||
const int dim = fes.GetMesh()->Dimension();
|
||||
const int dof1d = fes.GetTypicalFE()->GetOrder()+1;
|
||||
fes.GetTypicalFE()->GetFaceMap(face_id2, face_map);
|
||||
Array<int> face_nbr_dofs;
|
||||
const ParFiniteElementSpace &pfes =
|
||||
static_cast<const ParFiniteElementSpace&>(this->fes);
|
||||
pfes.GetFaceNbrElementVDofs(elem_index, face_nbr_dofs);
|
||||
|
||||
for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
|
||||
{
|
||||
const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
|
||||
orientation, dof1d, face_dof_elem1);
|
||||
const int volume_dof_elem2 = face_map[face_dof_elem2];
|
||||
const int global_dof_elem2 = face_nbr_dofs[volume_dof_elem2];
|
||||
// Encode the volume DOF index and element index
|
||||
const int global_dof_elem2 = elem_index*elem_dofs + volume_dof_elem2;
|
||||
const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
|
||||
// Trick to differentiate dof location inter/shared
|
||||
scatter_indices2[restriction_dof_elem2] = ndofs+global_dof_elem2;
|
||||
scatter_indices2[restriction_dof_elem2] = ndofs + global_dof_elem2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -278,9 +278,18 @@ void ArraysByName<T>::Load(std::istream &in)
|
||||
q1 = ArrayLine.find(' ');
|
||||
ArrayName = ArrayLine.substr(0,q1-1);
|
||||
}
|
||||
|
||||
// Ignore the remainder of the line which may contain explanatory comments
|
||||
data[ArrayName].Load(in, 0);
|
||||
if (q1+2 < ArrayLine.size())
|
||||
{
|
||||
// Read the remainder of the line which contains the array data
|
||||
std::istringstream ArrayDataStream(ArrayLine.substr(q1+2,
|
||||
ArrayLine.size()));
|
||||
data[ArrayName].Load(ArrayDataStream, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the array data starting on the next line
|
||||
data[ArrayName].Load(in, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -726,16 +726,16 @@ std::string Device::GetUUID(const int device_id)
|
||||
MFEM_GPU_CHECK(cudaGetDeviceProperties(&prop, device_id));
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
res << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<unsigned>(prop.uuid.bytes[i]);
|
||||
const unsigned b = static_cast<unsigned char>(prop.uuid.bytes[i]);
|
||||
res << std::setfill('0') << std::setw(2) << std::hex << b;
|
||||
}
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
hipUUID uuid;
|
||||
MFEM_GPU_CHECK(hipDeviceGetUuid(&uuid, device_id));
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
res << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<unsigned>(uuid.bytes[i]);
|
||||
const unsigned b = static_cast<unsigned char>(uuid.bytes[i]);
|
||||
res << std::setfill('0') << std::setw(2) << std::hex << b;
|
||||
}
|
||||
#endif
|
||||
return res.str();
|
||||
|
||||
@@ -317,6 +317,9 @@ void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
|
||||
|
||||
Vector * HypreParVector::GlobalVector() const
|
||||
{
|
||||
MFEM_VERIFY(size > 0,
|
||||
"GlobalVector method can only be called on vectors wherein each "
|
||||
"process owns one or more entries");
|
||||
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
|
||||
Vector *v = new Vector(hv->data, internal::to_int(hv->size));
|
||||
v->MakeDataOwner();
|
||||
|
||||
+44
-78
@@ -38,6 +38,13 @@
|
||||
#if PETSC_VERSION_LT(3,19,0)
|
||||
#define PETSC_SUCCESS 0
|
||||
#endif
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
#define PetscContainerSetCtxDestroy(A,B) PetscContainerSetUserDestroy(A,B)
|
||||
typedef PetscErrorCode (PetscCtxDestroyFn)(void**);
|
||||
#endif
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
typedef PetscErrorCode KSPMonitorFn(KSP,PetscInt,PetscReal,void*);
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@@ -77,13 +84,17 @@ static PetscErrorCode __mfem_mat_shell_apply_transpose(Mat,Vec,Vec);
|
||||
static PetscErrorCode __mfem_mat_shell_destroy(Mat);
|
||||
static PetscErrorCode __mfem_mat_shell_copy(Mat,Mat,MatStructure);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_array_container_destroy(void*);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void *);
|
||||
#else
|
||||
static PetscErrorCode __mfem_array_container_destroy(void**);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void**);
|
||||
typedef void *PetscCtxRt;
|
||||
#elif PETSC_VERSION_LT(3,25,0)
|
||||
typedef void **PetscCtxRt;
|
||||
#endif
|
||||
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void**);
|
||||
#else
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt);
|
||||
#endif
|
||||
|
||||
// auxiliary functions
|
||||
static PetscErrorCode Convert_Array_IS(MPI_Comm,bool,const mfem::Array<int>*,
|
||||
@@ -1317,11 +1328,7 @@ BlockDiagonalConstructor(MPI_Comm comm,
|
||||
|
||||
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CCHKERRQ(comm,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
CCHKERRQ(comm,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)A,names[i],(PetscObject)c);
|
||||
CCHKERRQ(comm,ierr);
|
||||
@@ -1648,11 +1655,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
PetscContainer c;
|
||||
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
|
||||
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_matarray_container_destroy);
|
||||
#endif
|
||||
PCHKERRQ(c,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(*A),"_MatIS_PtAP_l2l",(PetscObject)c);
|
||||
PCHKERRQ((*A),ierr);
|
||||
@@ -1748,11 +1751,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
|
||||
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); PCHKERRQ(B,ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); PCHKERRQ(B,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
PCHKERRQ(B,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(B),names[i],(PetscObject)c);
|
||||
PCHKERRQ(B,ierr);
|
||||
@@ -2198,11 +2197,7 @@ PetscParMatrix * RAP(PetscParMatrix *Rt, PetscParMatrix *A, PetscParMatrix *P)
|
||||
ierr = PetscContainerCreate(PetscObjectComm((PetscObject)B),&c);
|
||||
PCHKERRQ(B,ierr);
|
||||
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_matarray_container_destroy);
|
||||
#endif
|
||||
PCHKERRQ(c,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)B,"_MatIS_PtAP_l2l",(PetscObject)c);
|
||||
PCHKERRQ(B,ierr);
|
||||
@@ -2485,7 +2480,6 @@ void PetscSolver::SetMaxIter(int max_iter)
|
||||
|
||||
void PetscSolver::SetPrintLevel(int plev)
|
||||
{
|
||||
typedef PetscErrorCode (*myPetscFunc)(void**);
|
||||
PetscViewerAndFormat *vf = NULL;
|
||||
PetscViewer viewer = PETSC_VIEWER_STDOUT_(PetscObjectComm(obj));
|
||||
|
||||
@@ -2498,7 +2492,6 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
{
|
||||
// there are many other options, see the function KSPSetFromOptions() in
|
||||
// src/ksp/ksp/interface/itcl.c
|
||||
typedef PetscErrorCode (*myMonitor)(KSP,PetscInt,PetscReal,void*);
|
||||
KSP ksp = (KSP)obj;
|
||||
if (plev >= 0)
|
||||
{
|
||||
@@ -2507,29 +2500,29 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
if (plev == 1)
|
||||
{
|
||||
#if PETSC_VERSION_LT(3,15,0)
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorDefault,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorDefault,vf,
|
||||
#else
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorResidual,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorResidual,vf,
|
||||
#endif
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
}
|
||||
else if (plev > 1)
|
||||
{
|
||||
ierr = KSPSetComputeSingularValues(ksp,PETSC_TRUE); PCHKERRQ(ksp,ierr);
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorSingularValue,vf,
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorSingularValue,vf,
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
if (plev > 2)
|
||||
{
|
||||
ierr = PetscViewerAndFormatCreate(viewer,PETSC_VIEWER_DEFAULT,&vf);
|
||||
PCHKERRQ(viewer,ierr);
|
||||
#if PETSC_VERSION_LT(3,15,0)
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidualNorm,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidualNorm,vf,
|
||||
#else
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidual,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidual,vf,
|
||||
#endif
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
}
|
||||
}
|
||||
@@ -2545,7 +2538,7 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
if (plev > 0)
|
||||
{
|
||||
ierr = SNESMonitorSet(snes,(myMonitor)SNESMonitorDefault,vf,
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(snes,ierr);
|
||||
}
|
||||
}
|
||||
@@ -5329,21 +5322,27 @@ static PetscErrorCode __mfem_pc_shell_destroy(PC pc)
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt ptr)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(void *ptr)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(ptr); CHKERRQ(ierr);
|
||||
#else
|
||||
ierr = PetscFree(*(void**)ptr); CHKERRQ(ierr);
|
||||
#endif
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt ptr)
|
||||
{
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
mfem::Array<Mat> *a = (mfem::Array<Mat>*)ptr;
|
||||
PetscErrorCode ierr;
|
||||
#else
|
||||
mfem::Array<Mat> *a = *(mfem::Array<Mat>**)ptr;
|
||||
#endif
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
for (int i=0; i<a->Size(); i++)
|
||||
@@ -5356,41 +5355,16 @@ static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
|
||||
#else
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(void **ptr)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt ctx)
|
||||
#endif
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(*ptr); CHKERRQ(ierr);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void **ptr)
|
||||
{
|
||||
mfem::Array<Mat> *a = (mfem::Array<Mat>*)*ptr;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
for (int i=0; i<a->Size(); i++)
|
||||
{
|
||||
Mat M = (*a)[i];
|
||||
MPI_Comm comm = PetscObjectComm((PetscObject)M);
|
||||
ierr = MatDestroy(&M); CCHKERRQ(comm,ierr);
|
||||
}
|
||||
delete a;
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(*ctx); CHKERRQ(ierr);
|
||||
ierr = PetscFree(*(void**)ctx); CHKERRQ(ierr);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -5635,11 +5609,7 @@ static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
|
||||
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
@@ -5733,11 +5703,7 @@ static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
|
||||
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
|
||||
@@ -126,11 +126,11 @@ EXAMPLE_TEST_DIRS := examples
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
|
||||
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
|
||||
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
|
||||
fluids/navier fluids/schrodinger-flow
|
||||
fluids/navier fluids/schrodinger-flow plasma
|
||||
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
|
||||
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
|
||||
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
|
||||
toys shifted dpg diag-smoothers fluids/navier)
|
||||
toys shifted dpg diag-smoothers fluids/navier plasma)
|
||||
|
||||
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
|
||||
|
||||
|
||||
@@ -3206,10 +3206,22 @@ public:
|
||||
|
||||
|
||||
/// Extrude a 1D mesh
|
||||
/**
|
||||
* @param mesh 1D mesh
|
||||
* @param ny number of transverse elements of the extruded mesh
|
||||
* @param sy physical size in the direction of extrusion
|
||||
* @param closed if false, only the original boundaries are extruded,
|
||||
* otherwise boundaries are generated all around the domain
|
||||
*/
|
||||
Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy,
|
||||
const bool closed = false);
|
||||
|
||||
/// Extrude a 2D mesh
|
||||
/**
|
||||
* @param mesh 2D mesh
|
||||
* @param nz number of transverse elements of the extruded mesh
|
||||
* @param sz physical size in the direction of extrusion
|
||||
*/
|
||||
Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz);
|
||||
|
||||
/** @brief Constructs the smallest possible [0,1]^dim serial mesh that can be
|
||||
|
||||
@@ -1516,12 +1516,15 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
|
||||
void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
{
|
||||
string buff;
|
||||
real_t version;
|
||||
string version;
|
||||
int binary, dsize;
|
||||
input >> version >> binary >> dsize;
|
||||
if (version < 2.2)
|
||||
if (version != "2.2")
|
||||
{
|
||||
MFEM_ABORT("Gmsh file version < 2.2");
|
||||
MFEM_ABORT("Gmsh file version must be 2.2, found version "
|
||||
<< version << ".\n"
|
||||
"To convert your mesh to the required format, use:\n"
|
||||
" gmsh -format msh22 -save -o output.msh input.msh");
|
||||
}
|
||||
if (dsize != sizeof(double))
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ add_subdirectory(multidomain)
|
||||
add_subdirectory(nurbs)
|
||||
add_subdirectory(parelag)
|
||||
add_subdirectory(performance)
|
||||
add_subdirectory(plasma)
|
||||
add_subdirectory(shifted)
|
||||
add_subdirectory(solvers)
|
||||
add_subdirectory(spde)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# 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.
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
list(APPEND PLASMA_COMMON_SOURCES)
|
||||
|
||||
list(APPEND PLASMA_COMMON_HEADERS
|
||||
plasma.hpp)
|
||||
|
||||
convert_filenames_to_full_paths(PLASMA_COMMON_SOURCES)
|
||||
convert_filenames_to_full_paths(PLASMA_COMMON_HEADERS)
|
||||
|
||||
set(PLASMA_COMMON_FILES
|
||||
EXTRA_SOURCES ${PLASMA_COMMON_SOURCES}
|
||||
EXTRA_HEADERS ${PLASMA_COMMON_HEADERS})
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,85 @@
|
||||
# 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.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/plasma/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_MINIAPPS =
|
||||
PAR_MINIAPPS =
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all lib-common clean clean-build clean-exec
|
||||
.PRECIOUS: %.o
|
||||
|
||||
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
|
||||
|
||||
# If MFEM_SHARED is set, add the ../common rpath
|
||||
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
|
||||
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
|
||||
$(MFEM_BUILD_DIR)/miniapps/common))
|
||||
|
||||
COMMON_O=
|
||||
|
||||
# Remove built-in rules
|
||||
%: %.cpp
|
||||
%.o: %.cpp
|
||||
|
||||
all: $(MINIAPPS)
|
||||
|
||||
# Rules for building the miniapps
|
||||
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $< -o $@ $(COMMON_O) $(COMMON_LIB) \
|
||||
$(MFEM_LIBS)
|
||||
|
||||
# Rules for compiling miniapp dependencies
|
||||
$(COMMON_O) $(addsuffix _solver.o,$(MINIAPPS)): \
|
||||
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
|
||||
|
||||
# Rule for building lib-common
|
||||
lib-common:
|
||||
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
|
||||
|
||||
MFEM_TESTS = MINIAPPS
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Specific execution options
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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_PLASMA_HPP
|
||||
#define MFEM_PLASMA_HPP
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace plasma
|
||||
{
|
||||
|
||||
// Physical Constants
|
||||
|
||||
// Permittivity of Free Space (units F/m)
|
||||
static const real_t epsilon0_ = 8.8541878176e-12;
|
||||
|
||||
// Permeability of Free Space (units H/m)
|
||||
static const real_t mu0_ = 4.0e-7 * M_PI;
|
||||
|
||||
// Speed of light in Free Space (units m/s)
|
||||
static const real_t c0_ = 1.0 / sqrt(epsilon0_ * mu0_);
|
||||
|
||||
// Impedance of Free Space (units Ohm)
|
||||
static const real_t Z0_ = sqrt(mu0_ / epsilon0_);
|
||||
|
||||
static const real_t q_ = 1.602176634e-19; // Elementary charge in coulombs
|
||||
static const real_t eV_ = 1.602176634e-19; // 1 eV in Joules
|
||||
static const real_t amu_ = 1.660539040e-27; // Atomic mass unit in kilograms
|
||||
static const real_t me_kg_ = 9.10938356e-31; // Mass of electron in kilograms
|
||||
static const real_t me_u_ = 5.4857990907e-4; // Mass of electron in a.m.u
|
||||
|
||||
/**
|
||||
Returns the cyclotron frequency in radians/second
|
||||
m is the mass in a.m.u
|
||||
q is the charge in units of elementary electric charge
|
||||
B is the magnetic field magnitude in tesla
|
||||
*/
|
||||
inline real_t cyclotronFrequency(real_t B, real_t m, real_t q)
|
||||
{
|
||||
return fabs(q * q_ * B / (m * amu_));
|
||||
}
|
||||
|
||||
typedef std::complex<real_t> complex_t;
|
||||
|
||||
} // namespace plasma
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_PLASMA_HPP
|
||||
|
||||
@@ -295,8 +295,13 @@ namespace Catch {
|
||||
// Otherwise all supported compilers support COUNTER macro,
|
||||
// but user still might want to turn it off
|
||||
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
|
||||
#if ( !(defined(__clang__) && __clang_major__ >= 22 ) )
|
||||
// don't use __COUNTER__ if compiling with clang 22+ to avoid compiler warning
|
||||
// https://github.com/llvm/llvm-project/pull/162662
|
||||
// TODO: can enable if building with C2y
|
||||
#define CATCH_INTERNAL_CONFIG_COUNTER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -117,3 +117,52 @@ TEST_CASE("Vector FE Face Restriction", "[FaceRestriction]")
|
||||
gf2 -= gf;
|
||||
REQUIRE(gf2.Normlinf() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
TEST_CASE("L2 Face Restriction", "[FaceRestriction][Parallel]")
|
||||
{
|
||||
const int dim = GENERATE(2, 3);
|
||||
constexpr int nx = 3;
|
||||
constexpr int order = 2;
|
||||
constexpr int vdim = 2;
|
||||
const Ordering::Type ordering = GENERATE(Ordering::byNODES, Ordering::byVDIM);
|
||||
|
||||
Mesh serial_mesh = MakeCartesianMesh(nx, dim);
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
|
||||
L2_FECollection fec(order, dim, BasisType::GaussLobatto);
|
||||
ParFiniteElementSpace fes(&mesh, &fec, vdim, ordering);
|
||||
|
||||
auto *R = fes.GetFaceRestriction(ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Interior);
|
||||
|
||||
Vector vals({1.0, 2.0});
|
||||
VectorConstantCoefficient coeff(vals);
|
||||
|
||||
ParGridFunction gf(&fes);
|
||||
gf.ProjectCoefficient(coeff);
|
||||
|
||||
Vector face_vec(R->Height());
|
||||
R->Mult(gf, face_vec);
|
||||
|
||||
const int nf = mesh.GetNFbyType(FaceType::Interior);
|
||||
const int face_dofs = fes.GetTypicalTraceElement()->GetDof();
|
||||
auto h_face_vec = Reshape(face_vec.HostRead(), face_dofs, vdim, 2, nf);
|
||||
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
for (int m = 0; m < 2; ++m)
|
||||
{
|
||||
for (int c = 0; c < vdim; ++c)
|
||||
{
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
{
|
||||
REQUIRE(h_face_vec(i, c, m, f) == vals[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -281,8 +281,10 @@ TEST_CASE("Nedelec Segment Finite Element",
|
||||
REQUIRE( fe.GetRangeType() == (int) FiniteElement::VECTOR );
|
||||
REQUIRE( fe.GetMapType() == (int) FiniteElement::H_CURL );
|
||||
REQUIRE( fe.GetDerivType() == (int) FiniteElement::NONE );
|
||||
REQUIRE( fe.GetDerivRangeType() == (int) FiniteElement::SCALAR );
|
||||
REQUIRE( fe.GetDerivMapType() == (int) FiniteElement::INTEGRAL);
|
||||
REQUIRE( fe.GetDerivRangeType() ==
|
||||
(int) FiniteElement::UNKNOWN_RANGE_TYPE);
|
||||
REQUIRE( fe.GetDerivMapType() ==
|
||||
(int) FiniteElement::UNKNOWN_MAP_TYPE);
|
||||
}
|
||||
}
|
||||
SECTION("Sizes for p = " + std::to_string(p))
|
||||
|
||||
@@ -105,39 +105,39 @@ TEST_CASE("Integration rule order initialization", "[IntegrationRules]")
|
||||
SECTION("Segment rule constructed by accessing square rule")
|
||||
{
|
||||
auto &quad5_ir = intrules.Get(Geometry::SQUARE, 5);
|
||||
REQUIRE(quad5_ir.GetOrder() == 5);
|
||||
REQUIRE(quad5_ir.GetOrder() >= 5);
|
||||
// The segment integration rule of order 5 is lazy constructed when we get
|
||||
// the square integration rule of order 5. Make sure its order was
|
||||
// properly set:
|
||||
auto &line5_ir = intrules.Get(Geometry::SEGMENT, 5);
|
||||
REQUIRE(line5_ir.GetOrder() == 5);
|
||||
REQUIRE(line5_ir.GetOrder() >= 5);
|
||||
}
|
||||
|
||||
SECTION("Segment rule constructed by accessing cube rule")
|
||||
{
|
||||
auto &hex7_ir = intrules.Get(Geometry::CUBE, 7);
|
||||
REQUIRE(hex7_ir.GetOrder() == 7);
|
||||
REQUIRE(hex7_ir.GetOrder() >= 7);
|
||||
// The segment integration rule of order 7 is lazy constructed when we get
|
||||
// the cube integration rule of order 7. Make sure its order was properly
|
||||
// set:
|
||||
auto &line7_ir = intrules.Get(Geometry::SEGMENT, 7);
|
||||
REQUIRE(line7_ir.GetOrder() == 7);
|
||||
REQUIRE(line7_ir.GetOrder() >= 7);
|
||||
}
|
||||
|
||||
SECTION("Segment and triangle rules constructed by accessing prism rule")
|
||||
{
|
||||
auto &prism3_ir = intrules.Get(Geometry::PRISM, 3);
|
||||
REQUIRE(prism3_ir.GetOrder() == 3);
|
||||
REQUIRE(prism3_ir.GetOrder() >= 3);
|
||||
// The segment integration rule of order 3 is lazy constructed when we get
|
||||
// the prism integration rule of order 3. Make sure its order was properly
|
||||
// set:
|
||||
auto &line3_ir = intrules.Get(Geometry::SEGMENT, 3);
|
||||
REQUIRE(line3_ir.GetOrder() == 3);
|
||||
REQUIRE(line3_ir.GetOrder() >= 3);
|
||||
// The triangle integration rule of order 3 is lazy constructed when we
|
||||
// get the prism integration rule of order 3. Make sure its order was
|
||||
// properly set:
|
||||
auto &tri3_ir = intrules.Get(Geometry::TRIANGLE, 3);
|
||||
REQUIRE(tri3_ir.GetOrder() == 3);
|
||||
REQUIRE(tri3_ir.GetOrder() >= 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,3 +271,43 @@ TEST_CASE("Simplex integration rules", "[SimplexRules]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Monomial exactness is tested by [SimplexRules] above, which now uses
|
||||
// positive-weight rules by default. The tests below verify properties
|
||||
// specific to the positive-weight rules: weight positivity, stability,
|
||||
// and interior point placement.
|
||||
|
||||
TEST_CASE("Simplex rule positivity", "[IntegrationRules]")
|
||||
{
|
||||
IntegrationRules rules;
|
||||
|
||||
SECTION("triangle rules have all positive weights for orders 0-25")
|
||||
{
|
||||
for (int order = 0; order <= 25; order++)
|
||||
{
|
||||
const IntegrationRule &ir = rules.Get(Geometry::TRIANGLE, order);
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
INFO("order=" << order << ", point=" << i);
|
||||
REQUIRE(ir.IntPoint(i).weight > 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("tet rules have all positive weights for orders 0-20")
|
||||
{
|
||||
for (int order = 0; order <= 20; order++)
|
||||
{
|
||||
const IntegrationRule &ir =
|
||||
rules.Get(Geometry::TETRAHEDRON, order);
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
INFO("order=" << order << ", point=" << i);
|
||||
REQUIRE(ir.IntPoint(i).weight > 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,15 +25,201 @@ void Func_3D_lin(const Vector &x, Vector &v)
|
||||
v[2] = -2.572 * x[0] + 1.321 * x[1] + 3.234 * x[2];
|
||||
}
|
||||
|
||||
TEST_CASE("3D ProjectBdrCoefficientNormal Vector",
|
||||
"[GridFunction]"
|
||||
"[VectorGridFunctionCoefficient]")
|
||||
{
|
||||
const int n = 1;
|
||||
const int dim = 3;
|
||||
const int order = 1;
|
||||
|
||||
const double tol = 1e-6;
|
||||
|
||||
for (int type = (int)Element::TETRAHEDRON;
|
||||
type <= (int)Element::HEXAHEDRON; type++)
|
||||
{
|
||||
Mesh mesh = Mesh::MakeCartesian3D(
|
||||
n, n, n, (Element::Type)type, 2.0, 3.0, 5.0);
|
||||
|
||||
VectorFunctionCoefficient funcCoef(dim, Func_3D_lin);
|
||||
|
||||
SECTION("3D GetVectorValue tests for element type " +
|
||||
std::to_string(type))
|
||||
{
|
||||
RT_FECollection rt_fec(order+1, dim);
|
||||
|
||||
FiniteElementSpace rt_fespace(&mesh, &rt_fec);
|
||||
|
||||
GridFunction rt_x( &rt_fespace);
|
||||
|
||||
VectorGridFunctionCoefficient rt_xCoef( &rt_x);
|
||||
|
||||
Array<int> bdr_marker(6);
|
||||
|
||||
Vector normal(dim);
|
||||
Vector f_val(dim);
|
||||
Vector rt_val(dim);
|
||||
|
||||
for (int b = 1; b<=6; b++)
|
||||
{
|
||||
bdr_marker = 0;
|
||||
bdr_marker[b-1] = 1;
|
||||
|
||||
rt_x = 0.0;
|
||||
rt_x.ProjectBdrCoefficientNormal(funcCoef, bdr_marker);
|
||||
|
||||
for (int be = 0; be < mesh.GetNBE(); be++)
|
||||
{
|
||||
Element *e = mesh.GetBdrElement(be);
|
||||
if (e->GetAttribute() != b) { continue; }
|
||||
|
||||
ElementTransformation *T = mesh.GetBdrElementTransformation(be);
|
||||
const FiniteElement *fe = rt_fespace.GetBE(be);
|
||||
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(),
|
||||
2*order + 2);
|
||||
|
||||
double rt_err = 0.0;
|
||||
|
||||
for (int j=0; j<ir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
CalcOrtho(T->Jacobian(), normal);
|
||||
|
||||
funcCoef.Eval(f_val, *T, ip);
|
||||
rt_xCoef.Eval(rt_val, *T, ip);
|
||||
|
||||
rt_val -= f_val;
|
||||
|
||||
double rt_dist = rt_val * normal;
|
||||
|
||||
rt_err += rt_dist;
|
||||
|
||||
if (verbose_tests && rt_dist > tol)
|
||||
{
|
||||
mfem::out << be << ":" << j << " rt ("
|
||||
<< f_val[0] << "," << f_val[1] << "," << f_val[2]
|
||||
<< ") vs. ("
|
||||
<< rt_val[0] << "," << rt_val[1] << ","
|
||||
<< rt_val[2] << ") " << rt_dist << std::endl;
|
||||
}
|
||||
}
|
||||
rt_err /= ir.GetNPoints();
|
||||
|
||||
REQUIRE( rt_err == MFEM_Approx(0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("3D ProjectBdrCoefficientNormal Scalar",
|
||||
"[GridFunction]"
|
||||
"[VectorGridFunctionCoefficient]")
|
||||
{
|
||||
const int n = 1;
|
||||
const int dim = 3;
|
||||
const int order = 1;
|
||||
|
||||
const double tol = 1e-6;
|
||||
|
||||
const char bdrs_axis[] = {2, 1, 0, 1, 0, 2};
|
||||
const char bdrs_sign[] = {-1, -1, +1, +1, -1, +1};
|
||||
|
||||
for (int type = (int)Element::TETRAHEDRON;
|
||||
type <= (int)Element::HEXAHEDRON; type++)
|
||||
{
|
||||
Mesh mesh = Mesh::MakeCartesian3D(
|
||||
n, n, n, (Element::Type)type, 2.0, 3.0, 5.0);
|
||||
|
||||
VectorFunctionCoefficient funcCoef(dim, Func_3D_lin);
|
||||
|
||||
SECTION("3D GetVectorValue tests for element type " +
|
||||
std::to_string(type))
|
||||
{
|
||||
RT_FECollection rt_fec(order+1, dim);
|
||||
|
||||
FiniteElementSpace rt_fespace(&mesh, &rt_fec);
|
||||
|
||||
GridFunction rt_x( &rt_fespace);
|
||||
|
||||
VectorGridFunctionCoefficient rt_xCoef( &rt_x);
|
||||
|
||||
Array<int> bdr_marker(6);
|
||||
|
||||
Vector normal(dim);
|
||||
Vector f_val(dim);
|
||||
Vector rt_val(dim);
|
||||
|
||||
for (int b = 1; b<=6; b++)
|
||||
{
|
||||
bdr_marker = 0;
|
||||
bdr_marker[b-1] = 1;
|
||||
|
||||
rt_x = 0.0;
|
||||
|
||||
normal = 0.;
|
||||
normal(bdrs_axis[b-1]) = (bdrs_sign[b-1] > 0)?(+1.):(-1.);
|
||||
VectorConstantCoefficient normCoef(normal);
|
||||
InnerProductCoefficient prodCoef(funcCoef, normCoef);
|
||||
rt_x.ProjectBdrCoefficientNormal(prodCoef, bdr_marker);
|
||||
|
||||
for (int be = 0; be < mesh.GetNBE(); be++)
|
||||
{
|
||||
Element *e = mesh.GetBdrElement(be);
|
||||
if (e->GetAttribute() != b) { continue; }
|
||||
|
||||
ElementTransformation *T = mesh.GetBdrElementTransformation(be);
|
||||
const FiniteElement *fe = rt_fespace.GetBE(be);
|
||||
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(),
|
||||
2*order + 2);
|
||||
|
||||
double rt_err = 0.0;
|
||||
|
||||
for (int j=0; j<ir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
CalcOrtho(T->Jacobian(), normal);
|
||||
|
||||
funcCoef.Eval(f_val, *T, ip);
|
||||
rt_xCoef.Eval(rt_val, *T, ip);
|
||||
|
||||
rt_val -= f_val;
|
||||
|
||||
double rt_dist = rt_val * normal;
|
||||
|
||||
rt_err += rt_dist;
|
||||
|
||||
if (verbose_tests && rt_dist > tol)
|
||||
{
|
||||
mfem::out << be << ":" << j << " rt ("
|
||||
<< f_val[0] << "," << f_val[1] << "," << f_val[2]
|
||||
<< ") vs. ("
|
||||
<< rt_val[0] << "," << rt_val[1] << ","
|
||||
<< rt_val[2] << ") " << rt_dist << std::endl;
|
||||
}
|
||||
}
|
||||
rt_err /= ir.GetNPoints();
|
||||
|
||||
REQUIRE( rt_err == MFEM_Approx(0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("3D ProjectBdrCoefficientTangent",
|
||||
"[GridFunction]"
|
||||
"[VectorGridFunctionCoefficient]")
|
||||
{
|
||||
int n = 1;
|
||||
int dim = 3;
|
||||
int order = 1;
|
||||
const int n = 1;
|
||||
const int dim = 3;
|
||||
const int order = 1;
|
||||
|
||||
double tol = 1e-6;
|
||||
const double tol = 1e-6;
|
||||
|
||||
for (int type = (int)Element::TETRAHEDRON;
|
||||
type <= (int)Element::HEXAHEDRON; type++)
|
||||
|
||||
@@ -200,3 +200,39 @@ TEST_CASE("ArraysByName Sort/Unique Methods", "[ArraysByName]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ArraysByName Print/Load Methods", "[ArraysByName]")
|
||||
{
|
||||
ArraysByName<int> abn;
|
||||
|
||||
FillArraysByName(abn);
|
||||
|
||||
// Print object to string using default format
|
||||
std::ostringstream oss1;
|
||||
abn.Print(oss1);
|
||||
|
||||
// Load new object from printed output
|
||||
ArraysByName<int> abn_load1;
|
||||
std::istringstream iss1(oss1.str());
|
||||
abn_load1.Load(iss1);
|
||||
REQUIRE(abn == abn_load1);
|
||||
|
||||
// Print object to string using one line per array
|
||||
std::ostringstream oss2;
|
||||
oss2 << abn.Size() << '\n';
|
||||
for (auto a : abn)
|
||||
{
|
||||
oss2 << '"' << a.first << "\" " << a.second.Size();
|
||||
for (auto d : a.second)
|
||||
{
|
||||
oss2 << ' ' << d;
|
||||
}
|
||||
oss2 << '\n';
|
||||
}
|
||||
|
||||
// Load new object from printed output
|
||||
ArraysByName<int> abn_load2;
|
||||
std::istringstream iss2(oss2.str());
|
||||
abn_load2.Load(iss2);
|
||||
REQUIRE(abn == abn_load2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user