Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c01a9cb623 | ||
|
|
007183c70a | ||
|
|
0e63fc90e3 | ||
|
|
06cb917637 | ||
|
|
db97637c09 | ||
|
|
cbaf930388 | ||
|
|
b53dd0fea1 | ||
|
|
acf167ed86 | ||
|
|
8a8ac07910 | ||
|
|
c2d5eed541 | ||
|
|
f8e71cf89c |
@@ -25,7 +25,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
|
||||
@@ -36,7 +36,7 @@ runs:
|
||||
steps:
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v4
|
||||
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
|
||||
id: debug
|
||||
with:
|
||||
|
||||
@@ -23,7 +23,7 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache/restore@v5 # Cache for LLVM libcxx
|
||||
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
@@ -32,14 +32,14 @@ runs:
|
||||
- uses: ./.github/actions/sanitize/mpi
|
||||
if: ${{inputs.par == 'true'}}
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Hypre
|
||||
- uses: actions/cache/restore@v4 # Cache for Hypre
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.HYPRE_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
|
||||
|
||||
- uses: actions/cache/restore@v5 # Cache for Metis
|
||||
- uses: actions/cache/restore@v4 # Cache for Metis
|
||||
if: ${{inputs.par == 'true'}}
|
||||
with:
|
||||
path: ${{env.METIS_DIR}}
|
||||
@@ -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@v5 # Cache for LSAN suppression file
|
||||
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
fail-on-cache-miss: true
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
- uses: actions/checkout@v6 # Checkout the repository
|
||||
- uses: actions/checkout@v4 # Checkout the repository
|
||||
with:
|
||||
path: mfem
|
||||
# ref: ${{env.BRANCH}}
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
remove-docker-images: 'true'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# It's easier to reference named variables than indexes of the matrix
|
||||
- name: Set Environment
|
||||
|
||||
@@ -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@v6
|
||||
uses: actions/checkout@v4
|
||||
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@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
@@ -255,7 +255,7 @@ jobs:
|
||||
- name: cache metis
|
||||
id: metis-cache
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
@@ -270,7 +270,7 @@ jobs:
|
||||
- name: cache vcpkg (Windows)
|
||||
id: vcpkg-cache
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vcpkg_cache
|
||||
key: ${{ runner.os }}-${{ matrix.mpi }}-vcpkg-v1
|
||||
|
||||
@@ -40,11 +40,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -70,4 +70,4 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout MFEM
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- name: Cache Hypre Install
|
||||
id: hypre-cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
- name: Cache Metis Install
|
||||
id: metis-cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: copyright check
|
||||
id: copyright
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get astyle
|
||||
run: |
|
||||
@@ -110,7 +110,7 @@ jobs:
|
||||
github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get doxygen and graphviz
|
||||
run: |
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 2.19.0
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.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@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
with:
|
||||
NO_FLAGS: true
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LLVM_DIR}}
|
||||
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: lsan.supp
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.LSAN_DIR}}
|
||||
key: build-lsan-suppression-file
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: 4.0.3
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/config
|
||||
- name: Cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{env.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@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/mfem
|
||||
with:
|
||||
par: ${{inputs.par}}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
env:
|
||||
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
env:
|
||||
exclude: ${{inputs.par && '-E "_ser"' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
|
||||
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
env:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
@@ -165,7 +165,7 @@ jobs:
|
||||
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
|
||||
np: ${{inputs.par && '_np=2' || ''}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/sanitize/restore
|
||||
id: restore
|
||||
with:
|
||||
|
||||
@@ -8,22 +8,6 @@
|
||||
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)
|
||||
===========================
|
||||
|
||||
@@ -33,9 +17,6 @@ Discretization improvements
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behaviour has not changed.
|
||||
|
||||
- Added methods to estimate function extremum using piecewise linear bounds +
|
||||
recursive subdivision.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
|
||||
+1
-5
@@ -652,8 +652,6 @@ 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)
|
||||
@@ -1017,7 +1015,5 @@ 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,6 +701,7 @@ 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()
|
||||
@@ -798,12 +799,7 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
# message(STATUS "Lib = ${Lib}")
|
||||
# Filter-out generator expressions
|
||||
if (NOT ("${Lib}" MATCHES "^\\$"))
|
||||
if(NOT ("${Lib}" STREQUAL "dl"))
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
else()
|
||||
# for some reason libdl doesn't include the "-l"
|
||||
list(APPEND LinkOpts "-ldl")
|
||||
endif()
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
endif()
|
||||
else()
|
||||
mfem_get_target_options(${Lib} COpts LOpts)
|
||||
@@ -892,18 +888,9 @@ function(mfem_export_mk_files)
|
||||
set(${var} NO)
|
||||
endif()
|
||||
endforeach()
|
||||
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()
|
||||
# TODO: Add support for MFEM_USE_CUDA=YES
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${MFEM_CXX})
|
||||
set(MFEM_CPPFLAGS "")
|
||||
get_target_property(cxx_std mfem CXX_STANDARD)
|
||||
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
|
||||
@@ -913,50 +900,6 @@ 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}")
|
||||
@@ -987,9 +930,6 @@ 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
|
||||
@@ -998,15 +938,8 @@ 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}")
|
||||
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()
|
||||
# TODO: CUDA/HIP support:
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
set(MFEM_MPIEXEC ${MPIEXEC})
|
||||
if (NOT MFEM_MPIEXEC)
|
||||
set(MFEM_MPIEXEC "mpirun")
|
||||
@@ -1054,21 +987,16 @@ 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} CompileOpts2 LinkOpts2)
|
||||
# remove generator expressions
|
||||
string(GENEX_STRIP "${CompileOpts2}" CompileOpts)
|
||||
string(GENEX_STRIP "${LinkOpts2}" LinkOpts)
|
||||
mfem_get_target_options(${lib} CompileOpts LinkOpts)
|
||||
# Removing duplicates may lead to issues:
|
||||
# list(REMOVE_DUPLICATES CompileOpts)
|
||||
# list(REMOVE_DUPLICATES LinkOpts)
|
||||
# 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()
|
||||
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(FATAL_ERROR "***** interface lib found ... exiting *****")
|
||||
# handle static and shared libs
|
||||
elseif ("${suffix}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
@@ -1076,7 +1004,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()
|
||||
@@ -1085,7 +1013,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" @ONLY)
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk")
|
||||
# Copy 'test.mk' from the source-tree to the build-tree
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/test.mk"
|
||||
@@ -1103,7 +1031,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" @ONLY)
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk")
|
||||
|
||||
# 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 464 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 82
|
||||
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
|
||||
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3877 -o 2 -sys
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
|
||||
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
|
||||
|
||||
+9
-27
@@ -302,21 +302,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
|
||||
<< "window_title 'Exact: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
|
||||
<< "window_title 'Exact: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
|
||||
// 11. Set up the parallel sesquilinear form a(.,.) on the finite element
|
||||
@@ -540,21 +534,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u.real()
|
||||
<< "window_title 'Solution: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u.imag()
|
||||
<< "window_title 'Solution: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
if (visualization && exact_sol)
|
||||
{
|
||||
@@ -563,21 +551,15 @@ int main(int argc, char *argv[])
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock_r(vishost, visport);
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_r.precision(8);
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
|
||||
<< "window_title 'Error: Real Part'" << flush;
|
||||
// Make sure all ranks have sent their real solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
socketstream sol_sock_i(vishost, visport);
|
||||
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock_i.precision(8);
|
||||
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
|
||||
<< "window_title 'Error: Imaginary Part'" << flush;
|
||||
// Make sure all ranks have sent their imaginary solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
|
||||
+8
-2
@@ -97,7 +97,13 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.ParseCheck();
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
const char *mesh_file = "../data/disc-nurbs.mesh";
|
||||
@@ -122,7 +128,7 @@ int main(int argc, char *argv[])
|
||||
*nodes /= scale;
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
H1Bubble_FECollection H1fec(order, order - 1, dim);
|
||||
H1_FECollection H1fec(order+1, dim);
|
||||
FiniteElementSpace H1fes(&mesh, &H1fec);
|
||||
|
||||
L2_FECollection L2fec(order-1, dim);
|
||||
|
||||
+14
-2
@@ -103,7 +103,19 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.ParseCheck();
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
const char *mesh_file = "../data/disc-nurbs.mesh";
|
||||
@@ -131,7 +143,7 @@ int main(int argc, char *argv[])
|
||||
mesh.Clear();
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
H1Bubble_FECollection H1fec(order, order - 1, dim);
|
||||
H1_FECollection H1fec(order+1, dim);
|
||||
ParFiniteElementSpace H1fes(&pmesh, &H1fec);
|
||||
|
||||
L2_FECollection L2fec(order-1, dim);
|
||||
|
||||
+1
-3
@@ -73,7 +73,6 @@ 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
|
||||
@@ -134,7 +133,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
|
||||
@@ -222,7 +221,6 @@ 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,8 +729,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
tr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (tr != NULL)
|
||||
{
|
||||
mfem::DofTransformation doftrans;
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
fe1 = fes -> GetFE (tr -> Elem1No);
|
||||
// The fe2 object is really a dummy and not used on the boundaries,
|
||||
// but we can't dereference a NULL pointer, and we don't want to
|
||||
@@ -744,7 +743,6 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
|
||||
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
|
||||
elemmat);
|
||||
doftrans.TransformDual(elemmat);
|
||||
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -1725,7 +1723,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < trial_fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
@@ -1734,8 +1731,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
ftr = mesh -> GetBdrFaceTransformations (i);
|
||||
if (ftr != NULL)
|
||||
{
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs, ran_dof_trans);
|
||||
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
@@ -1751,7 +1748,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
+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:
|
||||
|
||||
+28
-67
@@ -39,8 +39,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
b_type = b_type_i;
|
||||
cp_type = cp_type_i;
|
||||
tol = tol_i;
|
||||
lbound.SetSize(ncp, nb);
|
||||
ubound.SetSize(ncp, nb);
|
||||
lbound.SetSize(nb, ncp);
|
||||
ubound.SetSize(nb, ncp);
|
||||
nodes.SetSize(nb);
|
||||
weights.SetSize(nb);
|
||||
control_points.SetSize(ncp);
|
||||
@@ -125,25 +125,21 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
}
|
||||
else if (j == ncp-1)
|
||||
{
|
||||
lbound(j,i) = bv(i);
|
||||
ubound(j,i) = bv(i);
|
||||
lbound(i, j) = bv(i);
|
||||
ubound(i, j) = bv(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
vals(0) = bv(i);
|
||||
vals(1) = bmv(i) + dm*bdmv(i);
|
||||
vals(2) = bpv(i) + dp*bdpv(i);
|
||||
lbound(j,i) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(j,i) = vals.Max()+tol; // tolerance for good measure
|
||||
if (b_type == 2)
|
||||
{
|
||||
lbound(j,i) = std::max(lbound(j,i),0_r);
|
||||
}
|
||||
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
|
||||
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +273,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
intmax.SetSize(ncp);
|
||||
intmin = 0.0;
|
||||
intmax = 0.0;
|
||||
Vector coeffm;
|
||||
Vector coeffm(nb);
|
||||
coeffm = 0.0;
|
||||
|
||||
real_t a0 = 0.0;
|
||||
real_t a1 = 0.0;
|
||||
@@ -305,8 +302,6 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
// compute L2 projection for linear bases: a0 + a1*x
|
||||
if (proj)
|
||||
{
|
||||
coeffm.SetSize(nb);
|
||||
coeffm = 0.0;
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes_int(i)-1;
|
||||
@@ -347,8 +342,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t c = coeffm(i);
|
||||
for (int j = 0; j < ncp; j++)
|
||||
{
|
||||
intmin(j) += min(lbound(j,i)*c, ubound(j,i)*c);
|
||||
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
|
||||
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
|
||||
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,10 +474,10 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth row
|
||||
{
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
intmin(k*ncp+i) += vals.Min();
|
||||
intmax(k*ncp+i) += vals.Max();
|
||||
}
|
||||
@@ -558,17 +553,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
x = 2.0*nodes(i)-1; // x-coordinate
|
||||
minNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
|
||||
minBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
maxBounds(i) -= a0V(j) + a1V(j)*x;
|
||||
}
|
||||
// Compute Bernstein coefficients
|
||||
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
|
||||
lu.Solve(nb, 1, minNodalVals.GetData());
|
||||
lu.Solve(nb, 1, maxNodalVals.GetData());
|
||||
lu.Solve(nb, 1, minBounds.GetData());
|
||||
lu.Solve(nb, 1, maxBounds.GetData());
|
||||
for (int i = 0; i < nb; i++)
|
||||
{
|
||||
intminT(i*ncp2+j) = minNodalVals(i);
|
||||
intmaxT(i*ncp2+j) = maxNodalVals(i);
|
||||
intminT(i*ncp2+j) = minBounds(i);
|
||||
intmaxT(i*ncp2+j) = maxBounds(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,10 +617,10 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
real_t w1 = intmaxT(id2++);
|
||||
for (int k = 0; k < ncp; k++) // kth slice
|
||||
{
|
||||
vals(0) = w0*lbound(k,j);
|
||||
vals(1) = w0*ubound(k,j);
|
||||
vals(2) = w1*lbound(k,j);
|
||||
vals(3) = w1*ubound(k,j);
|
||||
vals(0) = w0*lbound(j,k);
|
||||
vals(1) = w0*ubound(j,k);
|
||||
vals(2) = w1*lbound(j,k);
|
||||
vals(3) = w1*ubound(j,k);
|
||||
intmin(k*ncp2+i) += vals.Min();
|
||||
intmax(k*ncp2+i) += vals.Max();
|
||||
}
|
||||
@@ -658,8 +653,7 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
Vector &nodesBern) const
|
||||
{
|
||||
const int nbern = nodesBern.Size();
|
||||
L2_SegmentElement el(nbern-1, 2);
|
||||
// we use L2 to leverage lexicographic order
|
||||
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
|
||||
Array<int> ordering = el.GetLexicographicOrdering();
|
||||
basisMat.SetSize(nbern, nbern);
|
||||
Vector shape(nbern);
|
||||
@@ -672,39 +666,6 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetBoundingMatrix(int dim, bool is_lower) const
|
||||
{
|
||||
if (dim > 1)
|
||||
{
|
||||
const int ncpd = static_cast<int>(std::pow(ncp, dim));
|
||||
const int nbd = static_cast<int>(std::pow(nb, dim));
|
||||
DenseMatrix boundND(ncpd, nbd);
|
||||
Vector phimin, phimax, col;
|
||||
Vector coeffs(nbd);
|
||||
coeffs = 0.0;
|
||||
for (int j = 0; j < nbd; j++)
|
||||
{
|
||||
coeffs(j) = 1.0;
|
||||
boundND.GetColumnReference(j, col);
|
||||
GetNDBounds(dim, coeffs, phimin, phimax);
|
||||
col = is_lower ? phimin : phimax;
|
||||
coeffs(j) = 0.0;
|
||||
}
|
||||
return boundND;
|
||||
}
|
||||
return is_lower ? lbound : ubound;
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetLowerBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, true);
|
||||
}
|
||||
|
||||
DenseMatrix PLBound::GetUpperBoundMatrix(int dim) const
|
||||
{
|
||||
return GetBoundingMatrix(dim, false);
|
||||
}
|
||||
|
||||
constexpr int PLBound::min_ncp_gl_x[2][11];
|
||||
constexpr int PLBound::min_ncp_gll_x[2][11];
|
||||
constexpr int PLBound::min_ncp_pos_x[2][11];
|
||||
@@ -755,4 +716,4 @@ void PLBound::Print(std::ostream &outp) const
|
||||
ubound.Print(outp);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+20
-71
@@ -19,18 +19,14 @@ namespace mfem
|
||||
{
|
||||
|
||||
/** @name Piecewise linear bounds of bases
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on
|
||||
the grid function in each element. The bounds for the bases are constructed
|
||||
based on the following parameters:
|
||||
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
|
||||
|
||||
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
|
||||
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre
|
||||
nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
|
||||
2 - Positive/Bernstein bases on uniformly distributed nodes,
|
||||
|
||||
(iii) @b ncp: number of control points used to construct the piecewise
|
||||
linear bounds
|
||||
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
|
||||
|
||||
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
|
||||
1 - Chebyshev.
|
||||
@@ -39,9 +35,7 @@ namespace mfem
|
||||
|
||||
If the user does not specify @b ncp and @b cp_type, the minimum value of
|
||||
@b ncp is used that would bound the bases for the @b cp_type. We default
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases.
|
||||
Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and
|
||||
increasing @b ncp results in tighter bounds.
|
||||
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
|
||||
|
||||
Finally, only tensor-product elements are currently supported.
|
||||
|
||||
@@ -60,7 +54,7 @@ private:
|
||||
bool proj = true; // Use linear projection to compute bounds.
|
||||
real_t tol = 0.0; // offset bounds to avoid round-off errors
|
||||
Vector nodes, weights, control_points;
|
||||
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
|
||||
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
|
||||
// Some auxillary storage for computing the bounds with Bernstein
|
||||
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
|
||||
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
|
||||
@@ -86,9 +80,6 @@ private:
|
||||
{3,5,8,9,11,12,13,13,14,15,16}
|
||||
};
|
||||
|
||||
/// Helper function to extract lower or upper bounding matrix
|
||||
DenseMatrix GetBoundingMatrix(int dim, bool is_lower) const;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
@@ -101,82 +92,40 @@ public:
|
||||
PLBound(const FiniteElementSpace *fes,
|
||||
const int ncp_i = -1, const int cp_type_i = 0);
|
||||
|
||||
/// Get minimum number of control points needed to bound the given bases
|
||||
// Get minimum number of control points needed to bound the given bases
|
||||
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
int cp_type_i) const;
|
||||
|
||||
/// Print information about the bounds
|
||||
// Print information about the bounds
|
||||
void Print(std::ostream &outp = mfem::out) const;
|
||||
|
||||
/** @brief Enable (default) or disable linear projection before bounding.
|
||||
*
|
||||
* @details This projection increases the computational cost but results in
|
||||
* tighter bounds.
|
||||
*/
|
||||
// Enable (default) or disable linear projection before bounding.
|
||||
// This projection increases the computational cost but results in tighter
|
||||
// bounds.
|
||||
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
|
||||
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D/2D/3D.
|
||||
*
|
||||
* @param[in] rdim The spatial dimension of the element (1, 2, or 3).
|
||||
* @param[in] coeff The vector of lexicographically-ordered coefficients.
|
||||
* Should be of size nb^rdim, where nb is the number of
|
||||
* bases/nodes in 1D. These coefficients must correspond
|
||||
* to the bases type and number of bases, used in the
|
||||
* constructor of PLBound.
|
||||
*
|
||||
* @param[out] intmin The vector of minimum bound for all control points.
|
||||
* @param[out] intmax The vector of maximum bound for all control points.
|
||||
* Both intmin and intmax are of size ncp^rdim, where
|
||||
* ncp is the number of control points in 1D, and are
|
||||
* ordered lexicographically.
|
||||
*/
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D/2D/3D.
|
||||
void GetNDBounds(const int rdim, const Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Get number of control points used to compute the bounds.
|
||||
int GetNControlPoints() const { return ncp; }
|
||||
|
||||
/// Get 1D control point locations (lexicographic order) in [0,1].
|
||||
const Vector &GetControlPoints() const { return control_points; }
|
||||
|
||||
/** @brief Get lower and upper bounding matrix (ncp^dim x nb^dim)
|
||||
*
|
||||
* @details The matrices can be used to compute the bounds at control points
|
||||
* by a simple matrix-vector product with the
|
||||
* lexicographically-ordered nodal coefficients.
|
||||
* The resulting output is also lexicographically-ordered.
|
||||
*
|
||||
* @note These matrices do not account for the linear projection step that
|
||||
* is optionally done in GetNDBounds before bounding the function.
|
||||
*/
|
||||
///@{
|
||||
DenseMatrix GetLowerBoundMatrix(int dim = 1) const;
|
||||
DenseMatrix GetUpperBoundMatrix(int dim = 1) const;
|
||||
///@}
|
||||
|
||||
private:
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 1D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D.
|
||||
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 2D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 2D.
|
||||
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
|
||||
* nodal coefficients in @a coeff in 3D.
|
||||
* See GetNDBounds for details of the input and output parameters.
|
||||
*/
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 3D.
|
||||
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
* for Bernstein bases.
|
||||
*/
|
||||
/// Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
/// for Bernstein bases.
|
||||
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
|
||||
|
||||
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
|
||||
|
||||
@@ -82,25 +82,6 @@ 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; }
|
||||
|
||||
+8
-11
@@ -90,8 +90,8 @@ void map_quadrature_data_to_fields_impl(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,9 +169,8 @@ void map_quadrature_data_to_fields_tensor_impl_1d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
"for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,9 +306,8 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,9 +492,8 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#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"
|
||||
|
||||
+49
-127
@@ -663,59 +663,58 @@ const
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
// Do not run if the new Dof2Quad is already present, e.g. added in a
|
||||
// previous call or added by another omp thread.
|
||||
// If the new Dof2Quad is already present, e.g. added in a previous call
|
||||
// or added by another omp thread, return.
|
||||
if (DofToQuad::SearchArray(dof2quad_array, ir,
|
||||
DofToQuad::LEXICOGRAPHIC_FULL) == nullptr)
|
||||
DofToQuad::LEXICOGRAPHIC_FULL))
|
||||
{ return; }
|
||||
|
||||
// Undo the native ordering which is what FiniteElement::GetDofToQuad
|
||||
// returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
|
||||
const int b_dim = (range_type == VECTOR) ? dim : 1;
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
// Undo the native ordering which is what FiniteElement::GetDofToQuad
|
||||
// returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
|
||||
const int b_dim = (range_type == VECTOR) ? dim : 1;
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
for (int d = 0; d < b_dim; d++)
|
||||
{
|
||||
for (int d = 0; d < b_dim; d++)
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
|
||||
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
|
||||
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
|
||||
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
|
||||
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
|
||||
const int g_dim = [this]()
|
||||
{
|
||||
switch (deriv_type)
|
||||
{
|
||||
case GRAD: return dim;
|
||||
case DIV: return 1;
|
||||
case CURL: return cdim;
|
||||
default: return 0;
|
||||
}
|
||||
}();
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int d = 0; d < g_dim; d++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
|
||||
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
|
||||
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q_new);
|
||||
}
|
||||
|
||||
const int g_dim = [this]()
|
||||
{
|
||||
switch (deriv_type)
|
||||
{
|
||||
case GRAD: return dim;
|
||||
case DIV: return 1;
|
||||
case CURL: return cdim;
|
||||
default: return 0;
|
||||
}
|
||||
}();
|
||||
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
for (int d = 0; d < g_dim; d++)
|
||||
{
|
||||
for (int j = 0; j < dof; j++)
|
||||
{
|
||||
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
|
||||
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
|
||||
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dof2quad_array.Append(d2q_new);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,50 +1043,9 @@ void VectorFiniteElement::SetDerivMembers()
|
||||
switch (map_type)
|
||||
{
|
||||
case H_DIV:
|
||||
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);
|
||||
}
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
case H_CURL:
|
||||
switch (dim)
|
||||
@@ -1105,49 +1063,13 @@ void VectorFiniteElement::SetDerivMembers()
|
||||
break;
|
||||
case 1:
|
||||
deriv_type = NONE;
|
||||
deriv_range_type = UNKNOWN_RANGE_TYPE;
|
||||
deriv_map_type = UNKNOWN_MAP_TYPE;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
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);
|
||||
}
|
||||
|
||||
+3
-31
@@ -295,20 +295,10 @@ 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
|
||||
@@ -340,28 +330,12 @@ 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 and the
|
||||
width of the DenseMatrix argument in
|
||||
CalcVShape(const IntegrationPoint &ip, DenseMatrix &shape). */
|
||||
which is also the dimension of the interpolation operation. */
|
||||
int GetRangeDim() const { return vdim; }
|
||||
|
||||
/** @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). */
|
||||
/// Returns the dimension of the curl for vector-valued finite elements.
|
||||
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; }
|
||||
|
||||
@@ -1016,8 +990,6 @@ 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
|
||||
|
||||
+1
-1
@@ -589,7 +589,7 @@ void H1_TriangleElement::CalcHessian(const IntegrationPoint &ip,
|
||||
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
|
||||
Vector dshape_x(p + 1), dshape_y(p + 1), dshape_l(p + 1);
|
||||
Vector ddshape_x(p + 1), ddshape_y(p + 1), ddshape_l(p + 1);
|
||||
DenseMatrix ddu(dof, (dim*(dim+1))/2);
|
||||
DenseMatrix ddu(dof, dim);
|
||||
#endif
|
||||
|
||||
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x, ddshape_x);
|
||||
|
||||
@@ -1,973 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_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_R1D, FunctionSpace::Pk)
|
||||
H_CURL, 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_R1D, FunctionSpace::Pk),
|
||||
H_CURL, 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_R2D, FunctionSpace::Pk),
|
||||
H_CURL, 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_R2D, FunctionSpace::Pk),
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
tk(tk_fe),
|
||||
dof_map(dof),
|
||||
dof2tk(dof)
|
||||
|
||||
@@ -663,9 +663,6 @@ 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;
|
||||
|
||||
@@ -708,9 +705,6 @@ 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_R1D, FunctionSpace::Pk),
|
||||
H_DIV, 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_R2D, FunctionSpace::Pk),
|
||||
H_DIV, 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_R2D, FunctionSpace::Pk),
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
nk(nk_fe),
|
||||
dof_map(dof),
|
||||
dof2nk(dof)
|
||||
|
||||
@@ -510,9 +510,6 @@ 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;
|
||||
|
||||
@@ -550,9 +547,6 @@ 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,21 +243,11 @@ 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));
|
||||
@@ -2132,173 +2122,6 @@ 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,8 +111,6 @@ 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) |
|
||||
@@ -319,59 +317,6 @@ 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
|
||||
|
||||
+22
-19
@@ -282,7 +282,14 @@ int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
|
||||
void FiniteElementSpace::AdjustVDofs(Array<int> &vdofs)
|
||||
{
|
||||
int n = vdofs.Size(), *vdof = vdofs;
|
||||
for (int i = 0; i < n; i++) { vdof[i] = UnsignIndex(vdof[i]); }
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int j;
|
||||
if ((j = vdof[i]) < 0)
|
||||
{
|
||||
vdof[i] = -1-j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
@@ -476,14 +483,13 @@ void FiniteElementSpace::ReorderElementToDofTable()
|
||||
for (int k = 0, dof_counter = 0; k < nnz; k++)
|
||||
{
|
||||
const int sdof = J[k]; // signed dof
|
||||
const int dof = UnsignIndex(sdof);
|
||||
const int dof = (sdof < 0) ? -1-sdof : sdof;
|
||||
int new_dof = dof_marker[dof];
|
||||
if (new_dof < 0)
|
||||
{
|
||||
dof_marker[dof] = new_dof = dof_counter++;
|
||||
}
|
||||
// Preserve the sign of sdof
|
||||
J[k] = (sdof < 0) ? FlipIndexSign(new_dof) : new_dof;
|
||||
J[k] = (sdof < 0) ? -1-new_dof : new_dof; // preserve the sign of sdof
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +547,7 @@ void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
|
||||
{
|
||||
for (auto d : dofs)
|
||||
{
|
||||
mark_array[UnsignIndex(d)] = -1;
|
||||
mark_array[d >= 0 ? d : -1 - d] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +931,7 @@ void FiniteElementSpace::AddDependencies(
|
||||
if (std::abs(coef) > 1e-12)
|
||||
{
|
||||
const int mdof = master_dofs[j];
|
||||
if (mdof != sdof && mdof != FlipIndexSign(sdof))
|
||||
if (mdof != sdof && mdof != (-1-sdof))
|
||||
{
|
||||
deps.Add(sdof, mdof, coef);
|
||||
}
|
||||
@@ -1018,7 +1024,7 @@ int FiniteElementSpace::GetDegenerateFaceDofs(int index, Array<int> &dofs,
|
||||
// FiniteElementSpace::AddDependencies.
|
||||
|
||||
Array<int> edof;
|
||||
int order = GetEdgeDofs(FlipIndexSign(index), edof, variant);
|
||||
int order = GetEdgeDofs(-1 - index, edof, variant);
|
||||
|
||||
int nv = fec->DofForGeometry(Geometry::POINT);
|
||||
int ne = fec->DofForGeometry(Geometry::SEGMENT);
|
||||
@@ -1704,8 +1710,8 @@ SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
|
||||
|
||||
for (int i = 0; i < fine_ldof; i++)
|
||||
{
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
|
||||
if (!mark[m])
|
||||
{
|
||||
@@ -1766,7 +1772,7 @@ SparseMatrix *FiniteElementSpace::VariableOrderRefinementMatrix(
|
||||
for (int i = 0; i < fine_ldof; i++)
|
||||
{
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
|
||||
if (!mark[m])
|
||||
{
|
||||
@@ -2476,8 +2482,8 @@ SparseMatrix* FiniteElementSpace::DerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
@@ -3195,7 +3201,7 @@ void FiniteElementSpace::CalcEdgeFaceVarOrders(
|
||||
else
|
||||
{
|
||||
// degenerate face (i.e., edge-face constraint)
|
||||
slave_orders |= edge_orders[FlipIndexSign(slave.index)];
|
||||
slave_orders |= edge_orders[-1 - slave.index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3877,12 +3883,9 @@ 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). 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)
|
||||
// consistency check: fec->GetOrder() and FE->GetOrder() should return
|
||||
// the same value (for standard, constant-order spaces)
|
||||
if (!IsVariableOrder() && FE->GetDim() > 0)
|
||||
{
|
||||
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
|
||||
"internal error: " <<
|
||||
|
||||
+1
-1
@@ -1150,7 +1150,7 @@ public:
|
||||
|
||||
/// Helper to return the DOF associated with a sign encoded DOF
|
||||
static inline int DecodeDof(int dof)
|
||||
{ return UnsignIndex(dof); }
|
||||
{ return (dof >= 0) ? dof : (-1 - dof); }
|
||||
|
||||
/// Helper to determine the DOF and sign of a sign encoded DOF
|
||||
static inline int DecodeDof(int dof, real_t& sign)
|
||||
|
||||
+44
-602
@@ -30,7 +30,6 @@
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -3137,29 +3136,52 @@ void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[],
|
||||
}
|
||||
|
||||
void GridFunction::ProjectBdrCoefficientNormal(
|
||||
Coefficient *coeff, VectorCoefficient *vcoeff, const Array<int> &bdr_attr)
|
||||
VectorCoefficient &vcoeff, const Array<int> &bdr_attr)
|
||||
{
|
||||
if (fes->GetNBE() > 0)
|
||||
{
|
||||
// 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!");
|
||||
}
|
||||
#if 0
|
||||
// implementation for the case when the face dofs are integrals of the
|
||||
// normal component.
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec, shape;
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
if (bdr_attr[fes->GetBdrAttribute(i)-1] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
fe = fes->GetBE(i);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
int intorder = 2*fe->GetOrder(); // !!!
|
||||
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(), intorder);
|
||||
int nd = fe->GetDof();
|
||||
lvec.SetSize(nd);
|
||||
shape.SetSize(nd);
|
||||
lvec = 0.0;
|
||||
for (int j = 0; j < ir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
fe->CalcShape(ip, shape);
|
||||
lvec.Add(ip.weight * (vc * nor), shape);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#else
|
||||
// implementation for the case when the face dofs are scaled point
|
||||
// values of the normal component.
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector vc, nor, lvec;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
DofTransformation doftrans;
|
||||
if (vcoeff)
|
||||
{
|
||||
const int dim = vcoeff->GetVDim();
|
||||
vc.SetSize(dim);
|
||||
nor.SetSize(dim);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
@@ -3175,22 +3197,15 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
T->SetIntPoint(&ip);
|
||||
if (coeff)
|
||||
{
|
||||
const real_t c = coeff->Eval(*T, ip);
|
||||
lvec(j) = c * T->Weight();
|
||||
}
|
||||
else if (vcoeff)
|
||||
{
|
||||
vcoeff->Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
vcoeff.Eval(vc, *T, ip);
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs, doftrans);
|
||||
doftrans.TransformPrimal(lvec);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GridFunction::ProjectBdrCoefficientTangent(
|
||||
@@ -4991,14 +5006,6 @@ real_t ExtrudeCoefficient::Eval(ElementTransformation &T,
|
||||
return sol_in.Eval(*T_in, ip);
|
||||
}
|
||||
|
||||
void VectorExtrudeCoefficient::Eval(Vector &v, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
ElementTransformation *T_in =
|
||||
mesh_in->GetElementTransformation(T.ElementNo / n);
|
||||
T_in->SetIntPoint(&ip);
|
||||
sol_in.Eval(v, *T_in, ip);
|
||||
}
|
||||
|
||||
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
GridFunction *sol, const int ny)
|
||||
@@ -5049,17 +5056,10 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
return NULL;
|
||||
}
|
||||
FiniteElementSpace *solfes2d;
|
||||
const int vdim = sol->FESpace()->GetVDim();
|
||||
solfes2d = new FiniteElementSpace(mesh2d, solfec2d, vdim);
|
||||
// assuming sol is scalar
|
||||
solfes2d = new FiniteElementSpace(mesh2d, solfec2d);
|
||||
sol2d = new GridFunction(solfes2d);
|
||||
sol2d->MakeOwner(solfec2d);
|
||||
if (vdim > 1)
|
||||
{
|
||||
VectorGridFunctionCoefficient vcsol(sol);
|
||||
VectorExtrudeCoefficient vc2d(mesh, vcsol, ny);
|
||||
sol2d->ProjectCoefficient(vc2d);
|
||||
}
|
||||
else
|
||||
{
|
||||
GridFunctionCoefficient csol(sol);
|
||||
ExtrudeCoefficient c2d(mesh, csol, ny);
|
||||
@@ -5117,103 +5117,6 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
const PLBound &plb,
|
||||
const Vector &ref_range,
|
||||
const int vdim,
|
||||
Vector &lower, Vector &upper,
|
||||
Vector &control_pos) const
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
const IntegrationRule ir_in = fe->GetNodes();
|
||||
IntegrationRule ir_new(ir_in.GetNPoints());
|
||||
const int dim = fes->GetMesh()->Dimension();
|
||||
const L2_FECollection *l2fec = dynamic_cast<const L2_FECollection *>
|
||||
(fes->FEColl());
|
||||
|
||||
const TensorBasisElement *tbe =
|
||||
dynamic_cast<const TensorBasisElement *>(fe);
|
||||
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
|
||||
|
||||
const Array<int> &dof_map = tbe->GetDofMap();
|
||||
bool lexico = (dof_map.Size() == 0);
|
||||
bool bern = (tbe->GetBasisType() == BasisType::Positive);
|
||||
bool h1 = (l2fec == nullptr);
|
||||
|
||||
Vector loc_data; // gridfunction values
|
||||
// Construct an integration rule to evaluate the gridfunction in
|
||||
// subinterval.
|
||||
for (int i = 0; i < ir_in.GetNPoints(); i++)
|
||||
{
|
||||
IntegrationPoint &ip_new = ir_new.IntPoint(i);
|
||||
const IntegrationPoint &ip_old =
|
||||
ir_in.IntPoint((lexico || bern) ? i : dof_map[i]);
|
||||
Vector ip_coord(dim);
|
||||
ip_old.Get(ip_coord.GetData(), dim);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
ip_coord(d) = ref_range(d) +
|
||||
(ref_range(dim+d) - ref_range(d)) * ip_coord(d);
|
||||
}
|
||||
ip_new.Set(ip_coord.GetData(), dim);
|
||||
}
|
||||
GetValues(elem, ir_new, loc_data, vdim);
|
||||
// At this point, the loc_data contains function values ordered
|
||||
// lexicographically, unless we are using Bernstein bases.
|
||||
// For Bernstein, we need to project and get coefficients first.
|
||||
|
||||
// For bernstein, we get coefficients corresponding to these function values
|
||||
if (bern)
|
||||
{
|
||||
int bt = 4; // BasisType::ClosedUniform
|
||||
int o = fe->GetOrder();
|
||||
DenseMatrix projmat;
|
||||
NodalTensorFiniteElement *ntfe = nullptr;
|
||||
if (dim == 1)
|
||||
{
|
||||
if (h1) { ntfe = new H1_SegmentElement(o, bt); }
|
||||
else { ntfe = new L2_SegmentElement(o, bt); }
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
if (h1) { ntfe = new H1_QuadrilateralElement(o, bt); }
|
||||
else { ntfe = new L2_QuadrilateralElement(o, bt); }
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
if (h1) { ntfe = new H1_HexahedronElement(o, bt); }
|
||||
else { ntfe = new L2_HexahedronElement(o, bt); }
|
||||
}
|
||||
// projection matrix from H1 to Positive
|
||||
ElementTransformation *eltran = fes->GetElementTransformation(elem);
|
||||
fe->Project(*ntfe, *eltran, projmat);
|
||||
Vector loc_data_temp(loc_data.Size());
|
||||
projmat.Mult(loc_data, loc_data_temp);
|
||||
for (int i = 0; i < dof_map.Size(); i++)
|
||||
{
|
||||
loc_data(i) = loc_data_temp(dof_map[i]);
|
||||
}
|
||||
if (dof_map.Size() == 0) { loc_data = loc_data_temp; }
|
||||
delete ntfe;
|
||||
}
|
||||
|
||||
// Get bounds at control points
|
||||
plb.GetNDBounds(dim, loc_data, lower, upper);
|
||||
|
||||
// Save control point positions
|
||||
int ncp = plb.GetNControlPoints();
|
||||
control_pos.SetSize(dim * ncp);
|
||||
const Vector control_pos_1D = plb.GetControlPoints();
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
control_pos(i + d*ncp) =
|
||||
ref_range(d) + (ref_range(dim+d)-ref_range(d))*control_pos_1D(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim) const
|
||||
@@ -5294,467 +5197,6 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
return plb;
|
||||
}
|
||||
|
||||
struct IntervalNode
|
||||
{
|
||||
real_t val_min;
|
||||
real_t val_max;
|
||||
Array<IntervalNode *> child;
|
||||
IntervalNode(real_t vmin, real_t vmax)
|
||||
: val_min(vmin), val_max(vmax)
|
||||
{
|
||||
child.SetSize(0);
|
||||
}
|
||||
void AddChild(IntervalNode *ch) { child.Append(ch); }
|
||||
real_t GetChildMinLower()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_min;
|
||||
}
|
||||
real_t valmin = numeric_limits<real_t>::max();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMinLower();
|
||||
valmin = std::min(valmin, candidate);
|
||||
}
|
||||
return valmin;
|
||||
}
|
||||
real_t GetChildMinUpper()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_max;
|
||||
}
|
||||
real_t valmax = numeric_limits<real_t>::max();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMinUpper();
|
||||
valmax = std::min(valmax, candidate);
|
||||
}
|
||||
return valmax;
|
||||
}
|
||||
real_t GetChildMaxLower()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_min;
|
||||
}
|
||||
real_t valmin = numeric_limits<real_t>::lowest();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMaxLower();
|
||||
valmin = std::max(valmin, candidate);
|
||||
}
|
||||
return valmin;
|
||||
}
|
||||
real_t GetChildMaxUpper()
|
||||
{
|
||||
if (child.Size() == 0)
|
||||
{
|
||||
return val_max;
|
||||
}
|
||||
real_t valmax = numeric_limits<real_t>::lowest();
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
real_t candidate = child[i]->GetChildMaxUpper();
|
||||
valmax = std::max(valmax, candidate);
|
||||
}
|
||||
return valmax;
|
||||
}
|
||||
void DeleteChildren()
|
||||
{
|
||||
for (int i = 0; i < child.Size(); i++)
|
||||
{
|
||||
child[i]->DeleteChildren();
|
||||
delete child[i];
|
||||
}
|
||||
child.SetSize(0);
|
||||
}
|
||||
};
|
||||
|
||||
struct SearchInterval
|
||||
{
|
||||
Vector ref_range;
|
||||
int depth;
|
||||
IntervalNode *node;
|
||||
SearchInterval(const Vector &ref_range_in, int d, IntervalNode *n)
|
||||
: ref_range(ref_range_in), depth(d), node(n)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct IntervalCompareMin
|
||||
{
|
||||
bool operator()(const SearchInterval *a, const SearchInterval *b) const
|
||||
{
|
||||
return a->node->val_min > b->node->val_min;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntervalCompareMax
|
||||
{
|
||||
bool operator()(const SearchInterval *a, const SearchInterval *b) const
|
||||
{
|
||||
return a->node->val_max < b->node->val_max;
|
||||
}
|
||||
};
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol) const
|
||||
{
|
||||
real_t min_threshold = std::numeric_limits<real_t>::max();
|
||||
return EstimateFunctionMinimum(elem, plb, vdim, max_depth, tol,
|
||||
min_threshold);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol, real_t &min_threshold) const
|
||||
{
|
||||
const int dim = this->FESpace()->GetMesh()->Dimension();
|
||||
const int ncp = plb.GetNControlPoints();
|
||||
Vector pos_range(2*dim); pos_range = 0.0;
|
||||
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
|
||||
Vector lower, upper, cp_ref_loc;
|
||||
|
||||
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
|
||||
real_t val_min = lower.Min();
|
||||
real_t val_max = upper.Min();
|
||||
|
||||
min_threshold = std::min(min_threshold, val_max);
|
||||
|
||||
// Pruning: if the element's lower bound is greater than the current global
|
||||
// upper bound, this element cannot contain the global minimum.
|
||||
if (val_min >= min_threshold)
|
||||
{
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
|
||||
if (val_min == val_max || max_depth == 0)
|
||||
{
|
||||
min_threshold = std::min(min_threshold, val_min);
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
real_t abs_tol = tol*(val_max-val_min);
|
||||
|
||||
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
|
||||
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
|
||||
initial_node);
|
||||
|
||||
std::priority_queue<SearchInterval*,
|
||||
std::vector<SearchInterval*>, IntervalCompareMin> pq;
|
||||
pq.push(initial_interval);
|
||||
|
||||
real_t min_upper_bound = upper.Min();
|
||||
real_t min_lower_bound = lower.Min();
|
||||
|
||||
while (!pq.empty())
|
||||
{
|
||||
SearchInterval *current = pq.top();
|
||||
pq.pop();
|
||||
int curr_depth = current->depth;
|
||||
|
||||
// Reached max depth or this interval cannot contain the global minimum
|
||||
if (current->node->val_min >= min_threshold || curr_depth >= max_depth)
|
||||
{
|
||||
delete current;
|
||||
continue;
|
||||
}
|
||||
|
||||
min_lower_bound = initial_node->GetChildMinLower();
|
||||
if (min_upper_bound - min_lower_bound < abs_tol)
|
||||
{
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subdivide the interval and get bounds on it
|
||||
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
|
||||
vdim, lower, upper, cp_ref_loc);
|
||||
|
||||
// process the bounds and create sub-intervals
|
||||
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
|
||||
{
|
||||
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
|
||||
{
|
||||
for (int i = 0; i < ncp-1; i++)
|
||||
{
|
||||
real_t lv = 0.0, uv = 0.0;
|
||||
if (dim == 1)
|
||||
{
|
||||
lv = std::min(lower(i), lower(i+1));
|
||||
uv = std::min(upper(i), upper(i+1));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
lv = std::min({lower(i + j*ncp), lower((i+1) + j*ncp),
|
||||
lower(i + (j+1)*ncp),
|
||||
lower((i+1) + (j+1)*ncp)});
|
||||
uv = std::min({upper(i + j*ncp), upper((i+1) + j*ncp),
|
||||
upper(i + (j+1)*ncp),
|
||||
upper((i+1) + (j+1)*ncp)});
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
lv = std::min({lower(i + j*ncp + k*ncp*ncp),
|
||||
lower((i+1) + j*ncp + k*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + k*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
lower(i + j*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
uv = std::min({upper(i + j*ncp + k*ncp*ncp),
|
||||
upper((i+1) + j*ncp + k*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + k*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
upper(i + j*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
}
|
||||
IntervalNode *child_node = new IntervalNode(lv, uv);
|
||||
current->node->AddChild(child_node);
|
||||
|
||||
if (lv < min_threshold)
|
||||
{
|
||||
min_upper_bound = std::min(min_upper_bound, uv);
|
||||
min_threshold = std::min(min_threshold, uv);
|
||||
if (curr_depth < max_depth)
|
||||
{
|
||||
pos_range(0) = cp_ref_loc(i);
|
||||
pos_range(0+dim) = cp_ref_loc(i+1);
|
||||
if (dim >= 2)
|
||||
{
|
||||
pos_range(1) = cp_ref_loc(ncp + j);
|
||||
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
pos_range(2) = cp_ref_loc(2*ncp + k);
|
||||
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
|
||||
}
|
||||
SearchInterval *child_interval =
|
||||
new SearchInterval(pos_range, curr_depth + 1,
|
||||
child_node);
|
||||
pq.push(child_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
|
||||
// clean up remaining intervals in queue
|
||||
while (!pq.empty())
|
||||
{
|
||||
delete pq.top();
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
min_lower_bound = initial_node->GetChildMinLower();
|
||||
initial_node->DeleteChildren();
|
||||
delete initial_node;
|
||||
|
||||
min_threshold = std::min(min_threshold, min_lower_bound);
|
||||
return std::make_pair(min_lower_bound, min_upper_bound);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol) const
|
||||
{
|
||||
real_t max_threshold = std::numeric_limits<real_t>::lowest();
|
||||
return EstimateFunctionMaximum(elem, plb, vdim, max_depth, tol,
|
||||
max_threshold);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int elem, const PLBound &plb, const int vdim,
|
||||
const int max_depth, const real_t tol, real_t &max_threshold) const
|
||||
{
|
||||
const int dim = this->FESpace()->GetMesh()->Dimension();
|
||||
const int ncp = plb.GetNControlPoints();
|
||||
Vector pos_range(2*dim); pos_range = 0.0;
|
||||
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
|
||||
Vector lower, upper, cp_ref_loc;
|
||||
|
||||
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
|
||||
real_t val_min = lower.Max();
|
||||
real_t val_max = upper.Max();
|
||||
|
||||
max_threshold = std::max(max_threshold, val_min);
|
||||
|
||||
// Pruning: if the element's upper bound is less than the current global
|
||||
// lower bound, this element cannot contain the global maximum.
|
||||
if (val_max <= max_threshold)
|
||||
{
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
|
||||
if (val_min == val_max || max_depth == 0)
|
||||
{
|
||||
max_threshold = std::max(max_threshold, val_max);
|
||||
return std::make_pair(val_min, val_max);
|
||||
}
|
||||
real_t abs_tol = tol*(val_max-val_min);
|
||||
|
||||
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
|
||||
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
|
||||
initial_node);
|
||||
|
||||
std::priority_queue<SearchInterval*,
|
||||
std::vector<SearchInterval*>, IntervalCompareMax> pq;
|
||||
pq.push(initial_interval);
|
||||
|
||||
real_t max_lower_bound = val_min;
|
||||
real_t max_upper_bound = val_max;
|
||||
|
||||
while (!pq.empty())
|
||||
{
|
||||
SearchInterval *current = pq.top();
|
||||
pq.pop();
|
||||
int curr_depth = current->depth;
|
||||
|
||||
// Reached max depth or this interval cannot contain the global maximum.
|
||||
if (current->node->val_max <= max_threshold || curr_depth >= max_depth)
|
||||
{
|
||||
delete current;
|
||||
continue;
|
||||
}
|
||||
|
||||
max_upper_bound = initial_node->GetChildMaxUpper();
|
||||
if (max_upper_bound - max_lower_bound < abs_tol)
|
||||
{
|
||||
delete current;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subdivide the interval and get bounds on it
|
||||
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
|
||||
vdim, lower, upper, cp_ref_loc);
|
||||
|
||||
// process the bounds and create sub-intervals
|
||||
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
|
||||
{
|
||||
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
|
||||
{
|
||||
for (int i = 0; i < ncp-1; i++)
|
||||
{
|
||||
real_t lv = 0.0, uv = 0.0;
|
||||
if (dim == 1)
|
||||
{
|
||||
lv = std::max(lower(i), lower(i+1));
|
||||
uv = std::max(upper(i), upper(i+1));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
lv = std::max({lower(i + j*ncp), lower((i+1) + j*ncp),
|
||||
lower(i + (j+1)*ncp),
|
||||
lower((i+1) + (j+1)*ncp)});
|
||||
uv = std::max({upper(i + j*ncp), upper((i+1) + j*ncp),
|
||||
upper(i + (j+1)*ncp),
|
||||
upper((i+1) + (j+1)*ncp)});
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
lv = std::max({lower(i + j*ncp + k*ncp*ncp),
|
||||
lower((i+1) + j*ncp + k*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + k*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
lower(i + j*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
uv = std::max({upper(i + j*ncp + k*ncp*ncp),
|
||||
upper((i+1) + j*ncp + k*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + k*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
|
||||
upper(i + j*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
|
||||
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
|
||||
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
|
||||
}
|
||||
IntervalNode *child_node = new IntervalNode(lv, uv);
|
||||
current->node->AddChild(child_node);
|
||||
|
||||
if (uv > max_threshold)
|
||||
{
|
||||
max_lower_bound = std::max(max_lower_bound, lv);
|
||||
max_threshold = std::max(max_threshold, lv);
|
||||
if (curr_depth < max_depth)
|
||||
{
|
||||
pos_range(0) = cp_ref_loc(i);
|
||||
pos_range(0+dim) = cp_ref_loc(i+1);
|
||||
if (dim >= 2)
|
||||
{
|
||||
pos_range(1) = cp_ref_loc(ncp + j);
|
||||
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
pos_range(2) = cp_ref_loc(2*ncp + k);
|
||||
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
|
||||
}
|
||||
SearchInterval *child_interval =
|
||||
new SearchInterval(pos_range, curr_depth + 1,
|
||||
child_node);
|
||||
pq.push(child_interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete current;
|
||||
}
|
||||
// clean up remaining intervals in queue
|
||||
while (!pq.empty())
|
||||
{
|
||||
delete pq.top();
|
||||
pq.pop();
|
||||
}
|
||||
|
||||
max_upper_bound = initial_node->GetChildMaxUpper();
|
||||
initial_node->DeleteChildren();
|
||||
delete initial_node;
|
||||
max_threshold = std::max(max_threshold, max_upper_bound);
|
||||
|
||||
return std::make_pair(max_lower_bound, max_upper_bound);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
real_t global_min_lower = std::numeric_limits<real_t>::max();
|
||||
real_t global_min_upper = std::numeric_limits<real_t>::max();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
std::pair<real_t, real_t> min_pair =
|
||||
EstimateFunctionMinimum(i, plb, vdim, max_depth, tol,
|
||||
global_min_lower);
|
||||
global_min_upper = std::min(global_min_upper, min_pair.second);
|
||||
}
|
||||
return std::make_pair(global_min_lower, global_min_upper);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
real_t global_max_lower = std::numeric_limits<real_t>::lowest();
|
||||
real_t global_max_upper = std::numeric_limits<real_t>::lowest();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
std::pair<real_t, real_t> max_pair =
|
||||
EstimateFunctionMaximum(i, plb, vdim, max_depth, tol,
|
||||
global_max_upper);
|
||||
global_max_lower = std::max(global_max_lower, max_pair.first);
|
||||
}
|
||||
return std::make_pair(global_max_lower, global_max_upper);
|
||||
}
|
||||
|
||||
}
|
||||
+16
-179
@@ -532,9 +532,6 @@ public:
|
||||
std::unique_ptr<GridFunction> ProlongateToMaxOrder() const;
|
||||
|
||||
protected:
|
||||
void ProjectBdrCoefficientNormal(Coefficient *coeff, VectorCoefficient *vcoeff,
|
||||
const Array<int> &attr);
|
||||
|
||||
/** @brief Accumulates (depending on @a type) the values of @a coeff at all
|
||||
shared vdofs and counts in how many zones each vdof appears. */
|
||||
void AccumulateAndCountZones(Coefficient &coeff, AvgType type,
|
||||
@@ -567,70 +564,6 @@ protected:
|
||||
/// P-refinement version of Update().
|
||||
void UpdatePRef();
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem
|
||||
* if it is below a certain @a min_threshold.
|
||||
*
|
||||
* @details For a given element \p elem and grid function component \p vdim
|
||||
* an estimate of the function minimum is the minimum of the piecewise
|
||||
* linear lower bound obtained using the given PLBound object. The actual
|
||||
* minimum is between [minimum lower bound, minimum upper bound]. We
|
||||
* improve the estimate of the function minimum by recursively
|
||||
* subdividing the interval with the lowest lower bound, and computing
|
||||
* bounds on the sub-intervals.
|
||||
* This process continues until (i) the maximum recursion depth is reached
|
||||
* or (ii) the difference between the minimum upper bound and minimum lower
|
||||
* bound is less than a certain tolerance (\p tol * [initial maximum
|
||||
* upper bound - initial minimum lower bound]).
|
||||
* The function also terminates if the lowest minima estimate is found
|
||||
* to be above the given threshold \p min_threshold. This is useful when
|
||||
* we are interested in computing the global minimum of the function
|
||||
* over all elements. In this case we can reject elements where the lowest
|
||||
* bound is above the current global minimum. In case the function
|
||||
* minimum on the element is below the global minimum, we update
|
||||
* \p min_threshold.
|
||||
*
|
||||
* We return a pair of values that bracket the actual minimum, i.e.
|
||||
* [min_lower_bound, min_upper_bound].
|
||||
*/
|
||||
std::pair<real_t,real_t> EstimateFunctionMinimum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol,
|
||||
real_t &min_threshold)const;
|
||||
|
||||
/** @brief Estimate the maximum value of the GridFunction in element @a elem
|
||||
* if it is below a certain @a max_threshold.
|
||||
*
|
||||
* @details For a given element \p elem and grid function component \p vdim
|
||||
* an estimate of the function maximum is the maximum of the piecewise
|
||||
* linear upper bound obtained using the given PLBound object. The actual
|
||||
* maximum is between [maximum lower bound, maximum upper bound]. We
|
||||
* improve the estimate of the function maximum by recursively
|
||||
* subdividing the interval with the highest upper bound, and computing
|
||||
* bounds on the sub-intervals.
|
||||
* This process continues until (i) the maximum recursion depth is reached
|
||||
* or (ii) the difference between the maximum upper bound and maximum lower
|
||||
* bound is less than a certain tolerance (\p tol * [initial maximum
|
||||
* upper bound - initial maximum lower bound]).
|
||||
* The function also terminates if the highest maxima estimate is found
|
||||
* to be below the given threshold \p max_threshold. This is useful when
|
||||
* we are interested in computing the global maximum of the function
|
||||
* over all elements. In this case we can reject elements where the upper
|
||||
* bound is below the current global maximum. In case the function
|
||||
* maximum on the element is above the global maximum, we update
|
||||
* \p max_threshold.
|
||||
*
|
||||
* We return a pair of values that bracket the actual maximum, i.e.
|
||||
* [max_lower_bound, max_upper_bound].
|
||||
*/
|
||||
std::pair<real_t,real_t> EstimateFunctionMaximum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol,
|
||||
real_t &max_threshold)const;
|
||||
|
||||
public:
|
||||
/** @brief For each vdof, counts how many elements contain the vdof,
|
||||
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
|
||||
@@ -659,26 +592,15 @@ public:
|
||||
virtual void ProjectBdrCoefficient(Coefficient *coeff[],
|
||||
const Array<int> &attr);
|
||||
|
||||
/** @brief Project the normal component of the given VectorCoefficient on
|
||||
the boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr are
|
||||
projected. Assumes RT-type vector finite element GridFunction. */
|
||||
/** Project the normal component of the given VectorCoefficient on
|
||||
the boundary. Only boundary attributes that are marked in
|
||||
'bdr_attr' are projected. Assumes RT-type VectorFE GridFunction. */
|
||||
void ProjectBdrCoefficientNormal(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr)
|
||||
{ ProjectBdrCoefficientNormal(NULL, &vcoeff, bdr_attr); }
|
||||
|
||||
/** @brief Project the given Coefficient in the normal direction on the
|
||||
boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr are projected.
|
||||
Assumes RT-type vector finite element GridFunction. */
|
||||
void ProjectBdrCoefficientNormal(Coefficient &coeff,
|
||||
const Array<int> &bdr_attr)
|
||||
{ ProjectBdrCoefficientNormal(&coeff, NULL, bdr_attr); }
|
||||
const Array<int> &bdr_attr);
|
||||
|
||||
/** @brief Project the tangential components of the given VectorCoefficient
|
||||
on the boundary. */
|
||||
/** Only boundary attributes that are marked in @a bdr_attr
|
||||
are projected. Assumes ND-type vector finite element GridFunction. */
|
||||
on the boundary. Only boundary attributes that are marked in @a bdr_attr
|
||||
are projected. Assumes ND-type VectorFE GridFunction. */
|
||||
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr);
|
||||
|
||||
@@ -1740,21 +1662,21 @@ public:
|
||||
*/
|
||||
///@{
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on \p ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in \p lower and \p upper. We also return the
|
||||
/// points based on @a ref_factor, and returns the overall bounds for each
|
||||
/// vdim (across all elements) in @b lower and @b upper. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if \p vdim < 1.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// Note: For most cases, this method/interface will be sufficient.
|
||||
virtual PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on \p ref_factor, and returns the bounds for each element
|
||||
/// ordered byNodes:
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
/// ordered byVDim:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if \p vdim < 1.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
PLBound GetElementBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
@@ -1765,18 +1687,6 @@ public:
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1) const;
|
||||
|
||||
/** @brief Gets the bounds on given reference range inside an element.
|
||||
*
|
||||
* @details @a ref_range is a vector of size 2*dim that specifies the
|
||||
* lower and upper limits in each dimension of the reference element.
|
||||
* For example, in 2D, ref_range = [rmin, smin, rmax, smax].
|
||||
*/
|
||||
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
|
||||
const Vector &ref_range,
|
||||
const int vdim,
|
||||
Vector &lower, Vector &upper,
|
||||
Vector &control_pos) const;
|
||||
|
||||
/// Compute bounds on the grid function for the given element.
|
||||
/// The bounds are stored in @b lower and @b upper.
|
||||
void GetElementBounds(const int elem, const PLBound &plb,
|
||||
@@ -1784,45 +1694,11 @@ public:
|
||||
const int vdim = -1) const;
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byNodes:
|
||||
/// are returned in @b lower and @b upper, ordered byVDim:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
const int vdim=-1) const;
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
|
||||
*
|
||||
* @details See the protected version of EstimateFunctionMinimum for
|
||||
* details.
|
||||
*/
|
||||
std::pair<real_t, real_t> EstimateFunctionMinimum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
|
||||
*
|
||||
* @details See the protected version of EstimateFunctionMaximum for
|
||||
* details.
|
||||
*/
|
||||
std::pair<real_t, real_t> EstimateFunctionMaximum(const int elem,
|
||||
const PLBound &plb,
|
||||
const int vdim,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the GridFunction minimum across all elements. */
|
||||
virtual std::pair<real_t,real_t> EstimateFunctionMinimum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
|
||||
/** @brief Estimate the GridFunction maximum across all elements. */
|
||||
virtual std::pair<real_t,real_t> EstimateFunctionMaximum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const;
|
||||
///@}
|
||||
|
||||
/// Destroys grid function.
|
||||
@@ -1928,7 +1804,7 @@ real_t ComputeElementLpDistance(real_t p, int i,
|
||||
GridFunction& gf1, GridFunction& gf2);
|
||||
|
||||
|
||||
/// Class used for extruding a scalar coefficient
|
||||
/// Class used for extruding scalar GridFunctions
|
||||
class ExtrudeCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
@@ -1936,52 +1812,13 @@ private:
|
||||
Mesh *mesh_in;
|
||||
Coefficient &sol_in;
|
||||
public:
|
||||
/// Constructs an instance of VectorExtrudeCoefficient
|
||||
/**
|
||||
* @param m 1D mesh
|
||||
* @param s 1D vector coefficient
|
||||
* @param n_ number of transverse elements of the extruded mesh
|
||||
*/
|
||||
ExtrudeCoefficient(Mesh *m, Coefficient &s, int n_)
|
||||
: n(n_), mesh_in(m), sol_in(s)
|
||||
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
|
||||
|
||||
: n(n_), mesh_in(m), sol_in(s) { }
|
||||
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~ExtrudeCoefficient() { }
|
||||
};
|
||||
|
||||
/// Class used for extruding a vector coefficient
|
||||
class VectorExtrudeCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
int n;
|
||||
Mesh *mesh_in;
|
||||
VectorCoefficient &sol_in;
|
||||
public:
|
||||
/// Constructs an instance of VectorExtrudeCoefficient
|
||||
/**
|
||||
* @param m 1D mesh
|
||||
* @param s 1D vector coefficient
|
||||
* @param n_ number of transverse elements of the extruded mesh
|
||||
*/
|
||||
VectorExtrudeCoefficient(Mesh *m, VectorCoefficient &s, int n_)
|
||||
: VectorCoefficient(s.GetVDim()), n(n_), mesh_in(m), sol_in(s)
|
||||
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
|
||||
|
||||
void Eval(Vector &v, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~VectorExtrudeCoefficient() { }
|
||||
};
|
||||
|
||||
/// Extrude a 1D GridFunction, after extruding the mesh with Extrude1D()
|
||||
/**
|
||||
* @param mesh 1D mesh
|
||||
* @param mesh2d extruded mesh
|
||||
* @param sol grid function
|
||||
* @param ny number of transverse elements of the extruded mesh
|
||||
*/
|
||||
/// Extrude a scalar 1D GridFunction, after extruding the mesh with Extrude1D.
|
||||
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
GridFunction *sol, const int ny);
|
||||
|
||||
|
||||
@@ -197,21 +197,15 @@ static void EAHdivAssemble3D(const int NE,
|
||||
// Assemble (one row per thread)
|
||||
MFEM_FOREACH_THREAD(idx_i, x, NDOF)
|
||||
{
|
||||
// NOTE: due to an llvm backend bug, usage of the modulus operator
|
||||
// has been removed from this foreach section.
|
||||
const int ic = idx_i / NDOF_C;
|
||||
const int idx_ii = idx_i - ic * NDOF_C; // idx_i % NDOF_C
|
||||
const int idx_ii = idx_i % NDOF_C;
|
||||
|
||||
const int nx_i = (ic == 0) ? D1D : D1D-1;
|
||||
const int ny_i = (ic == 1) ? D1D : D1D-1;
|
||||
|
||||
const int qx_i = idx_ii / nx_i;
|
||||
const int ix = idx_ii - qx_i * nx_i; // idx_ii % nx_i
|
||||
|
||||
const int qy_i = qx_i / ny_i;
|
||||
const int iy = qx_i - qy_i * ny_i; // (idx_ii / nx_i) % ny_i
|
||||
|
||||
const int iz = qy_i; // (idx_ii / nx_i) / ny_i
|
||||
const int ix = idx_ii % nx_i;
|
||||
const int iy = (idx_ii / nx_i) % ny_i;
|
||||
const int iz = (idx_ii / nx_i) / ny_i;
|
||||
|
||||
const real_t (&Bi1)[MQ1][MD1] = (ic == 0) ? r_Bc : r_Bo;
|
||||
const real_t (&Bi2)[MQ1][MD1] = (ic == 1) ? r_Bc : r_Bo;
|
||||
@@ -220,18 +214,14 @@ static void EAHdivAssemble3D(const int NE,
|
||||
for (int idx_j = 0; idx_j < NDOF; ++idx_j)
|
||||
{
|
||||
const int jc = idx_j / NDOF_C;
|
||||
const int idx_jj = idx_j - jc * NDOF_C; // idx_j % NDOF_C
|
||||
const int idx_jj = idx_j % NDOF_C;
|
||||
|
||||
const int nx_j = (jc == 0) ? D1D : D1D-1;
|
||||
const int ny_j = (jc == 1) ? D1D : D1D-1;
|
||||
|
||||
const int qx_j = idx_jj / nx_j;
|
||||
const int jx = idx_jj - qx_j * nx_j; // idx_jj % nx_j
|
||||
|
||||
const int qy_j = qx_j / ny_j;
|
||||
const int jy = qx_j - qy_j * ny_j; // (idx_jj / nx_j) % ny_j
|
||||
|
||||
const int jz = qy_j; // (idx_jj / nx_j) / ny_j
|
||||
const int jx = idx_jj % nx_j;
|
||||
const int jy = (idx_jj / nx_j) % ny_j;
|
||||
const int jz = (idx_jj / nx_j) / ny_j;
|
||||
|
||||
const real_t (&Bj1)[MQ1][MD1] = (jc == 0) ? r_Bc : r_Bo;
|
||||
const real_t (&Bj2)[MQ1][MD1] = (jc == 1) ? r_Bc : r_Bo;
|
||||
|
||||
@@ -171,15 +171,15 @@ template<int DIM, int T_SDIM, int T_D1D, int T_Q1D>
|
||||
VectorDiffusionIntegrator::ApplyKernelType
|
||||
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2)
|
||||
if (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
|
||||
}
|
||||
else if constexpr (DIM == 3)
|
||||
else if (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
|
||||
}
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
}
|
||||
|
||||
inline VectorDiffusionIntegrator::ApplyKernelType
|
||||
|
||||
@@ -182,15 +182,15 @@ template<int DIM, int T_D1D, int T_Q1D>
|
||||
VectorMassIntegrator::VectorMassAddMultPAType
|
||||
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2)
|
||||
if (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
|
||||
}
|
||||
else if constexpr (DIM == 3)
|
||||
else if (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
}
|
||||
|
||||
inline VectorMassIntegrator::VectorMassAddMultPAType
|
||||
|
||||
@@ -301,14 +301,18 @@ template <int DIM, int T_D1D, int T_Q1D>
|
||||
DomainLFIntegrator::AssembleKernelType
|
||||
DomainLFIntegrator::AssembleKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return DLFEvalAssemble1D<T_D1D, T_Q1D>; }
|
||||
if constexpr (DIM == 2) { return DLFEvalAssemble2D<T_D1D, T_Q1D>; }
|
||||
if constexpr (DIM == 3) { return DLFEvalAssemble3D<T_D1D, T_Q1D>; }
|
||||
switch (DIM)
|
||||
{
|
||||
case 1:
|
||||
return DLFEvalAssemble1D<T_D1D, T_Q1D>;
|
||||
case 2:
|
||||
return DLFEvalAssemble2D<T_D1D, T_Q1D>;
|
||||
case 3:
|
||||
return DLFEvalAssemble3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_LININTEG_DOMAIN_KERNELS_HPP
|
||||
#endif
|
||||
|
||||
+327
-811
File diff suppressed because it is too large
Load Diff
+27
-30
@@ -125,6 +125,18 @@ 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)
|
||||
{
|
||||
@@ -171,6 +183,14 @@ 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)
|
||||
{
|
||||
@@ -189,37 +209,14 @@ private:
|
||||
AddTetPoints6(off + 6, a, bc, cb, 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)
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
+1
-3
@@ -297,8 +297,7 @@ void LinearForm::Assemble()
|
||||
tr = mesh->GetBdrFaceTransformations(i);
|
||||
if (tr != NULL)
|
||||
{
|
||||
mfem::DofTransformation doftrans;
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (boundary_face_integs_marker[k] &&
|
||||
@@ -308,7 +307,6 @@ void LinearForm::Assemble()
|
||||
boundary_face_integs[k]->
|
||||
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
|
||||
*tr, elemvect);
|
||||
doftrans.TransformDual(elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -158,16 +158,15 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
|
||||
int i;
|
||||
i = dofmap_lor[off_lor + i1 + i2*2];
|
||||
int s1 = i < 0 ? -1 : 1;
|
||||
int idof_lor = vdof_lor[UnsignIndex(i)];
|
||||
int idof_lor = vdof_lor[absdof(i)];
|
||||
i = dofmap_ho[off_ho + i1*n1 + i2*n2];
|
||||
int s2 = i < 0 ? -1 : 1;
|
||||
int idof_ho = vdof_ho[UnsignIndex(i)];
|
||||
int idof_ho = vdof_ho[absdof(i)];
|
||||
int s3 = idof_lor < 0 ? -1 : 1;
|
||||
int s4 = idof_ho < 0 ? -1 : 1;
|
||||
int s = s1*s2*s3*s4;
|
||||
i = UnsignIndex(idof_ho);
|
||||
perm_[UnsignIndex(idof_lor)] = s < 0 ? -1-UnsignIndex(i) :
|
||||
UnsignIndex(i);
|
||||
i = absdof(idof_ho);
|
||||
perm_[absdof(idof_lor)] = s < 0 ? -1-absdof(i) : absdof(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -233,7 +232,7 @@ void LORBase::ConstructDofPermutation() const
|
||||
int j = l_perm[i];
|
||||
int s = j < 0 ? -1 : 1;
|
||||
int t_i = pfes_lor->GetLocalTDofNumber(i);
|
||||
int t_j = pfes_ho->GetLocalTDofNumber(UnsignIndex(j));
|
||||
int t_j = pfes_ho->GetLocalTDofNumber(absdof(j));
|
||||
// Either t_i and t_j both -1, or both non-negative
|
||||
if ((t_i < 0 && t_j >=0) || (t_j < 0 && t_i >= 0))
|
||||
{
|
||||
|
||||
@@ -57,6 +57,8 @@ private:
|
||||
/// values (after temporarily changing them for LOR assembly).
|
||||
void ResetIntegrationRules(GetIntegratorsFn get_integrators);
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
|
||||
protected:
|
||||
enum FESpaceType { H1, ND, RT, L2, INVALID };
|
||||
|
||||
|
||||
+47
-41
@@ -424,7 +424,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -462,7 +462,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -500,7 +500,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
{
|
||||
if (ind[l] < 0)
|
||||
{
|
||||
dofs[l] = m + FlipIndexSign(ind[l]);
|
||||
dofs[l] = m + (-1-ind[l]);
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
@@ -538,16 +538,16 @@ void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
|
||||
{
|
||||
if (dofs[i] < 0)
|
||||
{
|
||||
if (ldof_sign[FlipIndexSign(dofs[i])] < 0)
|
||||
if (ldof_sign[-1-dofs[i]] < 0)
|
||||
{
|
||||
dofs[i] = FlipIndexSign(dofs[i]);
|
||||
dofs[i] = -1-dofs[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ldof_sign[dofs[i]] < 0)
|
||||
{
|
||||
dofs[i] = FlipIndexSign(dofs[i]);
|
||||
dofs[i] = -1-dofs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -699,8 +699,7 @@ void ParFiniteElementSpace::GetSharedEdgeDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = di >= 0 ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,8 +723,7 @@ void ParFiniteElementSpace::GetSharedTriangleDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = di >= 0 ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,8 +747,7 @@ void ParFiniteElementSpace::GetSharedQuadrilateralDofs(
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
const int di = dofs[i];
|
||||
dofs[i] = (di >= 0) ? rdofs[di] :
|
||||
FlipIndexSign(rdofs[FlipIndexSign(di)]);
|
||||
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1490,7 +1487,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = UnsignIndex(ldofs[j]);
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
{
|
||||
@@ -1551,7 +1548,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = UnsignIndex(ldofs[j]);
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
{
|
||||
@@ -1576,15 +1573,14 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
|
||||
for (int i = 0; i < num_ldofs; i++)
|
||||
{
|
||||
int ldof = UnsignIndex(ldofs_fn[i]);
|
||||
int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
|
||||
ldof_marker[ldof] = i;
|
||||
}
|
||||
|
||||
for ( ; j < j_end; j++)
|
||||
{
|
||||
const int ldof = UnsignIndex(send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] :
|
||||
FlipIndexSign(ldof_marker[ldof]));
|
||||
int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1676,7 +1672,12 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
{
|
||||
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
|
||||
{
|
||||
const int ldof = UnsignIndex(face_nbr_ldof.GetJ()[j]);
|
||||
int ldof = face_nbr_ldof.GetJ()[j];
|
||||
if (ldof < 0)
|
||||
{
|
||||
ldof = -1-ldof;
|
||||
}
|
||||
|
||||
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
|
||||
}
|
||||
}
|
||||
@@ -1720,7 +1721,7 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
MFEM_ASSERT(Nonconforming() && i >= pmesh->GetNumFaces(), "");
|
||||
int el1, el2, inf1, inf2;
|
||||
pmesh->GetFaceElements(i, &el1, &el2);
|
||||
el2 = FlipIndexSign(el2);
|
||||
el2 = -1 - el2;
|
||||
pmesh->GetFaceInfos(i, &inf1, &inf2);
|
||||
MFEM_ASSERT(0 <= el2 && el2 < face_nbr_element_dof.Size(), "");
|
||||
const int nd = face_nbr_element_dof.RowSize(el2);
|
||||
@@ -1736,8 +1737,7 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
{
|
||||
const int ldof = vdofs[j];
|
||||
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] :
|
||||
FlipIndexSign(vol_vdofs[FlipIndexSign(ldof)]);
|
||||
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2061,8 +2061,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
|
||||
|
||||
for (int j = 0; j < ne; j++)
|
||||
{
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
|
||||
FlipIndexSign(first + FlipIndexSign(ind[j]));
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
|
||||
/* */ : (-1 - (first + (-1 - ind[j])));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2072,8 +2072,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
|
||||
const int *ind = fec->DofOrderForOrientation(Geometry::SEGMENT, Eo[i]);
|
||||
for (int j = 0; j < ne; j++)
|
||||
{
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
|
||||
FlipIndexSign(first + FlipIndexSign(ind[j]));
|
||||
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
|
||||
/* */ : (-1 - (first + (-1 - ind[j])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2866,7 +2866,7 @@ void NeighborRowMessage::Encode(int rank)
|
||||
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = FlipIndexSign(edof);
|
||||
edof = -1 - edof;
|
||||
s = -1;
|
||||
}
|
||||
|
||||
@@ -3067,10 +3067,10 @@ void NeighborRowMessage::Decode(int rank)
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
real_t s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = UnsignIndex(edof);
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = FlipIndexSign(edof);
|
||||
edof = -1 - edof;
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
@@ -3121,10 +3121,10 @@ void NeighborRowMessage::Decode(int rank)
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = UnsignIndex(edof);
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = FlipIndexSign(edof);
|
||||
edof = -1 - edof;
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
@@ -4405,9 +4405,12 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
{
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
|
||||
const int col = UnsignIndex(DofToVDof(old_dofs[j], vd,
|
||||
old_ndofs));
|
||||
int row = DofToVDof(dofs[j], vd);
|
||||
if (row < 0) { row = -1 - row; }
|
||||
|
||||
int col = DofToVDof(old_dofs[j], vd, old_ndofs);
|
||||
if (col < 0) { col = -1 - col; }
|
||||
|
||||
i_diag[row] = col;
|
||||
}
|
||||
}
|
||||
@@ -4432,7 +4435,9 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
{
|
||||
for (int j = 0; j < dofs.Size(); j++)
|
||||
{
|
||||
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
|
||||
int row = DofToVDof(dofs[j], vd);
|
||||
if (row < 0) { row = -1 - row; }
|
||||
|
||||
if (i_diag[row] == i_diag[row+1]) // diag row empty?
|
||||
{
|
||||
i_offd[row] = old_dofs[j + vd * dofs.Size()];
|
||||
@@ -4541,9 +4546,9 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
const Embedding &emb = dtrans.embeddings[k];
|
||||
|
||||
const int fine_rank = old_ranks[k];
|
||||
const int coarse_rank = (emb.parent < 0) ? FlipIndexSign(emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
int fine_rank = old_ranks[k];
|
||||
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
|
||||
: old_pncmesh->ElementRank(emb.parent);
|
||||
|
||||
if (coarse_rank != MyRank && fine_rank == MyRank)
|
||||
{
|
||||
@@ -4631,8 +4636,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
const int r = DofToVDof(dofs[i], vd);
|
||||
const int m = UnsignIndex(r);
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
@@ -4681,7 +4686,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
{
|
||||
if (!std::isfinite(lR(i, 0))) { continue; }
|
||||
|
||||
const int m = UnsignIndex(DofToVDof(dofs[i], vd));
|
||||
int r = DofToVDof(dofs[i], vd);
|
||||
int m = (r >= 0) ? r : (-1 - r);
|
||||
|
||||
if (is_dg || !mark[m])
|
||||
{
|
||||
|
||||
@@ -1568,39 +1568,6 @@ PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
return plb;
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMinimum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
std::pair<real_t, real_t> minmax =
|
||||
GridFunction::EstimateFunctionMinimum(vdim, plb, max_depth, tol);
|
||||
|
||||
real_t glob_min_lower = minmax.first;
|
||||
real_t glob_min_upper = minmax.second;
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_min_lower, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_min_upper, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
|
||||
|
||||
return std::make_pair(glob_min_lower, glob_min_upper);
|
||||
}
|
||||
|
||||
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMaximum(
|
||||
const int vdim, const PLBound &plb, const int max_depth,
|
||||
const real_t tol) const
|
||||
{
|
||||
std::pair<real_t, real_t> minmax =
|
||||
GridFunction::EstimateFunctionMaximum(vdim, plb, max_depth, tol);
|
||||
|
||||
real_t glob_max_lower = minmax.first;
|
||||
real_t glob_max_upper = minmax.second;
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_max_lower, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE, &glob_max_upper, 1,
|
||||
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
|
||||
return std::make_pair(glob_max_lower, glob_max_upper);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
@@ -609,18 +609,6 @@ public:
|
||||
PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const override;
|
||||
|
||||
/** @brief Estimate the GridFunction minimum across all elements. */
|
||||
std::pair<real_t, real_t> EstimateFunctionMinimum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const override;
|
||||
|
||||
/** @brief Estimate the GridFunction maximum across all elements. */
|
||||
std::pair<real_t, real_t> EstimateFunctionMaximum(const int vdim,
|
||||
const PLBound &plb,
|
||||
const int max_depth,
|
||||
const real_t tol) const override;
|
||||
|
||||
/** Save the local portion of the ParGridFunction. This differs from the
|
||||
serial GridFunction::Save in that it takes into account the signs of
|
||||
the local dofs. */
|
||||
|
||||
+5
-11
@@ -321,17 +321,12 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int threshold = ndofs;
|
||||
const int nsdofs = pfes.GetFaceNbrVSize() / vd;
|
||||
const int nsdofs = pfes.GetFaceNbrVSize();
|
||||
auto d_indices1 = scatter_indices1.Read();
|
||||
auto d_indices2 = scatter_indices2.Read();
|
||||
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs: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_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:nsdofs, t?nsdofs:vd);
|
||||
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
|
||||
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
@@ -351,9 +346,8 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
}
|
||||
else if (idx2>=threshold) // shared boundary
|
||||
{
|
||||
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);
|
||||
d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
|
||||
t?(idx2-threshold):c);
|
||||
}
|
||||
else // true boundary
|
||||
{
|
||||
|
||||
+4
-1
@@ -271,7 +271,10 @@ inline void QuadratureFunction::GetValues(
|
||||
const int s_offset = qspace->Offset(idx);
|
||||
const int sl_size = qspace->Offset(idx + 1) - s_offset;
|
||||
// Make the values matrix memory an alias of the quadrature function memory
|
||||
values.MakeRef(GetMemory(), vdim*s_offset, vdim, sl_size);
|
||||
Memory<real_t> &values_mem = values.GetMemory();
|
||||
values_mem.Delete();
|
||||
values_mem.MakeAlias(GetMemory(), vdim*s_offset, vdim*sl_size);
|
||||
values.SetSize(vdim, sl_size);
|
||||
}
|
||||
|
||||
inline void QuadratureFunction::GetValues(
|
||||
|
||||
+9
-8
@@ -334,16 +334,17 @@ template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1)
|
||||
if (DIM == 1)
|
||||
{
|
||||
if constexpr (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if constexpr (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
|
||||
else if constexpr (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
|
||||
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
|
||||
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
else if constexpr (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -203,10 +203,10 @@ template<int DIM, QVectorLayout Q_LAYOUT,
|
||||
QuadratureInterpolator::TensorEvalKernelType
|
||||
QuadratureInterpolator::TensorEvalKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -453,15 +453,8 @@ QuadratureInterpolator::TensorEvalHDivKernels::Kernel()
|
||||
{
|
||||
using namespace internal::quadrature_interpolator;
|
||||
static_assert(DIM == 2 || DIM == 3, "only DIM=2 and DIM=3 are implemented!");
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
MFEM_ABORT("only DIM=2 and DIM=3 are implemented!");
|
||||
if (DIM == 2) { return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>; }
|
||||
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -592,10 +592,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
QuadratureInterpolator::GradKernelType
|
||||
QuadratureInterpolator::GradKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
|
||||
MFEM_ABORT("");
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
@@ -603,10 +603,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
QuadratureInterpolator::CollocatedGradKernelType
|
||||
QuadratureInterpolator::CollocatedGradKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
MFEM_ABORT("");
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -752,10 +752,10 @@ template <int DIM, int VDIM, int ND, int NQ>
|
||||
EvalKernel QuadratureInterpolator::EvalKernels::Kernel()
|
||||
{
|
||||
using namespace internal::quadrature_interpolator;
|
||||
if constexpr (DIM == 1) { return Eval1D; }
|
||||
else if constexpr (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
|
||||
else if constexpr (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
|
||||
MFEM_ABORT("");
|
||||
if (DIM == 1) { return Eval1D; }
|
||||
else if (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
|
||||
else if (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template <int DIM>
|
||||
|
||||
+12
-7
@@ -844,6 +844,8 @@ void ConformingFaceRestriction::ComputeGatherIndices(
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
|
||||
|
||||
void ConformingFaceRestriction::SetFaceDofsScatterIndices(
|
||||
const Mesh::FaceInformation &face,
|
||||
const int face_index,
|
||||
@@ -866,9 +868,9 @@ void ConformingFaceRestriction::SetFaceDofsScatterIndices(
|
||||
{
|
||||
const int lex_volume_dof = face_map[face_dof];
|
||||
const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof]; // signed
|
||||
const int volume_dof = UnsignIndex(s_volume_dof);
|
||||
const int volume_dof = absdof(s_volume_dof);
|
||||
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
|
||||
const int global_dof = UnsignIndex(s_global_dof);
|
||||
const int global_dof = absdof(s_global_dof);
|
||||
const int restriction_dof = face_dofs*face_index + face_dof;
|
||||
scatter_indices[restriction_dof] = s_global_dof;
|
||||
++gather_offsets[global_dof + 1];
|
||||
@@ -895,10 +897,10 @@ void ConformingFaceRestriction::SetFaceDofsGatherIndices(
|
||||
{
|
||||
const int lex_volume_dof = face_map[face_dof];
|
||||
const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof];
|
||||
const int volume_dof = UnsignIndex(s_volume_dof);
|
||||
const int volume_dof = absdof(s_volume_dof);
|
||||
const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
|
||||
const int sgn = (s_global_dof >= 0) ? 1 : -1;
|
||||
const int global_dof = UnsignIndex(s_global_dof);
|
||||
const int global_dof = absdof(s_global_dof);
|
||||
const int restriction_dof = face_dofs*face_index + face_dof;
|
||||
const int s_restriction_dof = (sgn >= 0) ? restriction_dof : -1 -
|
||||
restriction_dof;
|
||||
@@ -1398,17 +1400,20 @@ 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];
|
||||
// Encode the volume DOF index and element index
|
||||
const int global_dof_elem2 = elem_index*elem_dofs + volume_dof_elem2;
|
||||
const int global_dof_elem2 = face_nbr_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
|
||||
}
|
||||
|
||||
+2
-5
@@ -4102,11 +4102,8 @@ void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &d_loc,
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Don't count the overlapping DOFs in parallel.
|
||||
// The pfes might be ordered byVDIM, while the loop goes consecutively.
|
||||
if (parallel)
|
||||
{
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
}
|
||||
const int dof_i = pfes->DofToVDof(i, 0);
|
||||
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
|
||||
#endif
|
||||
|
||||
dof_cnt++;
|
||||
|
||||
+1
-24
@@ -114,22 +114,10 @@ public:
|
||||
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
|
||||
|
||||
/// Move assignment operator
|
||||
/** If *this is a non-owning view (e.g., from MakeRef()), the data is copied
|
||||
so that the base is also modified. */
|
||||
Array<T> &operator=(Array<T> &&src)
|
||||
{
|
||||
if (this == &src) { return *this; }
|
||||
// If *this is a non-owning view (alias), and its capacity is sufficient
|
||||
// to contain src, then copy into *this so that the alias's base memory is
|
||||
// modified.
|
||||
if (!OwnsData() && Capacity() >= src.Size())
|
||||
{
|
||||
*this = src; // Copy assignment.
|
||||
}
|
||||
else
|
||||
{
|
||||
Swap(src); // Swap the pointers only.
|
||||
}
|
||||
Swap(src); // Swap does not use move assignment!
|
||||
src.DeleteAll();
|
||||
return *this;
|
||||
}
|
||||
@@ -263,9 +251,6 @@ public:
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
|
||||
/// Make this Array a reference to the given sub-Memory of @a base.
|
||||
inline void MakeRef(Memory<T> &base, int offset, int size_);
|
||||
|
||||
/// Reset the Array to use the given external Memory @a mem and size @a s.
|
||||
/** If @a own_mem is false, the Array will not own any of the pointers of
|
||||
@a mem.
|
||||
@@ -1088,14 +1073,6 @@ inline void Array<T>::MakeRef(const Array &master)
|
||||
data.MakeAlias(master.GetMemory(), 0, size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(Memory<T> &base, int offset, int size_)
|
||||
{
|
||||
data.Delete();
|
||||
size = size_;
|
||||
data.MakeAlias(base, offset, size_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::NewMemoryAndSize(
|
||||
const Memory<T> &mem, int s, bool own_mem)
|
||||
|
||||
@@ -278,18 +278,9 @@ void ArraysByName<T>::Load(std::istream &in)
|
||||
q1 = ArrayLine.find(' ');
|
||||
ArrayName = ArrayLine.substr(0,q1-1);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// Ignore the remainder of the line which may contain explanatory comments
|
||||
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)
|
||||
{
|
||||
const unsigned b = static_cast<unsigned char>(prop.uuid.bytes[i]);
|
||||
res << std::setfill('0') << std::setw(2) << std::hex << b;
|
||||
res << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<unsigned>(prop.uuid.bytes[i]);
|
||||
}
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
hipUUID uuid;
|
||||
MFEM_GPU_CHECK(hipDeviceGetUuid(&uuid, device_id));
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
const unsigned b = static_cast<unsigned char>(uuid.bytes[i]);
|
||||
res << std::setfill('0') << std::setw(2) << std::hex << b;
|
||||
res << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<unsigned>(uuid.bytes[i]);
|
||||
}
|
||||
#endif
|
||||
return res.str();
|
||||
|
||||
@@ -113,10 +113,6 @@ void SetGlobalMPI_Comm(MPI_Comm comm);
|
||||
/// to suppress the warning.
|
||||
const char* GetEnv(const char* name);
|
||||
|
||||
/// Signed indices i -> -1 - i are used as a convention to encode orientation.
|
||||
inline MFEM_HOST_DEVICE int FlipIndexSign(int i) { return -1 - i; }
|
||||
inline MFEM_HOST_DEVICE int UnsignIndex(int i) { return i < 0 ? -1 - i : i; }
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -759,9 +759,7 @@ private:
|
||||
{
|
||||
switch (mt)
|
||||
{
|
||||
case MT::HOST_DEBUG:
|
||||
if (GetEnv("MFEM_MMU_STD")) { return new StdHostMemorySpace(); }
|
||||
return new MmuHostMemorySpace();
|
||||
case MT::HOST_DEBUG: return new MmuHostMemorySpace();
|
||||
#ifdef MFEM_USE_UMPIRE
|
||||
case MT::HOST_UMPIRE:
|
||||
return new UmpireHostMemorySpace(
|
||||
@@ -790,9 +788,7 @@ private:
|
||||
case MT::DEVICE_UMPIRE: return new NoDeviceMemorySpace();
|
||||
case MT::DEVICE_UMPIRE_2: return new NoDeviceMemorySpace();
|
||||
#endif
|
||||
case MT::DEVICE_DEBUG:
|
||||
if (GetEnv("MFEM_MMU_STD")) { return new StdDeviceMemorySpace(); }
|
||||
return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE_DEBUG: return new MmuDeviceMemorySpace();
|
||||
case MT::DEVICE:
|
||||
{
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
|
||||
@@ -55,6 +55,7 @@ list(APPEND HDRS
|
||||
dinvariants.hpp
|
||||
dtensor.hpp
|
||||
dual.hpp
|
||||
eigensolver.hpp
|
||||
filteredsolver.hpp
|
||||
handle.hpp
|
||||
invariants.hpp
|
||||
|
||||
@@ -82,15 +82,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the DenseMatrix to reference the given sub-Memory of @a base.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the @a base Memory. */
|
||||
void MakeRef(Memory<real_t> &base, int offset, int h, int w)
|
||||
{
|
||||
data.MakeRef(base, offset, h*w);
|
||||
height = h; width = w;
|
||||
}
|
||||
|
||||
/// Change the data array and the size of the DenseMatrix.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the data array @a d. */
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @file eigensolver.hpp
|
||||
*
|
||||
* @brief This file contains a common interface for all eigensolver classes
|
||||
*/
|
||||
|
||||
#ifndef MFEM_EIGENSOLVER
|
||||
#define MFEM_EIGENSOLVER
|
||||
|
||||
#ifdef MFEM_HYPRE
|
||||
#include "hypre.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_SLEPC
|
||||
#include "slepc.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
enum class EigenSolverType
|
||||
{
|
||||
HYPRE,
|
||||
SLEPC,
|
||||
INVALID_TYPE
|
||||
};
|
||||
|
||||
/// Provides base class for MFEM Eigensolvers
|
||||
class EigenSolverBase
|
||||
{
|
||||
public:
|
||||
EigenSolverBase() {}
|
||||
|
||||
/// Destructor
|
||||
virtual ~EigenSolverBase() = default;
|
||||
|
||||
/// Solves the eigenvalue problem
|
||||
virtual void Solve() = 0;
|
||||
|
||||
/// Set the required number of modes
|
||||
virtual void SetNumModes(int num_Modes)
|
||||
{
|
||||
numModes=num_Modes;
|
||||
}
|
||||
|
||||
/// @brief Set the operator to the eigenvalue problem
|
||||
/// @param A - operator
|
||||
virtual void SetOperator(Operator& A) = 0;
|
||||
|
||||
/// @brief Sets operators for the generalized eigenvalue problem
|
||||
/// @param A - operator
|
||||
/// @param M - mass matrix
|
||||
virtual void SetOperator(Operator& A, Operator& M)
|
||||
{
|
||||
MFEM_ABORT("Generalized eigensolver is not supported!");
|
||||
}
|
||||
|
||||
/// Optional method - sets preconditioner for the
|
||||
/// eigenvalue solver.
|
||||
virtual void SetPreconditioner(Solver& precond)
|
||||
{
|
||||
MFEM_ABORT("Preconditioner is not supported!");
|
||||
}
|
||||
|
||||
/// Returns the converged eigenvalues
|
||||
virtual void GetEigenvalues(Array<real_t>& eigen_vals) = 0;
|
||||
|
||||
/// Returns the vec_index eigenvector.
|
||||
virtual void GetEigenvector(int vec_index, Vector& vector) = 0;
|
||||
|
||||
/// Returns the eigensolver type.
|
||||
EigenSolverType GetSolverType() { return eigSolverType; }
|
||||
|
||||
protected:
|
||||
int numModes = 0;
|
||||
EigenSolverType eigSolverType = EigenSolverType::INVALID_TYPE;
|
||||
};
|
||||
|
||||
#ifdef MFEM_HYPRE
|
||||
class EigenSolverHypreLOBPCG : public EigenSolverBase
|
||||
{
|
||||
public:
|
||||
EigenSolverHypreLOBPCG(MPI_Comm comm)
|
||||
{
|
||||
eigenSolver = std::make_unique<HypreLOBPCG>(comm);
|
||||
eigSolverType = EigenSolverType::HYPRE;
|
||||
}
|
||||
|
||||
~EigenSolverHypreLOBPCG() {}
|
||||
|
||||
void Solve() override { eigenSolver->Solve(); }
|
||||
void SetNumModes(int num_Modes) override
|
||||
{
|
||||
eigenSolver->SetNumModes(num_Modes);
|
||||
numModes = num_Modes;
|
||||
}
|
||||
|
||||
void SetOperator(Operator& A) override { eigenSolver->SetOperator(A); }
|
||||
|
||||
void SetOperator(Operator& A, Operator& M) override
|
||||
{
|
||||
eigenSolver->SetOperator(A);
|
||||
eigenSolver->SetMassMatrix(M);
|
||||
}
|
||||
|
||||
void SetPreconditioner(Solver& precond) override { eigenSolver->SetPreconditioner(precond); }
|
||||
void GetEigenvalues(Array<real_t>& eigen_vals) override { eigenSolver->GetEigenvalues(eigen_vals); }
|
||||
void GetEigenvector(int vec_index, Vector& vector) override
|
||||
{
|
||||
const HypreParVector& eigenvec = eigenSolver->GetEigenvector(vec_index);
|
||||
vector = eigenvec;
|
||||
}
|
||||
|
||||
void SetTol(real_t tol) { eigenSolver->SetTol(tol); }
|
||||
void SetRelTol(real_t rel_tol) { eigenSolver->SetRelTol(rel_tol); }
|
||||
void SetMaxIter(int max_iter) { eigenSolver->SetMaxIter(max_iter); }
|
||||
void SetPrintLevel(int logging) { eigenSolver->SetPrintLevel(logging); }
|
||||
void SetRandomSeed(int seed) { eigenSolver->SetRandomSeed(seed); }
|
||||
void SetPrecondUsageMode(int usage_mode) { eigenSolver->SetPrecondUsageMode(usage_mode); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<HypreLOBPCG> eigenSolver = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_SLEPC
|
||||
class EigenSolverSlepc : public EigenSolverBase
|
||||
{
|
||||
public:
|
||||
EigenSolverSlepc(MPI_Comm comm)
|
||||
{
|
||||
eigSolverType = EigenSolverType::SLEPC;
|
||||
eigenSolver = std::make_unique<SlepcEigenSolver>(comm);
|
||||
|
||||
eigenSolver->SetWhichEigenpairs(SlepcEigenSolver::TARGET_REAL);
|
||||
eigenSolver->SetTarget(0.0);
|
||||
eigenSolver->SetSpectralTransformation(SlepcEigenSolver::SHIFT_INVERT);
|
||||
}
|
||||
|
||||
~EigenSolverSlepc() {}
|
||||
|
||||
void Solve() override { eigenSolver->Solve(); }
|
||||
void SetNumModes(int num_Modes) override
|
||||
{
|
||||
eigenSolver->SetNumModes(num_Modes);
|
||||
numModes = num_Modes;
|
||||
}
|
||||
/// @brief Set the operator to the slepc eigenvalue problem. This method deep copies data to create a PetscParMatrix
|
||||
/// @param A - operator, must be of type HypreParMatrix.
|
||||
void SetOperator(Operator& A) override
|
||||
{
|
||||
petscMatA = std::make_unique<PetscParMatrix>
|
||||
(dynamic_cast<HypreParMatrix*>(&A));
|
||||
eigenSolver->SetOperator(*petscMatA);
|
||||
}
|
||||
/// @brief Set the operators to the slepc eigenvalue problem. This method deep copies data to create a PetscParMatrix
|
||||
/// @param A - operator, must be of type HypreParMatrix.
|
||||
/// @param M - operator, must be of type HypreParMatrix.
|
||||
void SetOperator(Operator& A, Operator& M) override
|
||||
{
|
||||
petscMatA = std::make_unique<PetscParMatrix>
|
||||
(dynamic_cast<const HypreParMatrix*>(&A));
|
||||
petscMatM = std::make_unique<PetscParMatrix>
|
||||
(dynamic_cast<const HypreParMatrix*>(&M));
|
||||
|
||||
eigenSolver->SetOperators(*petscMatA, *petscMatM);
|
||||
}
|
||||
void SetPreconditioner([[maybe_unused]] Solver& precond) override {}
|
||||
void GetEigenvalues(Array<real_t>& eigen_vals) override
|
||||
{
|
||||
eigen_vals.SetSize(numModes);
|
||||
for (int ik = 0; ik < numModes; ik++)
|
||||
{
|
||||
eigenSolver->GetEigenvalue(static_cast<unsigned int>(ik), eigen_vals[ik]);
|
||||
}
|
||||
}
|
||||
void GetEigenvector( int vec_index, Vector& vector) override
|
||||
{ eigenSolver->GetEigenvector(vec_index, vector); }
|
||||
|
||||
void SetTol(real_t tol) { eigenSolver->SetTol(tol); }
|
||||
void SetMaxIter(int max_iter) { eigenSolver->SetMaxIter(max_iter); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<SlepcEigenSolver> eigenSolver = nullptr;
|
||||
std::unique_ptr<PetscParMatrix> petscMatA = nullptr;
|
||||
std::unique_ptr<PetscParMatrix> petscMatM = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
@@ -317,9 +317,6 @@ 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();
|
||||
|
||||
+75
-41
@@ -38,13 +38,6 @@
|
||||
#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>
|
||||
@@ -84,17 +77,13 @@ 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)
|
||||
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**);
|
||||
static PetscErrorCode __mfem_array_container_destroy(void*);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void *);
|
||||
#else
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt);
|
||||
static PetscErrorCode __mfem_array_container_destroy(void**);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void**);
|
||||
#endif
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void**);
|
||||
|
||||
// auxiliary functions
|
||||
static PetscErrorCode Convert_Array_IS(MPI_Comm,bool,const mfem::Array<int>*,
|
||||
@@ -1328,7 +1317,11 @@ 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);
|
||||
@@ -1655,7 +1648,11 @@ 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);
|
||||
@@ -1751,7 +1748,11 @@ 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);
|
||||
@@ -2197,7 +2198,11 @@ 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);
|
||||
@@ -2480,6 +2485,7 @@ 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));
|
||||
|
||||
@@ -2492,6 +2498,7 @@ 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)
|
||||
{
|
||||
@@ -2500,29 +2507,29 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
if (plev == 1)
|
||||
{
|
||||
#if PETSC_VERSION_LT(3,15,0)
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorDefault,vf,
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorDefault,vf,
|
||||
#else
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorResidual,vf,
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorResidual,vf,
|
||||
#endif
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
}
|
||||
else if (plev > 1)
|
||||
{
|
||||
ierr = KSPSetComputeSingularValues(ksp,PETSC_TRUE); PCHKERRQ(ksp,ierr);
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorSingularValue,vf,
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorSingularValue,vf,
|
||||
(myPetscFunc)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,(KSPMonitorFn *)KSPMonitorTrueResidualNorm,vf,
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidualNorm,vf,
|
||||
#else
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidual,vf,
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidual,vf,
|
||||
#endif
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
}
|
||||
}
|
||||
@@ -2538,7 +2545,7 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
if (plev > 0)
|
||||
{
|
||||
ierr = SNESMonitorSet(snes,(myMonitor)SNESMonitorDefault,vf,
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(snes,ierr);
|
||||
}
|
||||
}
|
||||
@@ -5322,27 +5329,21 @@ static PetscErrorCode __mfem_pc_shell_destroy(PC pc)
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt ptr)
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(void *ptr)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscFree(ptr); CHKERRQ(ierr);
|
||||
#else
|
||||
ierr = PetscFree(*(void**)ptr); CHKERRQ(ierr);
|
||||
#endif
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt ptr)
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
|
||||
{
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
mfem::Array<Mat> *a = (mfem::Array<Mat>*)ptr;
|
||||
#else
|
||||
mfem::Array<Mat> *a = *(mfem::Array<Mat>**)ptr;
|
||||
#endif
|
||||
PetscErrorCode ierr;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
for (int i=0; i<a->Size(); i++)
|
||||
@@ -5355,16 +5356,41 @@ static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt ptr)
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
|
||||
#else
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt ctx)
|
||||
#endif
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(void **ptr)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(*(void**)ctx); CHKERRQ(ierr);
|
||||
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);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -5609,7 +5635,11 @@ 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);
|
||||
@@ -5703,7 +5733,11 @@ 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 plasma
|
||||
fluids/navier fluids/schrodinger-flow
|
||||
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 plasma)
|
||||
toys shifted dpg diag-smoothers fluids/navier)
|
||||
|
||||
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
|
||||
|
||||
|
||||
+52
-58
@@ -493,7 +493,8 @@ void Mesh::GetBdrElementTransformation(int i,
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
pm(k,j) = nodes(UnsignIndex(vdofs[n*k+j]));
|
||||
int idx = vdofs[n*k+j];
|
||||
pm(k,j) = nodes((idx<0)? -1-idx:idx);
|
||||
}
|
||||
}
|
||||
ElTr->SetFE(bdr_el);
|
||||
@@ -1355,7 +1356,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[0].conformity = ElementConformity::Coincident;
|
||||
face.element[1].conformity = ElementConformity::Coincident;
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].orientation = inf2%64;
|
||||
}
|
||||
}
|
||||
@@ -1378,7 +1379,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[0].conformity = ElementConformity::Coincident;
|
||||
face.element[1].conformity = ElementConformity::Superset;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].orientation = inf2%64;
|
||||
}
|
||||
face.point_matrix = nc_faces_info[ncface].PointMatrix;
|
||||
@@ -1404,7 +1405,7 @@ Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
face.element[1].location = ElementLocation::FaceNbr;
|
||||
face.element[0].conformity = ElementConformity::Superset;
|
||||
face.element[1].conformity = ElementConformity::Coincident;
|
||||
face.element[1].index = FlipIndexSign(e2);
|
||||
face.element[1].index = -1 - e2;
|
||||
face.element[1].orientation = inf2%64;
|
||||
face.point_matrix = nc_faces_info[ncface].PointMatrix;
|
||||
}
|
||||
@@ -1437,7 +1438,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::SharedConforming:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -1447,7 +1448,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::SharedSlaveNonconforming:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -1455,7 +1456,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
break;
|
||||
case FaceInfoTag::GhostSlave:
|
||||
res.Elem1No = element[0].index;
|
||||
res.Elem2No = FlipIndexSign(element[1].index);
|
||||
res.Elem2No = -1 - element[1].index;
|
||||
res.Elem1Inf = element[0].orientation + element[0].local_face_id*64;
|
||||
res.Elem2Inf = element[1].orientation + element[1].local_face_id*64;
|
||||
break;
|
||||
@@ -6513,7 +6514,7 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
|
||||
input >> edge_to_ukv[j] >> v[0] >> v[1];
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
edge_to_ukv[j] = FlipIndexSign(edge_to_ukv[j]);
|
||||
edge_to_ukv[j] = -1 - edge_to_ukv[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6550,6 +6551,9 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
const int NP = NumOfElements; // number of patches
|
||||
const int NPKV = NP * dim; // number of patch knotvectors
|
||||
constexpr int notset = -9999999;
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
auto unSign = [](int i) { return (i < 0) ? -1 - i : i; };
|
||||
// Local edge index -> dimension convention
|
||||
auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
|
||||
|
||||
@@ -6565,7 +6569,7 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
{
|
||||
GetElementVertices(i, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : FlipIndexSign(i);
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : flipSign(i);
|
||||
ukv_to_rpkv[i] = i;
|
||||
}
|
||||
return;
|
||||
@@ -6615,14 +6619,14 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
// We've set this edge already - link this index to it
|
||||
if (edge_to_pkv[edge] != notset)
|
||||
{
|
||||
const int pkv_other = UnsignIndex(edge_to_pkv[edge]);
|
||||
const int pkv_other = unSign(edge_to_pkv[edge]);
|
||||
unite(pkv, pkv_other);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEdgeVertices(edge, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : FlipIndexSign(pkv);
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : flipSign(pkv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6649,10 +6653,10 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
edge_to_ukv.SetSize(NumOfEdges);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
const int pkv = UnsignIndex(edge_to_pkv[i]);
|
||||
const int pkv = unSign(edge_to_pkv[i]);
|
||||
const int rpkv = pkv_to_rpkv[pkv];
|
||||
const int ukv = rpkv_to_ukv[rpkv];
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? FlipIndexSign(ukv) : ukv;
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? flipSign(ukv) : ukv;
|
||||
}
|
||||
|
||||
CorrectPatchTopoOrientations(edge_to_ukv);
|
||||
@@ -6663,6 +6667,9 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
const int dim = Dimension(); // Topological (not physical) dimension
|
||||
if (dim == 1) { return; }
|
||||
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
|
||||
const Table *face2elem = GetFaceToElementTable();
|
||||
Array<int> pfaces, orient;
|
||||
Array<int> fe, feo;
|
||||
@@ -6681,7 +6688,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (auto e : fe)
|
||||
{
|
||||
const int skv = edge_to_ukv[e];
|
||||
if (skv == kv || FlipIndexSign(skv) == kv) { hasKV = true; }
|
||||
if (skv == kv || flipSign(skv) == kv) { hasKV = true; }
|
||||
}
|
||||
if (hasKV)
|
||||
{
|
||||
@@ -6711,7 +6718,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
};
|
||||
}
|
||||
|
||||
Array<int> ukvs((dim == 2) ? 4 : 12);
|
||||
Array<int> ukvs((dim==2) ? 4 : 12);
|
||||
Array<int> pe, oe;
|
||||
bool initKV = false;
|
||||
|
||||
@@ -6725,7 +6732,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (int i = 0; i < pe.Size(); i++)
|
||||
{
|
||||
ukvs[i] = edge_to_ukv[pe[i]];
|
||||
ukvs[i] = (oe[i] < 0) ? FlipIndexSign(ukvs[i]) : ukvs[i];
|
||||
ukvs[i] = (oe[i] < 0) ? flipSign(ukvs[i]) : ukvs[i];
|
||||
}
|
||||
|
||||
// Find the direction with this kv.
|
||||
@@ -6733,19 +6740,12 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
for (int d=0; d<dim; ++d) // Loop over directions.
|
||||
{
|
||||
const int skv = edge_to_ukv[pe[dir_edges[d][0]]];
|
||||
if (skv == kv || FlipIndexSign(skv) == kv)
|
||||
if (skv == kv || flipSign(skv) == kv)
|
||||
{
|
||||
for (auto e : dir_edges[d])
|
||||
if (!edgeSet[pe[e]])
|
||||
{
|
||||
thisDir = d;
|
||||
}
|
||||
thisDir = d;
|
||||
}
|
||||
}
|
||||
if (thisDir == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
MFEM_VERIFY(thisDir >= 0, "");
|
||||
|
||||
// For this direction, find any edge already set. If no edge is set, we
|
||||
// arbitrarily take the first.
|
||||
@@ -6777,12 +6777,12 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
}
|
||||
|
||||
const int edge = pe[i];
|
||||
if ((dim == 2 && ukvs[i] != FlipIndexSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == FlipIndexSign(ukvs[ref_edge0])))
|
||||
if ((dim == 2 && ukvs[i] != flipSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == flipSign(ukvs[ref_edge0])))
|
||||
{
|
||||
// Flip the sign of this edge
|
||||
MFEM_ASSERT(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = FlipIndexSign(edge_to_ukv[edge]);
|
||||
MFEM_VERIFY(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = flipSign(edge_to_ukv[edge]);
|
||||
}
|
||||
|
||||
edgeSet[edge] = true;
|
||||
@@ -6827,11 +6827,10 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
int unsetDim = -1;
|
||||
for (int d=0; d<dim; ++d) // Loop over dimensions.
|
||||
{
|
||||
for (auto e : dir_edges[d])
|
||||
if (!edgeSet[pe[e]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
if (!edgeSet[pe[dir_edges[d][0]]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (unsetDim == -1)
|
||||
@@ -6840,7 +6839,9 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
continue;
|
||||
}
|
||||
|
||||
const int kv = UnsignIndex(edge_to_ukv[pe[dir_edges[unsetDim][0]]]);
|
||||
const int kv_signed = edge_to_ukv[pe[dir_edges[unsetDim][0]]];
|
||||
const int kv = kv_signed < 0 ? flipSign(kv_signed) : kv_signed;
|
||||
MFEM_VERIFY(!edgeSet[pe[dir_edges[unsetDim][0]]], "");
|
||||
|
||||
initKV = false;
|
||||
|
||||
@@ -6890,7 +6891,6 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
bool allSet = true;
|
||||
for (auto eset : edgeSet)
|
||||
{
|
||||
@@ -6899,8 +6899,7 @@ void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
allSet = false;
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(allSet && unset.size() == 0, "Some edge is not set");
|
||||
#endif
|
||||
MFEM_VERIFY(allSet && unset.size() == 0, "Some edge is not set");
|
||||
|
||||
delete face2elem;
|
||||
}
|
||||
@@ -6942,7 +6941,7 @@ void Mesh::LoadNonconformingPatchTopo(std::istream &input,
|
||||
|
||||
if (v[0] > v[1])
|
||||
{
|
||||
ukv = FlipIndexSign(ukv);
|
||||
ukv = -1 - ukv;
|
||||
}
|
||||
edge_to_ukv[j] = ukv;
|
||||
}
|
||||
@@ -12439,7 +12438,11 @@ void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
edge_vertex->GetRow(i, vert);
|
||||
const int ki = UnsignIndex(e_to_k[i]);
|
||||
int ki = e_to_k[i];
|
||||
if (ki < 0)
|
||||
{
|
||||
ki = -1 - ki;
|
||||
}
|
||||
|
||||
if (vmap)
|
||||
{
|
||||
@@ -15748,18 +15751,9 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
{
|
||||
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
|
||||
|
||||
// Closest int divisor to the cubit root, going down.
|
||||
auto factor3 = [](int N)
|
||||
auto factor = [&](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(round(cbrt(N))); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
|
||||
// Closest int divisor to the square root, going down.
|
||||
auto factor2 = [](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(round(sqrt(N))); i > 0; i--)
|
||||
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
@@ -15783,22 +15777,22 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
int el0_x, el0_y, el0_z;
|
||||
if (dim == 2)
|
||||
{
|
||||
mpi_x = factor2(mpi_cnt);
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_y = mpi_cnt / mpi_x;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_y = factor2(el0);
|
||||
el0_y = factor(el0);
|
||||
el0_x = el0 / el0_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpi_x = factor3(mpi_cnt);
|
||||
mpi_y = factor2(mpi_cnt / mpi_x);
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_y = factor(mpi_cnt / mpi_x);
|
||||
mpi_z = mpi_cnt / mpi_x / mpi_y;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_z = factor3(el0);
|
||||
el0_y = factor2(el0 / el0_z);
|
||||
el0_z = factor(el0);
|
||||
el0_y = factor(el0 / el0_z);
|
||||
el0_x = el0 / el0_y / el0_z;
|
||||
}
|
||||
|
||||
|
||||
@@ -3206,22 +3206,10 @@ 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
|
||||
|
||||
@@ -143,7 +143,7 @@ int ThresholdRefiner::ApplyImpl(Mesh &mesh)
|
||||
if (num_marked_elements == 0LL) { return STOP; }
|
||||
|
||||
mesh.GeneralRefinement(marked_elements, non_conforming, nc_limit);
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
|
||||
return CONTINUE + REFINED;
|
||||
}
|
||||
|
||||
void ThresholdRefiner::Reset()
|
||||
@@ -162,7 +162,7 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh)
|
||||
const Vector &local_err = estimator.GetLocalErrors();
|
||||
bool derefs = mesh.DerefineByError(local_err, threshold, nc_limit, op);
|
||||
|
||||
return derefs ? static_cast<int>(CONTINUE) + static_cast<int>(DEREFINED) : NONE;
|
||||
return derefs ? CONTINUE + DEREFINED : NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it)
|
||||
}
|
||||
delete l2fes;
|
||||
delete gf;
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REFINED);
|
||||
return CONTINUE + REFINED;
|
||||
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ int Rebalancer::ApplyImpl(Mesh &mesh)
|
||||
if (pmesh && pmesh->Nonconforming())
|
||||
{
|
||||
pmesh->Rebalance();
|
||||
return static_cast<int>(CONTINUE) + static_cast<int>(REBALANCED);
|
||||
return CONTINUE + REBALANCED;
|
||||
}
|
||||
#endif
|
||||
return NONE;
|
||||
|
||||
@@ -1516,15 +1516,12 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
|
||||
void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
{
|
||||
string buff;
|
||||
string version;
|
||||
real_t version;
|
||||
int binary, dsize;
|
||||
input >> version >> binary >> dsize;
|
||||
if (version != "2.2")
|
||||
if (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");
|
||||
MFEM_ABORT("Gmsh file version < 2.2");
|
||||
}
|
||||
if (dsize != sizeof(double))
|
||||
{
|
||||
|
||||
+2
-2
@@ -3542,7 +3542,7 @@ void NCMesh::TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
|
||||
|
||||
// create a slave face record with a degenerate point matrix
|
||||
face_list.slaves.Append(
|
||||
Slave(FlipIndexSign(enode.edge_index),
|
||||
Slave(-1 - enode.edge_index,
|
||||
eid[0].element, eid[0].local, Geometry::SQUARE));
|
||||
Slave &sl = face_list.slaves.Last();
|
||||
|
||||
@@ -3589,7 +3589,7 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
|
||||
// non-slave edge is really a (face-)slave itself.
|
||||
const MeshId &eid = *eid_and_type.id;
|
||||
face_list.slaves.Append(
|
||||
Slave(FlipIndexSign(eid.index), eid.element, eid.local, Geometry::TRIANGLE));
|
||||
Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
|
||||
|
||||
int v0index = nodes[vn0].vert_index;
|
||||
int v1index = nodes[vn1].vert_index;
|
||||
|
||||
+44
-40
@@ -93,7 +93,7 @@ void NCNURBSExtension::GetMasterEdgeEntities(
|
||||
}
|
||||
else
|
||||
{
|
||||
const int auxEdge = FlipIndexSign(edge_i);
|
||||
const int auxEdge = -1 - edge_i;
|
||||
GetAuxEdgeVertices(auxEdge, sverts);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
{
|
||||
if (edge < 0)
|
||||
{
|
||||
sideAuxEdges[s].Append(FlipIndexSign(edge));
|
||||
sideAuxEdges[s].Append(-1 - edge);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,7 +456,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
== afverts[j], "");
|
||||
}
|
||||
|
||||
ori_f2 = FlipIndexSign(ori_f2);
|
||||
ori_f2 = -1 - ori_f2;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -468,7 +468,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
}
|
||||
|
||||
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
|
||||
SlaveFaceInfo{FlipIndexSign(afid), ori_f2,
|
||||
SlaveFaceInfo{-1 - afid, ori_f2,
|
||||
{fki(vMinID,0), fki(vMinID,1)},
|
||||
{
|
||||
fki((vMinID + 2) % 4,0) - fki(vMinID,0),
|
||||
@@ -509,7 +509,7 @@ void NCNURBSExtension::FindAdditionalFacesSA(
|
||||
auxFaces.push_back(auxFace);
|
||||
|
||||
facePairs.emplace_back(FacePairInfo{fverts[vMinID], f,
|
||||
SlaveFaceInfo{FlipIndexSign(auxFaceId), ori_f,
|
||||
SlaveFaceInfo{-1 - auxFaceId, ori_f,
|
||||
{fki(vMinID,0), fki(vMinID,1)},
|
||||
{
|
||||
fki((vMinID + 2) % 4,0) - fki(vMinID,0),
|
||||
@@ -622,7 +622,7 @@ void NCNURBSExtension::GetAuxFaceEdges(int auxFace, Array<int> &edges) const
|
||||
}
|
||||
else // Auxiliary edge
|
||||
{
|
||||
edges[i] = FlipIndexSign(auxv2e.at(edge_v));
|
||||
edges[i] = -1 - auxv2e.at(edge_v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,7 +633,7 @@ int OffsetHelper(int i, int j, const Array<int> &a, const Array<int> &b)
|
||||
{
|
||||
if (i < 0)
|
||||
{
|
||||
return b[FlipIndexSign(i) + j];
|
||||
return b[-1 - i + j];
|
||||
}
|
||||
else if (i + j < a.Size())
|
||||
{
|
||||
@@ -679,7 +679,7 @@ void NCNURBSExtension::GetMasterEdgeDofs(bool dof, int me,
|
||||
}
|
||||
else // Auxiliary edge
|
||||
{
|
||||
GetAuxEdgeVertices(FlipIndexSign(slaveId), svert);
|
||||
GetAuxEdgeVertices(-1 - slaveId, svert);
|
||||
}
|
||||
|
||||
bool reverse = false;
|
||||
@@ -872,7 +872,7 @@ void ReorderArray2D(int i0, int j0, const Array2D<int> &a,
|
||||
// Set a quadrilateral vertex index permutation for a given orientation.
|
||||
void GetVertexOrdering(int ori, std::array<int, 4> &perm)
|
||||
{
|
||||
const int oriAbs = UnsignIndex(ori);
|
||||
const int oriAbs = ori < 0 ? -1 - ori : ori;
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
@@ -1094,7 +1094,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
if (slaveId < 0)
|
||||
{
|
||||
// Auxiliary face
|
||||
const int auxFace = FlipIndexSign(slaveId);
|
||||
const int auxFace = -1 - slaveId;
|
||||
|
||||
// Set slave face entity dimensions.
|
||||
if (dof)
|
||||
@@ -1171,7 +1171,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
}
|
||||
else
|
||||
{
|
||||
const int auxEdge = FlipIndexSign(edge);
|
||||
const int auxEdge = -1 - edge;
|
||||
GetAuxEdgeVertices(auxEdge, evert);
|
||||
}
|
||||
MFEM_ASSERT(evert[0] == vstart || evert[1] == vstart, "");
|
||||
@@ -1184,7 +1184,7 @@ void NCNURBSExtension::GetMasterFaceDofs(bool dof, int mf,
|
||||
// dimensions of the master face, by using ori.
|
||||
int e1 = -1, e2 = -1;
|
||||
{
|
||||
const int aori = UnsignIndex(ori);
|
||||
const int aori = ori < 0 ? -1 - ori : ori;
|
||||
if (aori % 2 == 0)
|
||||
{
|
||||
e1 = 0;
|
||||
@@ -1416,15 +1416,14 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
|
||||
{
|
||||
// Create a new auxiliary edge
|
||||
auxv2e[childPair] = auxEdges.size();
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ? parentEdge :
|
||||
FlipIndexSign(parentEdge),
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
|
||||
parentEdge : -1 - parentEdge,
|
||||
{childPair.first, childPair.second},
|
||||
{newParentEdge ? 0 : prevKI, ks}});
|
||||
}
|
||||
}
|
||||
|
||||
const int childEdge = childPairTopo ? v2e[childPair] :
|
||||
FlipIndexSign(auxv2e[childPair]);
|
||||
const int childEdge = childPairTopo ? v2e[childPair] : -1 - auxv2e[childPair];
|
||||
|
||||
// Check whether this is the final vertex in this parent edge. Note that
|
||||
// the logic for comparing (pv[0],pv[1]) to the next parents assumes the
|
||||
@@ -1461,15 +1460,14 @@ void NCNURBSExtension::ProcessVertexToKnot2D(const VertexToKnotSpan &v2k,
|
||||
|
||||
// -1 denotes `ne` at endpoint
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv[0] < pv[1] ?
|
||||
FlipIndexSign(parentEdge) :
|
||||
parentEdge,
|
||||
-1 - parentEdge : parentEdge,
|
||||
{finalChildPair.first, finalChildPair.second},
|
||||
{ks, -1}});
|
||||
}
|
||||
}
|
||||
|
||||
const int finalChildEdge = finalChildPairTopo ? v2e[finalChildPair] :
|
||||
FlipIndexSign(auxv2e[finalChildPair]);
|
||||
-1 - auxv2e[finalChildPair];
|
||||
edgePairs.emplace_back(-1, -1, finalChildEdge, parentEdge);
|
||||
}
|
||||
|
||||
@@ -1807,7 +1805,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
auxFaces.push_back(auxFace);
|
||||
facePairs.emplace_back(
|
||||
FacePairInfo{cv[0], parentFace,
|
||||
SlaveFaceInfo{FlipIndexSign(auxv2f[childPair]),
|
||||
SlaveFaceInfo{-1 - auxv2f[childPair],
|
||||
0, {i0, j0}, {d0, d1}}});
|
||||
}
|
||||
}
|
||||
@@ -2113,7 +2111,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
auxv2e[childPair] = auxEdges.size();
|
||||
auxEdges.emplace_back(AuxiliaryEdge{pv0 < pv1 ?
|
||||
parentEdge :
|
||||
FlipIndexSign(parentEdge),
|
||||
-1 - parentEdge,
|
||||
{childPair.first, childPair.second},
|
||||
{knotIndex0, knotIndex1}});
|
||||
}
|
||||
@@ -2133,8 +2131,7 @@ void NCNURBSExtension::ProcessVertexToKnot3D(
|
||||
|
||||
const EdgePairInfo ep_e((e_idx == n_d - de) ? -1 : tv,
|
||||
(e_idx == n_d - de) ? -1 : tvki,
|
||||
FlipIndexSign(auxv2e[childPair]),
|
||||
parentEdge);
|
||||
-1 - auxv2e[childPair], parentEdge);
|
||||
|
||||
const bool unset = !edgePairs[edgePairOS[parentEdge] + e_idx].isSet;
|
||||
if (unset)
|
||||
@@ -2229,7 +2226,7 @@ void NCNURBSExtension::GetAuxFaceToPatchTable(Array2D<int> &auxface2patch)
|
||||
if (s < 0)
|
||||
{
|
||||
// Auxiliary face.
|
||||
const int aux = FlipIndexSign(s);
|
||||
const int aux = -1 - s;
|
||||
if (auxface2patch(aux, 0) >= 0)
|
||||
{
|
||||
if (auxface2patch(aux, 1) != -1) { consistent = false; }
|
||||
@@ -2319,7 +2316,7 @@ void NCNURBSExtension::UpdateAuxiliaryKnotSpans(const Array<int> &rf)
|
||||
for (auto auxEdge : auxEdges)
|
||||
{
|
||||
const int p = auxEdge.parent;
|
||||
const int parent = UnsignIndex(p);
|
||||
const int parent = p < 0 ? -1 - p : p;
|
||||
const int kv = KnotInd(parent);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
@@ -2385,8 +2382,14 @@ int NCNURBSExtension::AuxiliaryEdgeNE(int aux_edge)
|
||||
const int signedParentEdge = auxEdges[aux_edge].parent;
|
||||
const int ki0 = auxEdges[aux_edge].ksi[0];
|
||||
const int ki1raw = auxEdges[aux_edge].ksi[1];
|
||||
const int ki1 = ki1raw == -1 ? KnotVec(UnsignIndex(signedParentEdge))->GetNE()
|
||||
: ki1raw;
|
||||
int ki1 = ki1raw;
|
||||
if (ki1raw == -1)
|
||||
{
|
||||
const bool rev = signedParentEdge < 0;
|
||||
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
|
||||
ki1 = KnotVec(parentEdge)->GetNE();
|
||||
}
|
||||
|
||||
return ki1 - ki0;
|
||||
}
|
||||
|
||||
@@ -2400,7 +2403,7 @@ void NCNURBSExtension::SlaveEdgeToParent(int se, int parent,
|
||||
Array<int> sev(2);
|
||||
if (se < 0) // Auxiliary edge
|
||||
{
|
||||
for (int i=0; i<2; ++i) { sev[i] = auxEdges[FlipIndexSign(se)].v[i]; }
|
||||
for (int i=0; i<2; ++i) { sev[i] = auxEdges[-1 - se].v[i]; }
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2456,7 +2459,7 @@ void NCNURBSExtension::GetMasterEdgePieceOffsets(int mid, Array<int> &os)
|
||||
}
|
||||
else
|
||||
{
|
||||
nes = AuxiliaryEdgeNE(FlipIndexSign(s));
|
||||
nes = AuxiliaryEdgeNE(-1 - s);
|
||||
}
|
||||
|
||||
os[i+1] = os[i] + nes;
|
||||
@@ -2562,7 +2565,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
|
||||
}
|
||||
else // Aux edge
|
||||
{
|
||||
const int aux_edge = FlipIndexSign(s);
|
||||
const int aux_edge = -1 - s;
|
||||
if (auxef[aux_edge].Size() == 0)
|
||||
{
|
||||
auxef[aux_edge].SetSize(AuxiliaryEdgeNE(aux_edge));
|
||||
@@ -2608,7 +2611,7 @@ int NCNURBSExtension::SetPatchFactors(int p)
|
||||
}
|
||||
|
||||
MFEM_VERIFY(consistent, "");
|
||||
return partialChange ? FlipIndexSign(dirSet) : dirSet;
|
||||
return partialChange ? -1 - dirSet : dirSet;
|
||||
}
|
||||
|
||||
void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
@@ -2712,7 +2715,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
if (s < 0)
|
||||
{
|
||||
// Auxiliary face.
|
||||
const int aux = FlipIndexSign(s);
|
||||
const int aux = -1 - s;
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
const int patch = auxface2patch(aux, i);
|
||||
@@ -2760,7 +2763,7 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
|
||||
const int dirSetSigned = SetPatchFactors(p);
|
||||
const bool partialChange = dirSetSigned < 0;
|
||||
const int dirSet = UnsignIndex(dirSetSigned);
|
||||
const int dirSet = partialChange ? -1 - dirSetSigned : dirSetSigned;
|
||||
const bool changed = (patchState[p] != dirSet) || partialChange;
|
||||
patchState[p] = dirSet;
|
||||
|
||||
@@ -2803,8 +2806,8 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
{
|
||||
const int dirSetSigned_i = SetPatchFactors(i);
|
||||
const bool partialChange_i = dirSetSigned_i < 0;
|
||||
const int dirSet_i = partialChange_i ?
|
||||
FlipIndexSign(dirSetSigned_i) : dirSetSigned_i;
|
||||
const int dirSet_i = partialChange_i ? -1 - dirSetSigned_i :
|
||||
dirSetSigned_i;
|
||||
const bool changed_i = (patchState[i] != dirSet_i) ||
|
||||
partialChange_i;
|
||||
patchState[p] = dirSet_i;
|
||||
@@ -3024,7 +3027,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
|
||||
|
||||
// Check whether ordering is reversed.
|
||||
const bool rev = verts[(s + 1) % 4] != fverts[1];
|
||||
if (rev) { s = FlipIndexSign(s); } // Reversed order is encoded by the sign.
|
||||
if (rev) { s = -1 - s; } // Reversed order is encoded by the sign.
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -3037,7 +3040,7 @@ int GetFaceOrientation(const Mesh *mesh, const int face,
|
||||
// see GetFaceOrientation.
|
||||
bool Reorder2D(int ori, std::array<int, 2> &s0)
|
||||
{
|
||||
const int shift = UnsignIndex(ori);
|
||||
const int shift = ori < 0 ? -1 - ori : ori;
|
||||
|
||||
// Shift is an F1 index in the counter-clockwise ordering of 4 quad vertices.
|
||||
// Now find the (i,j) indices of this index, with i,j in {0,1}.
|
||||
@@ -3061,7 +3064,7 @@ void GetInverseShiftedDimensions2D(int signedShift, int sm, int sn, int &m,
|
||||
int &n)
|
||||
{
|
||||
const bool rev = (signedShift < 0);
|
||||
const int shift = UnsignIndex(signedShift);
|
||||
const int shift = rev ? -1 - signedShift : signedShift;
|
||||
MFEM_ASSERT(0 <= shift && shift < 4, "");
|
||||
|
||||
// We consider 8 cases for the possible values of rev and shift.
|
||||
@@ -3133,7 +3136,7 @@ void GetShiftedGridPoints2D(int m, int n, int i, int j, int signedShift,
|
||||
int& sm, int& sn, int& si, int& sj)
|
||||
{
|
||||
const bool rev = (signedShift < 0);
|
||||
const int shift = UnsignIndex(signedShift);
|
||||
const int shift = rev ? -1 - signedShift : signedShift;
|
||||
MFEM_ASSERT(0 <= shift && shift < 4, "");
|
||||
|
||||
// (0,0) <= (i,j) < (m,n) are old indices, and old vertex [shift] maps
|
||||
@@ -3795,7 +3798,8 @@ void NCNURBSExtension::GenerateOffsets()
|
||||
const int signedParentEdge = auxEdges[e].parent;
|
||||
const int ki0 = auxEdges[e].ksi[0];
|
||||
const int ki1raw = auxEdges[e].ksi[1];
|
||||
const int parentEdge = UnsignIndex(signedParentEdge);
|
||||
const bool rev = signedParentEdge < 0;
|
||||
const int parentEdge = rev ? -1 - signedParentEdge : signedParentEdge;
|
||||
const int masterNE = KnotVec(parentEdge)->GetNE();
|
||||
const int ki1 = ki1raw == -1 ? masterNE : ki1raw;
|
||||
const int perEdgeCP = GetNCPperEdge(KnotVec(e));
|
||||
|
||||
+15
-11
@@ -3723,7 +3723,10 @@ bool NURBSExtension::CheckPatches()
|
||||
for (int i = 0; i < edges.Size(); i++)
|
||||
{
|
||||
edges[i] = edge_to_ukv[edges[i]];
|
||||
if (oedge[i] < 0) { edges[i] = FlipIndexSign(edges[i]); }
|
||||
if (oedge[i] < 0)
|
||||
{
|
||||
edges[i] = -1 - edges[i];
|
||||
}
|
||||
}
|
||||
|
||||
// In 2d - opposite edges must be same knotvector with opposite sign.
|
||||
@@ -3737,7 +3740,7 @@ bool NURBSExtension::CheckPatches()
|
||||
// {7, 6}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7} for Geometry::CUBE in 3D
|
||||
// See fem/geom.cpp for these definitions.
|
||||
if ((dim == 2 &&
|
||||
(edges[0] != FlipIndexSign(edges[2]) || edges[1] != FlipIndexSign(edges[3]))) ||
|
||||
(edges[0] != -1 - edges[2] || edges[1] != -1 - edges[3])) ||
|
||||
|
||||
(dim == 3 &&
|
||||
(edges[0] != edges[2] || edges[0] != edges[4] ||
|
||||
@@ -3766,7 +3769,7 @@ void NURBSExtension::CheckBdrPatches()
|
||||
edges[i] = edge_to_ukv[edges[i]];
|
||||
if (oedge[i] < 0)
|
||||
{
|
||||
edges[i] = FlipIndexSign(edges[i]);
|
||||
edges[i] = -1 - edges[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4763,13 +4766,14 @@ void NURBSExtension::GenerateBdrElementDofTable()
|
||||
SetPatchToBdrElements();
|
||||
|
||||
int *dof = bel_dof->GetJ();
|
||||
const int ndof = bel_dof->Size_of_connections();
|
||||
int ndof = bel_dof->Size_of_connections();
|
||||
for (int i = 0; i < ndof; i++)
|
||||
{
|
||||
const int idx = dof[i];
|
||||
int idx = dof[i];
|
||||
if (idx < 0)
|
||||
{
|
||||
dof[i] = -activeDof[FlipIndexSign(idx)];
|
||||
dof[i] = -1 - (activeDof[-1-idx] - 1);
|
||||
dof[i] = -activeDof[-1-idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4854,12 +4858,12 @@ void NURBSExtension::Generate2DBdrElementDofTable()
|
||||
for (int ii = 0; ii <= ord0; ii++)
|
||||
{
|
||||
conn.to = DofMap(p2g[(okv[0] >= 0) ? (i+ii) : (nx-i-ii)]);
|
||||
if (s == -1) { conn.to = FlipIndexSign(conn.to); }
|
||||
if (s == -1) { conn.to = -1 -conn.to; }
|
||||
bel_dof_list.Append(conn);
|
||||
}
|
||||
}
|
||||
bel_to_patch[lbe] = b;
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
|
||||
lbe++;
|
||||
}
|
||||
gbe++;
|
||||
@@ -4932,14 +4936,14 @@ void NURBSExtension::Generate3DBdrElementDofTable()
|
||||
{
|
||||
const int ii_ = (okv[0] >= 0) ? (i+ii) : (nx-i-ii);
|
||||
conn.to = DofMap(p2g(ii_, jj_));
|
||||
if (s == -1) { conn.to = FlipIndexSign(conn.to); }
|
||||
if (s == -1) { conn.to = -1 -conn.to; }
|
||||
bel_dof_list.Append(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
bel_to_patch[lbe] = b;
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : FlipIndexSign(i);
|
||||
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : FlipIndexSign(j);
|
||||
bel_to_IJK(lbe,0) = (okv[0] >= 0) ? i : (-1-i);
|
||||
bel_to_IJK(lbe,1) = (okv[1] >= 0) ? j : (-1-j);
|
||||
lbe++;
|
||||
}
|
||||
gbe++;
|
||||
|
||||
+4
-3
@@ -220,7 +220,7 @@ public:
|
||||
@a u.
|
||||
The main purpose of this function is its use in FindInterpolant.
|
||||
Use GetBotella instead for each shape function separately, perhaps in
|
||||
conjunction with GetSpan and GetRefPoint.*/
|
||||
conjuction with GetSpan and GetRefPoint.*/
|
||||
MFEM_DEPRECATED void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
|
||||
|
||||
/** @brief Global curve interpolation through the points @a x (overwritten).
|
||||
@@ -1398,7 +1398,8 @@ inline const real_t &NURBSPatch::operator()(int i, int j, int k, int l) const
|
||||
|
||||
inline int NURBSExtension::KnotInd(int edge) const
|
||||
{
|
||||
return UnsignIndex(edge_to_ukv[edge]);
|
||||
const int kv = edge_to_ukv[edge];
|
||||
return kv >= 0 ? kv : -1 - kv;
|
||||
}
|
||||
|
||||
inline int NURBSExtension::KnotSign(int edge) const
|
||||
@@ -1428,7 +1429,7 @@ const
|
||||
else
|
||||
{
|
||||
*okv = -oedge;
|
||||
return knotVectors[FlipIndexSign(kv)];
|
||||
return knotVectors[-1-kv];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-8
@@ -400,7 +400,7 @@ void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
|
||||
}
|
||||
else // special case: prism edge-face constraint
|
||||
{
|
||||
if (entity_owner[1][FlipIndexSign(si)] != MyRank)
|
||||
if (entity_owner[1][-1-si] != MyRank)
|
||||
{
|
||||
master_flag |= 0x2;
|
||||
}
|
||||
@@ -571,10 +571,9 @@ void ParNCMesh::CalculatePMatrixGroups()
|
||||
ranks.SetSize(0);
|
||||
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
|
||||
{
|
||||
const int si = face_list.slaves[j].index;
|
||||
const int owner =
|
||||
(si >= 0) ? entity_owner[2][si] : // standard face dependency
|
||||
entity_owner[1][FlipIndexSign(si)]; // prism edge-face dep
|
||||
int si = face_list.slaves[j].index;
|
||||
int owner = (si >= 0) ? entity_owner[2][si] // standard face dependency
|
||||
/* */ : entity_owner[1][-1 - si]; // prism edge-face dep
|
||||
ranks.Append(groups[owner][0]);
|
||||
}
|
||||
ranks.Sort();
|
||||
@@ -1182,7 +1181,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
|
||||
|
||||
Mesh::FaceInfo &fi = pmesh.faces_info[cf.index];
|
||||
fi.Elem2No = FlipIndexSign(fnbr_index[e[0]->index - NElements]);
|
||||
fi.Elem2No = -1 - fnbr_index[e[0]->index - NElements];
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
@@ -1271,7 +1270,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
// In other words, side 1 IS the side that generated the face.
|
||||
}
|
||||
MFEM_ASSERT(fi.Elem2No >= NElements, "");
|
||||
fi.Elem2No = FlipIndexSign(fnbr_index[fi.Elem2No - NElements]);
|
||||
fi.Elem2No = -1 - fnbr_index[fi.Elem2No - NElements];
|
||||
|
||||
const DenseMatrix* pm = full_list.point_matrices[sf.geom][sf.matrix];
|
||||
if (!sloc && Dim == 3)
|
||||
@@ -2287,7 +2286,7 @@ void ParNCMesh::Derefine(const Array<int> &derefs)
|
||||
if (element_type[index] == 0)
|
||||
{
|
||||
// this coarse element will get pruned, encode who owns it now
|
||||
index = FlipIndexSign(elements[coarse[i]].rank);
|
||||
index = -1 - elements[coarse[i]].rank;
|
||||
}
|
||||
transforms.embeddings[i].parent = index;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ add_subdirectory(multidomain)
|
||||
add_subdirectory(nurbs)
|
||||
add_subdirectory(parelag)
|
||||
add_subdirectory(performance)
|
||||
add_subdirectory(plasma)
|
||||
add_subdirectory(shifted)
|
||||
add_subdirectory(solvers)
|
||||
add_subdirectory(spde)
|
||||
|
||||
@@ -264,7 +264,7 @@ int main(int argc, char *argv[])
|
||||
// parallel degrees of freedom, with offsets given by array true_offset.
|
||||
FiniteElementCollection *fec = nullptr;
|
||||
NURBSExtension *NURBSext = nullptr;
|
||||
if (pmesh->NURBSext)
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
NURBSext = new NURBSExtension(pmesh->NURBSext, order);
|
||||
fec = new NURBSFECollection(order);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
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()
|
||||
@@ -1,85 +0,0 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# 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:
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_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
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
// (2) Dzanic et al., "A method for bounding high-order finite element
|
||||
// functions: Applications to mesh validity and bounds-preserving limiters".
|
||||
//
|
||||
// We also use a recursive subdivision strategy to compute tighter estimate of
|
||||
// the function extremum.
|
||||
//
|
||||
// Compile with: make gridfunction-bounds
|
||||
//
|
||||
@@ -33,6 +31,9 @@
|
||||
// mpirun -np 4 gridfunction-bounds -nb 100 -ref 5 -bt 2 -l2
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
@@ -55,8 +56,6 @@ int main (int argc, char *argv[])
|
||||
int b_type = -1;
|
||||
bool continuous = true;
|
||||
int nbrute = 0;
|
||||
int rec_depth = 4;
|
||||
real_t rel_tol = 1e-4;
|
||||
|
||||
// Parse command-line options.
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -84,12 +83,6 @@ int main (int argc, char *argv[])
|
||||
args.AddOption(&nbrute, "-nb", "--nbrute",
|
||||
"Brute force search for minimum in an array of nxnxn points "
|
||||
"in each element.");
|
||||
args.AddOption(&rec_depth, "-rd", "--rec-depth",
|
||||
"Maximum depth for recursive subdivision to compute function "
|
||||
"extremum.");
|
||||
args.AddOption(&rel_tol, "-rt", "--rel-tol",
|
||||
"Relative tolerance for termination of recursive "
|
||||
"subdivision.");
|
||||
args.ParseCheck();
|
||||
|
||||
Mesh mesh(mesh_file, 1, 1, false);
|
||||
@@ -158,19 +151,7 @@ int main (int argc, char *argv[])
|
||||
ParGridFunction lowerb(&fes_pc), upperb(&fes_pc);
|
||||
|
||||
// Compute bounds
|
||||
PLBound plb = pfunc_proj->GetElementBounds(lowerb, upperb, ref);
|
||||
|
||||
// Compute minimum and maximum bounds via recursion
|
||||
Vector bound_rec_min(vdim), bound_rec_max(vdim);
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
auto min_interval = pfunc_proj->EstimateFunctionMinimum(d, plb, rec_depth,
|
||||
rel_tol);
|
||||
auto max_interval = pfunc_proj->EstimateFunctionMaximum(d, plb, rec_depth,
|
||||
rel_tol);
|
||||
bound_rec_min(d) = min_interval.first;
|
||||
bound_rec_max(d) = max_interval.second;
|
||||
}
|
||||
pfunc_proj->GetElementBounds(lowerb, upperb, ref);
|
||||
|
||||
Vector bound_min(vdim), bound_max(vdim);
|
||||
for (int d = 0; d < vdim; d++)
|
||||
@@ -255,31 +236,17 @@ int main (int argc, char *argv[])
|
||||
{
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
cout << "Compare function extremum for component " <<
|
||||
cout << "Brute force and bounding comparison for component " <<
|
||||
d << endl;
|
||||
constexpr int w = 20;
|
||||
cout << left << setw(w) << " "
|
||||
<< setw(w) << "Brute force"
|
||||
<< setw(w) << "PL Bound"
|
||||
<< setw(w) << "PL Bound + recursion" << endl
|
||||
<< left << setw(w) << "Minimum: "
|
||||
<< setw(w) << global_min(d)
|
||||
<< setw(w) << bound_min(d)
|
||||
<< setw(w) << bound_rec_min(d) << endl
|
||||
<< left << setw(w) << "Difference: "
|
||||
<< setw(w) << "-"
|
||||
<< setw(w) << global_min(d)-bound_min(d)
|
||||
<< setw(w) << global_min(d)-bound_rec_min(d) << endl;
|
||||
cout << endl
|
||||
<< left << setw(w) << "Maximum: "
|
||||
<< setw(w) << global_max(d)
|
||||
<< setw(w) << bound_max(d)
|
||||
<< setw(w) << bound_rec_max(d) << endl
|
||||
<< left << setw(w) << "Difference: "
|
||||
<< setw(w) << "-"
|
||||
<< setw(w) << bound_max(d)-global_max(d)
|
||||
<< setw(w) << bound_rec_max(d)-global_max(d) << endl;
|
||||
cout << endl;
|
||||
cout << "Brute force minimum and minimum bound: " << global_min(d)
|
||||
<< " " << bound_min(d) << endl;
|
||||
|
||||
cout << "Brute force maximum and maximum bound: " << global_max(d)
|
||||
<< " " << bound_max(d) << endl;
|
||||
|
||||
cout << "The difference in bounds is: " <<
|
||||
global_min(d)-bound_min(d) << " " <<
|
||||
bound_max(d)-global_max(d) << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,19 +255,10 @@ int main (int argc, char *argv[])
|
||||
{
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
cout << "Compare function extremum for component " <<
|
||||
d << endl;
|
||||
constexpr int w = 20;
|
||||
cout << left << setw(w) << " "
|
||||
<< setw(w) << "PL Bound"
|
||||
<< setw(w) << "PL Bound + recursion" << endl
|
||||
<< left << setw(w) << "Minimum: "
|
||||
<< setw(w) << bound_min(d)
|
||||
<< setw(w) << bound_rec_min(d) << endl;
|
||||
cout << endl
|
||||
<< left << setw(w) << "Maximum: "
|
||||
<< setw(w) << bound_max(d)
|
||||
<< setw(w) << bound_rec_max(d) << endl;
|
||||
cout << "Minimum bound for component " << d << " is " <<
|
||||
bound_min(d) << endl;
|
||||
cout << "Maximum bound for component " << d << " is " <<
|
||||
bound_max(d) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -295,13 +295,8 @@ 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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -151,8 +151,6 @@ TEST_CASE("Collocated Derivative Kernels", "[QuadratureInterpolator]")
|
||||
auto L = GENERATE(QVectorLayout::byNODES, QVectorLayout::byVDIM);
|
||||
auto P = GENERATE(true, false);
|
||||
|
||||
CAPTURE(L, P);
|
||||
|
||||
const int nd = maps.ndof;
|
||||
const int nq = maps.nqpt;
|
||||
|
||||
@@ -165,15 +163,7 @@ TEST_CASE("Collocated Derivative Kernels", "[QuadratureInterpolator]")
|
||||
CGK::Run(dim, L, P, vdim, nd, nelem, maps.G.Read(), geom->J.Read(),
|
||||
evec_values.Read(), col_der.Write(), sdim, vdim, nd);
|
||||
|
||||
const real_t max_norm = qp_der.Normlinf();
|
||||
|
||||
qp_der -= col_der;
|
||||
|
||||
const real_t abs_err = qp_der.Normlinf();
|
||||
const real_t rel_err = max_norm > 0_r ?
|
||||
abs_err/max_norm :
|
||||
abs_err > 0_r ? mfem::infinity() : 0_r;
|
||||
CAPTURE(rel_err, max_norm);
|
||||
CHECK(rel_err <= 1e-13);
|
||||
REQUIRE(qp_der.Normlinf() == MFEM_Approx(0.0, 1e-10, 1e-10));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,52 +117,3 @@ 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,10 +281,8 @@ 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::UNKNOWN_RANGE_TYPE);
|
||||
REQUIRE( fe.GetDerivMapType() ==
|
||||
(int) FiniteElement::UNKNOWN_MAP_TYPE);
|
||||
REQUIRE( fe.GetDerivRangeType() == (int) FiniteElement::SCALAR );
|
||||
REQUIRE( fe.GetDerivMapType() == (int) FiniteElement::INTEGRAL);
|
||||
}
|
||||
}
|
||||
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,43 +271,3 @@ 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,201 +25,15 @@ 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]")
|
||||
{
|
||||
const int n = 1;
|
||||
const int dim = 3;
|
||||
const int order = 1;
|
||||
int n = 1;
|
||||
int dim = 3;
|
||||
int order = 1;
|
||||
|
||||
const double tol = 1e-6;
|
||||
double tol = 1e-6;
|
||||
|
||||
for (int type = (int)Element::TETRAHEDRON;
|
||||
type <= (int)Element::HEXAHEDRON; type++)
|
||||
|
||||
@@ -125,59 +125,6 @@ TEST_CASE("Array stl-interactions", "[Array]")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Array move assignment to view (MakeRef)", "[Array]")
|
||||
{
|
||||
constexpr int n = 5;
|
||||
|
||||
// Helper function that returns an Array by value (rvalue)
|
||||
auto make_sequence_array = [](int n)
|
||||
{
|
||||
Array<int> arr(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arr[i] = i + 1; // 1, 2, 3, ...
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// Create backing storage
|
||||
Array<int> backing1(n), backing2(n);
|
||||
backing1 = 0.0; backing2 = 0.0;
|
||||
|
||||
// Create a view into the backing storage
|
||||
Array<int> view1, view2;
|
||||
view1.MakeRef(backing1);
|
||||
view2.MakeRef(backing2);
|
||||
|
||||
auto seq_array = make_sequence_array(n);
|
||||
view1 = seq_array; // copy assign
|
||||
view2 = std::move(seq_array); // move assign
|
||||
|
||||
CHECK(seq_array.Size() == 0); // seq_array is invalidated by the move
|
||||
|
||||
// Both assignments should be semantically equivalent.
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
CHECK(backing1[i] == backing2[i]);
|
||||
}
|
||||
|
||||
// Create backing storage as raw array
|
||||
int backing3[n] = {0, 0, 0, 0, 0};
|
||||
|
||||
// Create a view into the backing storage
|
||||
Array<int> view3;
|
||||
view3.MakeRef(backing3, n);
|
||||
|
||||
// Assign from rvalue
|
||||
view3 = std::move(view2);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
CHECK(backing3[i] == backing1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Array delete at indices", "[Array],[GPU]")
|
||||
{
|
||||
for (int use_dev = 0; use_dev < 2; use_dev++)
|
||||
|
||||
@@ -200,39 +200,3 @@ 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);
|
||||
}
|
||||
|
||||
@@ -961,38 +961,3 @@ TEST_CASE("NNLS", "[DenseMatrix]")
|
||||
}
|
||||
|
||||
#endif // if MFEM_USE_LAPACK
|
||||
|
||||
TEST_CASE("DenseTensor slice copy and move assign equivalency",
|
||||
"[DenseMatrix][DenseTensor]")
|
||||
{
|
||||
auto fill_matrix = [](int n)
|
||||
{
|
||||
DenseMatrix M(n, n);
|
||||
M = 0.0;
|
||||
for (int i = 0; i < n; i++)
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
M(i, j) = i+j*n+1;
|
||||
}
|
||||
return M;
|
||||
};
|
||||
|
||||
constexpr int n = 3;
|
||||
constexpr int k = 2;
|
||||
|
||||
DenseTensor tensor1(n, n, k), tensor2(n, n, k);
|
||||
tensor1 = 0.0; tensor2 = 0.0;
|
||||
|
||||
DenseMatrix temp = fill_matrix(n);
|
||||
tensor1(0) = temp; // copy assign
|
||||
tensor2(0) = std::move(temp); // move assign
|
||||
|
||||
// Check that the tensor was actually updated
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
CHECK(tensor1(i, j, 0) == tensor2(i, j, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,13 +222,13 @@ TEST_CASE("Greville, Botella and Demko points", "[NURBS]")
|
||||
mfem::out<<"Knotvector : "; kvp.Print(mfem::out);
|
||||
|
||||
kvp.GetGreville(xi);
|
||||
mfem::out<<"Greville points : "; xi.Print(mfem::out,999);
|
||||
mfem::out<<"Greville points : "; xi.Print(std::cout,999);
|
||||
|
||||
kvp.GetBotella(xi);
|
||||
mfem::out<<"Botella points : "; xi.Print(mfem::out,999);
|
||||
mfem::out<<"Botella points : "; xi.Print(std::cout,999);
|
||||
|
||||
kvp.GetDemko(xi);
|
||||
mfem::out<<"Demko points : "; xi.Print(mfem::out,999);
|
||||
mfem::out<<"Demko points : "; xi.Print(std::cout,999);
|
||||
}
|
||||
|
||||
KnotVector kv(3, Vector({0.0, 0.3, 0.3, 0.3, 0.6, 1.0}));
|
||||
|
||||
Reference in New Issue
Block a user