Compare commits

..
Author SHA1 Message Date
Stowell, Mark L. c968516f36 Merge branch 'complex-coef-dev' of github.com:mfem/mfem into complex-coef-dev 2025-07-22 10:46:29 -07:00
Stowell, Mark L. 9a9d1ea967 Switching to complex_t 2025-07-22 10:45:56 -07:00
Stowell, Mark L. 46ee2ab5dc Adding complex_t type using code by @camierjs 2025-07-22 10:43:59 -07:00
adam-sim-dev fb672667cc Merge branch 'master' into complex-coef-dev 2025-07-15 09:22:34 +08:00
Stowell, Mark L. 1d35fafd21 Removing complex<int> unit test due to a "Build Analysis" error 2025-07-14 13:44:17 -07:00
Stowell, Mark L. a0ed1bfbca Adding ComplexVector unit test 2025-07-14 13:18:58 -07:00
Stowell, Mark L. 0c08279225 Adding coefficient as template argument in SesquilinearForm 2025-07-14 10:40:53 -07:00
Stowell, Mark L. bc84ce3b47 Adding ComplexMatrixConstantCoefficient 2025-07-14 10:40:02 -07:00
Stowell, Mark L. 36abe386e0 Adding DenseMatrix constructor to StdComplexDenseMatrix 2025-07-14 10:39:31 -07:00
Stowell, Mark L. a4868f2a98 Adding ComplexMatrixCoefficient 2025-07-12 16:05:19 -07:00
Stowell, Mark L. 21321b3abc Adding new type of complex dense matrix 2025-07-12 16:05:03 -07:00
Stowell, Mark L. bb4f39c3d7 Avoiding circular dependency 2025-07-12 11:49:55 -07:00
Stowell, Mark L. b605a29988 Making Real/Imag part coefficients publicly available 2025-07-12 11:49:35 -07:00
Stowell, Mark L. 7967e13f1d Moving complex coefficient code into separate files 2025-07-12 10:30:00 -07:00
Stowell, Mark L. b615f22b66 Adding complex coefficient support to ComplexLinearForm classes 2025-07-12 10:20:34 -07:00
Stowell, Mark L. e0fe515f21 Adding ComplexCoefficient methods to ComplexLinearForm classes 2025-07-12 07:42:11 -07:00
Stowell, Mark L. 269ee766db Updating ex22p 2025-07-11 17:14:17 -07:00
Stowell, Mark L. da8a221097 Adding complex coefficients to parallel classes 2025-07-11 17:14:04 -07:00
Stowell, Mark L. 20d6e63df0 Ubuntu portability 2025-07-11 14:48:12 -07:00
Stowell, Mark L. 2288cdcb7f Ubuntu portability 2025-07-11 14:32:25 -07:00
Stowell, Mark L. 233337c9d1 Adding UseDevice to extraction of real and imaginary parts of ComplexVector 2025-07-11 14:26:26 -07:00
Stowell, Mark L. 4d9cd853b7 Fix for Ubuntu portability 2025-07-11 14:18:25 -07:00
Stowell, Mark L. 27352658c3 Using the new complex coefficient classes in ex22 2025-07-11 14:00:22 -07:00
Stowell, Mark L. b8a303a07a Adding Complex coefficient classes 2025-07-11 14:00:02 -07:00
Stowell, Mark L. cee9bf3bb2 Adding a ComplexVector class 2025-07-11 13:59:23 -07:00
125 changed files with 4753 additions and 5229 deletions
+4 -4
View File
@@ -289,10 +289,10 @@ jobs:
run: |
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew update
brew install llvm@20 enzyme
echo "LLVM_PREFIX=$(brew --prefix llvm@20)" >> $GITHUB_ENV
echo "OMPI_CC=$(brew --prefix llvm@20)/bin/clang" >> $GITHUB_ENV
echo "OMPI_CXX=$(brew --prefix llvm@20)/bin/clang++" >> $GITHUB_ENV
brew install llvm@19 enzyme
echo "LLVM_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV
echo "OMPI_CC=$(brew --prefix llvm@19)/bin/clang" >> $GITHUB_ENV
echo "OMPI_CXX=$(brew --prefix llvm@19)/bin/clang++" >> $GITHUB_ENV
# MFEM build and test
- name: build
+4 -6
View File
@@ -211,7 +211,7 @@ miniapps/electromagnetics/joule
miniapps/electromagnetics/Volta-AMR*
miniapps/electromagnetics/Tesla-AMR*
miniapps/electromagnetics/Maxwell-Parallel*
miniapps/electromagnetics/Joule_[0-9]*
miniapps/electromagnetics/Joule_*
miniapps/gslib/field-diff
miniapps/gslib/field-interp
@@ -267,9 +267,9 @@ miniapps/meshing/bounding-box*
miniapps/meshing/jacobian-determinant*
miniapps/mtop/parheat
miniapps/mtop/ParHeat/*
miniapps/mtop/ParHeat*
miniapps/mtop/seqheat
miniapps/mtop/SeqHeat/*
miniapps/mtop/SeqHeat*
miniapps/autodiff/paradiff
miniapps/autodiff/seqadiff
@@ -277,7 +277,7 @@ miniapps/autodiff/seqtest
miniapps/autodiff/par_example
miniapps/autodiff/seq_example
miniapps/autodiff/seq_test
miniapps/autodiff/Example/*
miniapps/autodiff/Exampl*
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
@@ -411,8 +411,6 @@ miniapps/tribol/contact-patch-test
miniapps/diag-smoothers/abs-l1-jacobi
miniapps/diag-smoothers/mg-abs-l1-jacobi
miniapps/benchmarks/ceed-solver-bps/solver-bp
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
+5 -5
View File
@@ -22,7 +22,7 @@ include:
# the "needs" keyword and express the DAG of jobs for more efficiency.
# - We use setup and setup_baseline phases to download content outside of mfem
# directory.
# - Allocate/Release is where Dane resource are allocated/released once for all.
# - Allocate/Release is where ruby resource are allocated/released once for all.
# - Build and Test is where we build and MFEM for multiple toolchains.
# - Baseline_checks gathers baseline-type test suites execution
# - Baseline_publish, only available on master, allows to update baseline
@@ -53,7 +53,7 @@ variables:
AUTOTEST_COMMIT: "YES"
# Trigger subpipelines:
dane-build-and-test:
ruby-build-and-test:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
@@ -61,10 +61,10 @@ dane-build-and-test:
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-build-and-test.yml
include: .gitlab/ruby-build-and-test.yml
strategy: depend
dane-baseline:
ruby-baseline:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
@@ -73,7 +73,7 @@ dane-baseline:
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-baseline.yml
include: .gitlab/ruby-baseline.yml
strategy: depend
lassen-build-and-test:
+3 -3
View File
@@ -24,7 +24,7 @@ and `test type`.
Machines typically include:
* Dane: Intel Sapphire Rapids
* Ruby: 2nd Gen Intel Xeon (Cascade Lake)
* Lassen: Power9 + Nvidia GPU
* Corona: AMD GPU
@@ -76,13 +76,13 @@ with a spack spec of MFEM, within the limits permitted by the MFEM spack
package.
In any build-and-test sub-pipeline a job basically consists in defining the
spack spec to use. Adding a job on Dane for example resumes to:
spack spec to use. Adding a job on ruby for example resumes to:
```yaml
<job_name>:
variables:
SPEC: "<spack_spec>"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
```
The remaining and non trivial work is to make sure this spec is working. To
+1 -1
View File
@@ -24,7 +24,7 @@ variables:
# TODO: add a clean-up mechanism
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
# On LLNL's Dane, there is only one allocation shared among jobs in order to
# On LLNL's ruby, there is only one allocation shared among jobs in order to
# save time and resource. This allocation has to be uniquely named so that we
# are sure to retrieve it.
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
@@ -9,17 +9,17 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# GitLab pipelines configurations for the Dane machine at LLNL
# GitLab pipelines configurations for the Ruby machine at LLNL
variables:
MACHINE_NAME: dane
MACHINE_NAME: ruby
.on_dane:
.on_ruby:
tags:
- shell
- dane
- ruby
rules:
# Don't run dane jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_DANE == "OFF"'
# Don't run ruby jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_RUBY == "OFF"'
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
@@ -40,17 +40,16 @@ variables:
- when: on_success
# Spack helped builds
# Generic dane build job, extending build script
.build_and_test_on_dane:
extends: [.on_dane]
# Generic ruby build job, extending build script
.build_and_test_on_ruby:
extends: [.on_ruby]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
# Dane has 224 threads/node and we run 7 separate jobs: 224=7*32
- export THREADS=28
- export THREADS=16
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 60 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
+1 -1
View File
@@ -18,7 +18,7 @@
setup_baseline:
tags:
- shell
- dane
- ruby
stage: setup
variables:
GIT_STRATEGY: none
+1 -1
View File
@@ -16,7 +16,7 @@
setup:
tags:
- shell
- dane
- ruby
stage: setup
variables:
GIT_STRATEGY: none
@@ -19,8 +19,8 @@ stages:
- cleanup
- baseline_publish
baselinecheck_mfem_intel_dane:
extends: [.on_dane]
baselinecheck_mfem_intel_ruby:
extends: [.on_ruby]
stage: baseline_check
variables:
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
@@ -31,8 +31,8 @@ baselinecheck_mfem_intel_dane:
script:
- echo ${BUILD_ROOT}
- echo ${TPLS_DIR}
# Used by the tests in MFEM/tests, dane has 224 threads/node:
- export MFEM_TEST_NP=192
# Used by the tests in MFEM/tests:
- export MFEM_TEST_NP=48
# The next script uses the following environment variables:
# * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR,
# * BUILD_ROOT, TPLS_DIR, MACHINE_NAME
@@ -44,7 +44,7 @@ baselinecheck_mfem_intel_dane:
allow_failure: true
cleanup:
extends: .on_dane
extends: .on_ruby
stage: cleanup
variables:
GIT_STRATEGY: none
@@ -53,7 +53,7 @@ cleanup:
- rm -rf "${BUILD_ROOT}" || true
report_baseline:
extends: [.on_dane]
extends: [.on_ruby]
stage: baseline_report
script:
- echo ${MACHINE_NAME}
@@ -113,8 +113,8 @@ report_baseline:
exit $err
) 9> autotest.lock
baselinepublish_mfem_dane:
extends: [.on_dane]
baselinepublish_mfem_ruby:
extends: [.on_ruby]
stage: baseline_publish
rules:
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
@@ -129,5 +129,5 @@ baselinepublish_mfem_dane:
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/dane-config.yml
- local: .gitlab/configs/ruby-config.yml
- local: .gitlab/configs/setup-baseline.yml
@@ -19,54 +19,54 @@ stages:
allocate_resource:
variables:
GIT_STRATEGY: none
extends: .on_dane
extends: .on_ruby
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --reservation=ci --time=60 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
# GitLab jobs for the Dane machine at LLNL
# GitLab jobs for the Ruby machine at LLNL
debug_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug~mpi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
debug_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug+mpi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 ~mpi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10_sundials:
variables:
SPEC: "%gcc@10.3.1 +sundials"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10_petsc:
variables:
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
opt_par_gcc_10_pumi:
variables:
SPEC: "%gcc@10.3.1 +pumi"
extends: .build_and_test_on_dane
extends: .build_and_test_on_ruby
# Release
release_resource:
variables:
GIT_STRATEGY: none
extends: .on_dane
extends: .on_ruby
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
@@ -78,17 +78,17 @@ release_resource:
report_job_success:
stage: release_resource_and_report
extends:
- .on_dane
- .on_ruby
- .report_job_success
report_job_failure:
stage: release_resource_and_report
extends:
- .on_dane
- .on_ruby
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/dane-config.yml
- local: .gitlab/configs/ruby-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+2 -2
View File
@@ -14,7 +14,7 @@
# locals
glob_err=${BASELINE_TEST}.err
base=${BASELINE_TEST}-${SYS_TYPE}
if [[ "${MACHINE_NAME}" == "dane" ]]; then
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
base="${BASELINE_TEST}-${MACHINE_NAME}"
fi
base_diff=${base}.diff
@@ -31,7 +31,7 @@ cd tests
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
# run
if [[ "${MACHINE_NAME}" == "dane" ]]; then
if [[ "${MACHINE_NAME}" == "ruby" ]]; then
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "corona" ]]; then
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
+2 -2
View File
@@ -11,7 +11,7 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# There will be collision between corona and dane baselines.
# There will be collision between corona and ruby baselines.
# Once the corresponding files have been generated, we can switch to machine
# specific ref.
ARTIFACT_PATH=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}
@@ -21,7 +21,7 @@ PATCH_FILE=${ARTIFACT_PATH}.patch
FULL_FILE=${ARTIFACT_PATH}.out
DIFF_FILE=${ARTIFACT_PATH}.diff
# There will be collision between corona and dane baselines.
# There will be collision between corona and ruby baselines.
# Once the corresponding files have been generated, we can switch to machine
# specific ref.
SAVED_NAME=baseline-${SYS_TYPE}.saved
-12
View File
@@ -46,11 +46,6 @@ GPU computing
where host execution is always needed (e.g. when the DOFs array is small).
- Introduced MFEM_FOREACH_THREAD_DIRECT, which directly maps loop tasks to GPU
threads, assigning one task per thread.
- Implemented a GPU-accelerated matrix-free AMR derefinement `GridFunction`
update operator. This supports mixed geometry meshes and variable order
spaces, and is the default derefinement operator constructed by
`FiniteElementSpace::Update` and `ParFiniteElementSpace::Update`.
The operator requires `FiniteElementSpace::Nonconforming() == true`.
New and updated examples and miniapps
-------------------------------------
@@ -80,13 +75,6 @@ Miscellaneous
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
- MFEM_PERF_* annotations: added options to enable GPU-stream- and
MPI-synchronizations at the start and at the end of annotation regions. These
synchronizations can be enabled or disabled (default) in code via the new
macros: MFEM_PERF_SYNC, MFEM_PERF_SYNC_STREAM, and MFEM_PERF_SYNC_MPI; the
environment variables with the same names can be set to 0/1 to control the
synchronization as well.
Version 4.8, released on Apr 9, 2025
====================================
+5 -14
View File
@@ -598,20 +598,14 @@ set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
ALGOIM ENZYME)
# Add all created targets and *_FOUND libraries in the variables TPL_TARGETS and
# TPL_LIBRARIES, respectively.
set(TPL_TARGETS)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
foreach(TPL IN LISTS MFEM_TPLS)
if (${TPL}_FOUND OR TARGET ${TPL})
if (${TPL}_FOUND)
message(STATUS "MFEM: using package ${TPL}")
if (TARGET ${TPL})
list(APPEND TPL_TARGETS ${TPL})
else()
list(APPEND TPL_LIBRARIES ${${TPL}_LIBRARIES})
list(APPEND TPL_INCLUDE_DIRS ${${TPL}_INCLUDE_DIRS})
endif()
list(APPEND TPL_LIBRARIES ${${TPL}_LIBRARIES})
list(APPEND TPL_INCLUDE_DIRS ${${TPL}_INCLUDE_DIRS})
endif()
endforeach(TPL)
list(REVERSE TPL_LIBRARIES)
@@ -686,10 +680,7 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
# Declaring the library
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
if (TPL_TARGETS)
add_dependencies(mfem ${TPL_TARGETS})
endif()
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
if (MINGW)
target_link_libraries(mfem PRIVATE ws2_32)
endif()
-16
View File
@@ -121,11 +121,6 @@ Parallel build:
make -j 4
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
Parallel build with fetching of hypre and METIS:
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DFETCH_TPLS=YES
make -j 4
CUDA build:
(this build requires CMake 3.17 or newer)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
@@ -668,7 +663,6 @@ The specific libraries and their options are:
- OpenMP (optional), usually part of compiler, used when either MFEM_USE_OPENMP
or MFEM_USE_LEGACY_OPENMP is set to YES.
Options: OPENMP_OPT, OPENMP_LIB.
Versions: OpenMP >= 3.1 when MFEM_USE_OPENMP=YES.
- High-resolution POSIX clocks: when using MFEM_TIMER_TYPE = 2, it may be
necessary to link with a system library (e.g. librt.so).
@@ -848,7 +842,6 @@ The specific libraries and their options are:
- HIP (optional), used when MFEM_USE_HIP = YES.
URL: https://rocmdocs.amd.com
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
Versions: ROCm >= 5.6.1.
- OCCA (optional), used when MFEM_USE_OCCA = YES.
URL: https://libocca.org
@@ -1081,9 +1074,6 @@ The following options are CMake specific:
MFEM_ENABLE_TESTING - Enable the ctest framework for testing.
MFEM_ENABLE_EXAMPLES - Build all of the examples by default.
MFEM_ENABLE_MINIAPPS - Build all of the miniapps by default.
FETCH_TPLS - Enable fetching of all supported third-party libraries.
HYPRE_FETCH - Enable fetching of hypre.
METIS_FETCH - Enable fetching of metis.
External libraries (CMake):
---------------------------
@@ -1145,12 +1135,6 @@ The following built-in CMake packages are also used:
set the <LIBNAME>_LIBRARIES option directly; the configuration option
<LIBNAME>_DIR is not supported.
The MFEM CMake build system also provides fetching (automated building) for the
packages/libraries listed below. Note that when fetching is enabled, any related
auto-detection functionality is disabled.
- HYPRE
- METIS
Building without GNU make or CMake
==================================
+2 -54
View File
@@ -9,18 +9,15 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Defines the following variables if fetching of TPLs is disabled (default):
# Defines the following variables:
# - HYPRE_FOUND
# - HYPRE_LIBRARIES
# - HYPRE_INCLUDE_DIRS
# - HYPRE_VERSION
# - HYPRE_USING_CUDA (internal)
# - HYPRE_USING_HIP (internal)
# otherwise, the following are defined:
# - HYPRE (imported library target)
# - HYPRE_VERSION (cache variable)
if (HYPRE_FOUND OR TARGET HYPRE)
if (HYPRE_FOUND)
if (HYPRE_USING_CUDA)
find_package(CUDAToolkit REQUIRED)
endif()
@@ -36,55 +33,6 @@ if (HYPRE_FOUND OR TARGET HYPRE)
endif()
endif()
if (HYPRE_FETCH OR FETCH_TPLS)
set(HYPRE_FETCH_VERSION 2.33.0)
add_library(HYPRE STATIC IMPORTED)
# set options and associated dependencies
set(CMAKE_OPTIONS)
list(APPEND CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
if (MFEM_USE_CUDA)
list(APPEND CMAKE_OPTIONS -DHYPRE_WITH_CUDA:BOOL=ON)
find_package(CUDAToolkit REQUIRED)
target_link_libraries(HYPRE INTERFACE CUDA::cusparse CUDA::curand CUDA::cublas)
elseif (MFEM_USE_HIP)
list(APPEND CMAKE_OPTIONS -DHYPRE_WITH_HIP:BOOL=ON)
find_package(rocsparse REQUIRED)
find_package(rocrand REQUIRED)
target_link_libraries(HYPRE INTERFACE rocsparse rocrand)
endif()
if (MFEM_USE_SINGLE)
list(APPEND CMAKE_OPTIONS -DHYPRE_ENABLE_SINGLE:BOOL=ON)
endif()
# define external project and create future include directory so it is present
# to pass CMake checks at end of MFEM configuration step
message(STATUS "Will fetch HYPRE ${HYPRE_FETCH_VERSION} to be built with ${CMAKE_OPTIONS}")
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/hypre)
include(ExternalProject)
ExternalProject_Add(hypre
GIT_REPOSITORY https://github.com/hypre-space/hypre.git
GIT_TAG v${HYPRE_FETCH_VERSION}
GIT_SHALLOW TRUE
UPDATE_DISCONNECTED TRUE
SOURCE_SUBDIR src
PREFIX ${PREFIX}
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${PREFIX} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${CMAKE_OPTIONS})
file(MAKE_DIRECTORY ${PREFIX}/include)
# set imported library target properties
add_dependencies(HYPRE hypre)
set_target_properties(HYPRE PROPERTIES
IMPORTED_LOCATION ${PREFIX}/lib/libHYPRE.a
INTERFACE_INCLUDE_DIRECTORIES ${PREFIX}/include)
# convert HYPRE version to integer
string(REGEX MATCHALL "[0-9]+" HYPRE_SPLIT_VERSION ${HYPRE_FETCH_VERSION})
list(GET HYPRE_SPLIT_VERSION 0 HYPRE_MAJOR_VERSION)
list(GET HYPRE_SPLIT_VERSION 1 HYPRE_MINOR_VERSION)
list(GET HYPRE_SPLIT_VERSION 2 HYPRE_PATCH_VERSION)
math(EXPR HYPRE_VERSION "10000*${HYPRE_MAJOR_VERSION} + 100*${HYPRE_MINOR_VERSION} + ${HYPRE_PATCH_VERSION}")
# set cache variables that would otherwise be set after mfem_find_package call
set(HYPRE_VERSION ${HYPRE_VERSION} CACHE STRING "HYPRE version." FORCE)
return()
endif()
include(MfemCmakeUtilities)
mfem_find_package(HYPRE HYPRE HYPRE_DIR "include" "HYPRE.h" "lib" "HYPRE"
"Paths to headers required by HYPRE." "Libraries required by HYPRE."
+1 -29
View File
@@ -9,38 +9,10 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Defines the following variables if fetching of TPLs is disabled (default):
# Defines the following variables:
# - METIS_FOUND
# - METIS_LIBRARIES
# - METIS_INCLUDE_DIRS
# - METIS_VERSION_5
# otherwise, the following are defined:
# - METIS (imported library target)
# - METIS_VERSION_5 (cache variable)
if (METIS_FETCH OR FETCH_TPLS)
set(METIS_FETCH_VERSION 4.0.3)
add_library(METIS STATIC IMPORTED)
# define external project
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with default options")
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/metis)
include(ExternalProject)
ExternalProject_Add(metis
GIT_REPOSITORY https://github.com/mfem/tpls
GIT_TAG b60352fbe9675d374b00828055e55be4584c7995 # tag from 1/16/25
GIT_SHALLOW TRUE
UPDATE_DISCONNECTED TRUE
PREFIX ${PREFIX}
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
# set imported library target properties
add_dependencies(METIS metis)
set_target_properties(METIS PROPERTIES
IMPORTED_LOCATION ${PREFIX}/lib/libmetis.a)
# set cache variables that would otherwise be set after mfem_find_package call
set(METIS_VERSION_5 FALSE CACHE BOOL "Is METIS version 5?")
return()
endif()
include(MfemCmakeUtilities)
mfem_find_package(METIS METIS METIS_DIR "include;Lib" "metis.h"
+1 -4
View File
@@ -23,14 +23,11 @@
#include "_config.hpp"
#endif
#include <cstdint>
#include <climits>
namespace mfem
{
#if (defined(MFEM_USE_CUDA) && defined(__CUDACC__)) || \
(defined(MFEM_USE_HIP) && defined(__HIP__))
(defined(MFEM_USE_HIP) && defined(__HIPCC__))
#define MFEM_HOST_DEVICE __host__ __device__
#else
#define MFEM_HOST_DEVICE
-6
View File
@@ -89,12 +89,6 @@ option(MFEM_ENABLE_EXAMPLES "Build all of the examples" OFF)
option(MFEM_ENABLE_MINIAPPS "Build all of the miniapps" OFF)
option(MFEM_ENABLE_BENCHMARKS "Build all of the benchmarks" OFF)
# Allow a user to specify fetching of certain third-party libraries instead of
# searching for existing installations.
option(FETCH_TPLS "Enable fetching of all supported third-party libraries" OFF)
option(HYPRE_FETCH "Enable fetching of hypre" OFF)
option(METIS_FETCH "Enable fetching of METIS" OFF)
# Setting CXX/MPICXX on the command line or in user.cmake will overwrite the
# autodetected C++ compiler.
# set(CXX g++)
+35 -76
View File
@@ -62,14 +62,9 @@ static real_t epsilon_ = 1.0;
static real_t sigma_ = 20.0;
static real_t omega_ = 10.0;
real_t u0_real_exact(const Vector &);
real_t u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
complex<real_t> u0_exact(const Vector &x);
void u1_exact(const Vector &, ComplexVector &);
void u2_exact(const Vector &, ComplexVector &);
bool check_for_inline_mesh(const char * mesh_file);
@@ -215,54 +210,48 @@ int main(int argc, char *argv[])
ComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ComplexFunctionCoefficient u0(u0_exact);
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
ComplexConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
switch (prob)
{
case 0:
if (exact_sol)
{
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
u_exact->ProjectCoefficient(u0_r, u0_i);
u.ProjectBdrCoefficient(u0, ess_bdr);
u_exact->ProjectCoefficient(u0);
}
else
{
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
}
break;
case 1:
if (exact_sol)
{
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
u_exact->ProjectCoefficient(u1_r, u1_i);
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
u_exact->ProjectCoefficient(u1);
}
else
{
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
}
break;
case 2:
if (exact_sol)
{
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
u_exact->ProjectCoefficient(u2_r, u2_i);
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
u_exact->ProjectCoefficient(u2);
}
else
{
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
}
break;
default: break; // This should be unreachable
@@ -300,27 +289,24 @@ int main(int argc, char *argv[])
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
ComplexConstantCoefficient complexMassCoef(-omega_ * omega_ * epsilon_,
omega_ * sigma_);
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
a->AddDomainIntegrator<DiffusionIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
break;
default: break; // This should be unreachable
}
@@ -436,29 +422,24 @@ int main(int argc, char *argv[])
if (exact_sol)
{
real_t err_r = -1.0;
real_t err_i = -1.0;
real_t err_u = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
err_u = u.ComputeL2Error(u0);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
err_u = u.ComputeL2Error(u1);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
err_u = u.ComputeL2Error(u2);
break;
default: break; // This should be unreachable
}
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
cout << endl;
}
@@ -564,36 +545,14 @@ complex<real_t> u0_exact(const Vector &x)
return std::exp(-i * kappa * x[dim - 1]);
}
real_t u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
real_t u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
void u1_exact(const Vector &x, ComplexVector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
void u2_exact(const Vector &x, ComplexVector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
}
+50 -33
View File
@@ -62,6 +62,10 @@ static real_t epsilon_ = 1.0;
static real_t sigma_ = 20.0;
static real_t omega_ = 10.0;
complex<real_t> u0_exact(const Vector &x);
void u1_exact(const Vector &, ComplexVector &);
void u2_exact(const Vector &, ComplexVector &);
real_t u0_real_exact(const Vector &);
real_t u0_imag_exact(const Vector &);
@@ -244,13 +248,22 @@ int main(int argc, char *argv[])
ParComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
ComplexFunctionCoefficient u0(u0_exact);
ComplexVectorFunctionCoefficient u1(dim, u1_exact);
ComplexVectorFunctionCoefficient u2(dim, u2_exact);
ComplexConstantCoefficient oneCoef(1.0);
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
ComplexVectorConstantCoefficient oneVecCoef(oneVec);
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
/*
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
@@ -258,40 +271,40 @@ int main(int argc, char *argv[])
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
*/
switch (prob)
{
case 0:
if (exact_sol)
{
u.ProjectBdrCoefficient(u0_r, u0_i, ess_bdr);
u_exact->ProjectCoefficient(u0_r, u0_i);
u.ProjectBdrCoefficient(u0, ess_bdr);
u_exact->ProjectCoefficient(u0);
}
else
{
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
u.ProjectBdrCoefficient(oneCoef, ess_bdr);
}
break;
case 1:
if (exact_sol)
{
u.ProjectBdrCoefficientTangent(u1_r, u1_i, ess_bdr);
u_exact->ProjectCoefficient(u1_r, u1_i);
u.ProjectBdrCoefficientTangent(u1, ess_bdr);
u_exact->ProjectCoefficient(u1);
}
else
{
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
u.ProjectBdrCoefficientTangent(oneVecCoef, ess_bdr);
}
break;
case 2:
if (exact_sol)
{
u.ProjectBdrCoefficientNormal(u2_r, u2_i, ess_bdr);
u_exact->ProjectCoefficient(u2_r, u2_i);
u.ProjectBdrCoefficientNormal(u2, ess_bdr);
u_exact->ProjectCoefficient(u2);
}
else
{
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
u.ProjectBdrCoefficientNormal(oneVecCoef, ess_bdr);
}
break;
default: break; // This should be unreachable
@@ -331,27 +344,24 @@ int main(int argc, char *argv[])
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
ComplexConstantCoefficient complexMassCoef(-omega_ * omega_ * epsilon_,
omega_ * sigma_);
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
a->AddDomainIntegrator<DiffusionIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<MassIntegrator>(complexMassCoef);
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
a->AddDomainIntegrator<CurlCurlIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
a->AddDomainIntegrator<DivDivIntegrator>(stiffnessCoef);
a->AddDomainIntegrator<VectorFEMassIntegrator>(complexMassCoef);
break;
default: break; // This should be unreachable
}
@@ -475,22 +485,18 @@ int main(int argc, char *argv[])
if (exact_sol)
{
real_t err_r = -1.0;
real_t err_i = -1.0;
real_t err_u = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
err_u = u.ComputeL2Error(u0);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
err_u = u.ComputeL2Error(u1);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
err_u = u.ComputeL2Error(u2);
break;
default: break; // This should be unreachable
}
@@ -498,8 +504,7 @@ int main(int argc, char *argv[])
if ( myid == 0 )
{
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << "|| u_h - u ||_{L^2} = " << err_u << endl;
cout << endl;
}
}
@@ -627,6 +632,12 @@ real_t u0_imag_exact(const Vector &x)
return u0_exact(x).imag();
}
void u1_exact(const Vector &x, ComplexVector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_exact(x);
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
@@ -639,6 +650,12 @@ void u1_imag_exact(const Vector &x, Vector &v)
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_exact(const Vector &x, ComplexVector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
+1 -3
View File
@@ -78,7 +78,6 @@ private:
opr.SetOperatorOwner(false);
CGSolver* pcg = new CGSolver();
// pcg->iterative_mode = false; // the multigrid algorithm does this
pcg->SetPrintLevel(-1);
pcg->SetMaxIter(200);
pcg->SetRelTol(sqrt(1e-4));
@@ -101,8 +100,7 @@ private:
Vector diag(fespace.GetTrueVSize());
bfs[level]->AssembleDiagonal(diag);
Solver *smoother = new OperatorChebyshevSmoother(
*opr, diag, ess_tdof_list, 2);
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag, ess_tdof_list, 2);
AddLevel(opr.Ptr(), smoother, true, true);
}
};
-1
View File
@@ -88,7 +88,6 @@ private:
amg->SetPrintLevel(-1);
CGSolver* pcg = new CGSolver(MPI_COMM_WORLD);
// pcg->iterative_mode = false; // the multigrid algorithm does this
pcg->SetPrintLevel(-1);
pcg->SetMaxIter(10);
pcg->SetRelTol(sqrt(1e-4));
+2 -2
View File
@@ -59,6 +59,7 @@ set(SRCS
integ/nonlininteg_vecconvection_pa.cpp
integ/nonlininteg_vecconvection_mf.cpp
coefficient.cpp
complex_coefficient.cpp
complex_fem.cpp
convergence.cpp
datacollection.cpp
@@ -82,8 +83,6 @@ set(SRCS
fe/fe_ser.cpp
fe_coll.cpp
fespace.cpp
derefmat_op.cpp
pderefmat_op.cpp
geom.cpp
gridfunc.cpp
hybridization.cpp
@@ -178,6 +177,7 @@ set(HDRS
integ/bilininteg_hcurlhdiv_kernels.hpp
integ/bilininteg_mass_kernels.hpp
coefficient.hpp
complex_coefficient.hpp
complex_fem.hpp
convergence.hpp
datacollection.hpp
+64 -34
View File
@@ -255,8 +255,6 @@ PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
{
MFEM_PERF_FUNCTION;
if ( Device::Allows(Backend::CEED_MASK) ) { return; }
ElementDofOrdering ordering = GetEVectorOrdering(*a->FESpace());
elem_restrict = trial_fes->GetElementRestriction(ordering);
@@ -268,7 +266,11 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
// Gather the attributes on the host from all the elements
const Mesh &mesh = *trial_fes->GetMesh();
elem_attributes = &mesh.GetElementAttributes();
elem_attributes.SetSize(mesh.GetNE());
for (int i = 0; i < mesh.GetNE(); ++i)
{
elem_attributes[i] = mesh.GetAttribute(i);
}
}
// Construct face restriction operators only if the bilinear form has
@@ -327,14 +329,50 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
bdr_face_dYdn.SetSize(bdr_face_restrict_lex->Height());
}
bdr_face_attributes = &trial_fes->GetMesh()->GetBdrFaceAttributes();
const Mesh &mesh = *trial_fes->GetMesh();
// See LinearFormExtension::Update for explanation of f_to_be logic.
std::unordered_map<int,int> f_to_be;
for (int i = 0; i < mesh.GetNBE(); ++i)
{
const int f = mesh.GetBdrElementFaceIndex(i);
f_to_be[f] = i;
}
const int nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
bdr_attributes.SetSize(nf_bdr);
int f_ind = 0;
int missing_bdr_elems = 0;
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
if (!mesh.GetFaceInformation(f).IsOfFaceType(FaceType::Boundary))
{
continue;
}
int attribute = 1; // default value
if (f_to_be.find(f) != f_to_be.end())
{
const int be = f_to_be[f];
attribute = mesh.GetBdrAttribute(be);
}
else
{
// If a boundary face does not correspond to the a boundary element,
// we assign it the default attribute of 1. We also generate a
// warning at runtime with the number of such missing elements.
++missing_bdr_elems;
}
bdr_attributes[f_ind] = attribute;
++f_ind;
}
if (missing_bdr_elems)
{
MFEM_WARNING("Missing " << missing_bdr_elems << " boundary elements "
"for boundary faces.");
}
}
}
void PABilinearFormExtension::Assemble()
{
MFEM_PERF_FUNCTION;
SetupRestrictionOperators(L2FaceValues::DoubleValued);
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
@@ -391,7 +429,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
const int attr = d_attr[e];
if (attr <= 0 || d_m[attr - 1] == 0)
if (d_m[attr - 1] == 0)
{
for (int i = 0; i < nd; ++i)
{
@@ -412,7 +450,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
for (int i = 0; i < iSz; ++i)
{
assemble_diagonal_with_markers(*integrators[i], elem_markers[i],
*elem_attributes, localY);
elem_attributes, localY);
}
const ElementRestriction* H1elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict);
@@ -438,7 +476,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
for (int i = 0; i < iSz; ++i)
{
assemble_diagonal_with_markers(*integrators[i], elem_markers[i],
*elem_attributes, y);
elem_attributes, y);
}
}
@@ -451,7 +489,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
for (int i = 0; i < n_bdr_integs; ++i)
{
assemble_diagonal_with_markers(*bdr_integs[i], bdr_markers[i],
*bdr_face_attributes, bdr_face_Y);
bdr_attributes, bdr_face_Y);
}
bdr_face_restrict_lex->AddAbsMultTranspose(bdr_face_Y, y);
}
@@ -491,8 +529,6 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
const bool useAbs) const
{
MFEM_PERF_FUNCTION;
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
@@ -552,7 +588,7 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
for (int i = 0; i < iSz; ++i)
{
AddMultWithMarkers(*integrators[i], localX, elem_markers[i],
*elem_attributes, false, localY, useAbs);
elem_attributes, false, localY, useAbs);
}
if (H1elem_restrict && useAbs)
{
@@ -654,8 +690,8 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
}
for (int i = 0; i < n_bdr_integs; ++i)
{
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i],
*bdr_face_attributes, false, bdr_face_Y);
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
false, bdr_face_Y);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
@@ -663,14 +699,12 @@ void PABilinearFormExtension::MultInternal(const Vector &x, Vector &y,
{
AddMultNormalDerivativesWithMarkers(
*bdr_face_integs[i], bdr_face_X, bdr_face_dXdn,
bdr_face_markers[i], *bdr_face_attributes, bdr_face_Y,
bdr_face_dYdn);
bdr_face_markers[i], bdr_attributes, bdr_face_Y, bdr_face_dYdn);
}
else
{
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X,
bdr_face_markers[i], *bdr_face_attributes, false,
bdr_face_Y);
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
bdr_attributes, false, bdr_face_Y);
}
}
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
@@ -693,7 +727,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], *elem_attributes,
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], elem_attributes,
true, localY);
}
elem_restrict->MultTranspose(localY, y);
@@ -740,14 +774,13 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
bdr_face_Y = 0.0;
for (int i = 0; i < n_bdr_integs; ++i)
{
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i],
*bdr_face_attributes, true, bdr_face_Y);
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
true, bdr_face_Y);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X,
bdr_face_markers[i], *bdr_face_attributes, true,
bdr_face_Y);
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
bdr_attributes, true, bdr_face_Y);
}
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
@@ -771,7 +804,7 @@ static void AddWithMarkers_(
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
const int attr = d_attr[e];
if (attr <= 0 || d_m[attr - 1] == 0) { return; }
if (d_m[attr - 1] == 0) { return; }
for (int i = 0; i < nd; ++i)
{
d_y(i, e) += d_x(i, e);
@@ -865,7 +898,6 @@ EABilinearFormExtension::EABilinearFormExtension(BilinearForm *form)
void EABilinearFormExtension::Assemble()
{
MFEM_PERF_FUNCTION;
SetupRestrictionOperators(L2FaceValues::SingleValued);
ne = trial_fes->GetMesh()->GetNE();
@@ -888,8 +920,7 @@ void EABilinearFormExtension::Assemble()
{
const int i = idx % sz;
const int e = idx / sz;
const real_t val =
d_a[e] > 0 ? (d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0) : 0;
const real_t val = d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0.0;
if (add)
{
d_ea_2(i, e) += val;
@@ -922,7 +953,7 @@ void EABilinearFormExtension::Assemble()
ea_data_tmp.SetSize(ea_data.Size());
integrators[i]->AssembleEA(*a->FESpace(), ea_data_tmp, false);
add_with_markers(ea_data_tmp, ea_data, ne, *markers,
*elem_attributes, add);
elem_attributes, add);
}
}
}
@@ -951,7 +982,7 @@ void EABilinearFormExtension::Assemble()
ea_data_tmp.SetSize(ea_data_bdr.Size());
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_tmp, add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
*bdr_face_attributes, add);
bdr_attributes, add);
}
}
}
@@ -1000,7 +1031,7 @@ void EABilinearFormExtension::Assemble()
ea_data_tmp,
add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
*bdr_face_attributes, add);
bdr_attributes, add);
}
}
}
@@ -1414,7 +1445,6 @@ FABilinearFormExtension::FABilinearFormExtension(BilinearForm *form)
void FABilinearFormExtension::Assemble()
{
MFEM_PERF_FUNCTION;
EABilinearFormExtension::Assemble();
FiniteElementSpace &fes = *a->FESpace();
int width = fes.GetVSize();
+1 -2
View File
@@ -69,8 +69,7 @@ class PABilinearFormExtension : public BilinearFormExtension
protected:
const FiniteElementSpace *trial_fes, *test_fes; // Not owned
/// Attributes of all mesh elements.
const Array<int> *elem_attributes; // Not owned
const Array<int> *bdr_face_attributes; // Not owned
Array<int> elem_attributes, bdr_attributes;
mutable Vector tmp_evec; // Work array
mutable Vector localX, localY;
mutable Vector int_face_X, int_face_Y;
+10 -10
View File
@@ -812,7 +812,7 @@ protected:
const FiniteElement & test_fe) const
{
return (trial_fe.GetDim() == 1 && test_fe.GetDim() == 1 &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -884,7 +884,7 @@ protected:
const FiniteElement & trial_fe,
const FiniteElement & test_fe) const
{
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -919,7 +919,7 @@ protected:
const FiniteElement & trial_fe,
const FiniteElement & test_fe) const
{
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
return (trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -1600,7 +1600,7 @@ public:
{
return (trial_fe.GetCurlDim() == 3 && test_fe.GetRangeDim() == 3 &&
trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -1635,7 +1635,7 @@ public:
{
return (trial_fe.GetDim() == 2 && test_fe.GetDim() == 2 &&
trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
@@ -1669,7 +1669,7 @@ public:
{
return (trial_fe.GetDim() == 2 && test_fe.GetDim() == 2 &&
trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -1760,7 +1760,7 @@ public:
const FiniteElement & test_fe) const
{
return (trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR );
}
@@ -1793,7 +1793,7 @@ public:
const FiniteElement & test_fe) const
{
return (trial_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
trial_fe.GetDerivType() == mfem::FiniteElement::GRAD &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
test_fe.GetDerivType() == mfem::FiniteElement::DIV );
}
@@ -1832,7 +1832,7 @@ public:
const FiniteElement & test_fe) const
{
return (trial_fe.GetRangeType() == mfem::FiniteElement::VECTOR &&
trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
trial_fe.GetDerivType() == mfem::FiniteElement::DIV &&
test_fe.GetRangeType() == mfem::FiniteElement::SCALAR &&
test_fe.GetDerivType() == mfem::FiniteElement::GRAD
);
@@ -1973,7 +1973,7 @@ protected:
const FiniteElement & test_fe) const override
{
return (trial_fe.GetCurlDim() == 3 && test_fe.GetRangeDim() == 3 &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
trial_fe.GetDerivType() == mfem::FiniteElement::CURL &&
test_fe.GetRangeType() == mfem::FiniteElement::VECTOR );
}
-1
View File
@@ -50,7 +50,6 @@ ElementTransformation *RefinedToCoarse(
void Coefficient::Project(QuadratureFunction &qf)
{
MFEM_PERF_FUNCTION;
QuadratureSpaceBase &qspace = *qf.GetSpace();
const int ne = qspace.GetNE();
Vector values;
+217
View File
@@ -0,0 +1,217 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "complex_fem.hpp"
#include "../general/forall.hpp"
using namespace std;
namespace mfem
{
real_t
RealPartCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
complex_t val = complex_coef_.Eval(T, ip);
return val.real();
}
real_t
ImagPartCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
complex_t val = complex_coef_.Eval(T, ip);
return val.imag();
}
RealPartVectorCoefficient::RealPartVectorCoefficient(ComplexVectorCoefficient &
complex_vcoef)
: VectorCoefficient(complex_vcoef.GetVDim()),
complex_vcoef_(complex_vcoef),
val_(vdim)
{}
void
RealPartVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
complex_vcoef_.Eval(val_, T, ip);
V = val_.real();
}
ImagPartVectorCoefficient::ImagPartVectorCoefficient(ComplexVectorCoefficient &
complex_vcoef)
: VectorCoefficient(complex_vcoef.GetVDim()),
complex_vcoef_(complex_vcoef),
val_(vdim)
{}
void
ImagPartVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
complex_vcoef_.Eval(val_, T, ip);
V = val_.imag();
}
RealPartMatrixCoefficient::RealPartMatrixCoefficient(ComplexMatrixCoefficient &
complex_mcoef)
: MatrixCoefficient(complex_mcoef.GetHeight(), complex_mcoef.GetWidth()),
complex_mcoef_(complex_mcoef),
val_(height, width)
{}
void
RealPartMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
complex_mcoef_.Eval(val_, T, ip);
M = val_.real();
}
ImagPartMatrixCoefficient::ImagPartMatrixCoefficient(ComplexMatrixCoefficient &
complex_mcoef)
: MatrixCoefficient(complex_mcoef.GetHeight(), complex_mcoef.GetWidth()),
complex_mcoef_(complex_mcoef),
val_(height, width)
{}
void
ImagPartMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
complex_mcoef_.Eval(val_, T, ip);
M = val_.imag();
}
ComplexCoefficient::ComplexCoefficient()
: time(0.),
re_part_coef_(*this), im_part_coef_(*this),
real_coef_(re_part_coef_), imag_coef_(im_part_coef_)
{ }
ComplexCoefficient::ComplexCoefficient(Coefficient &c_r,
Coefficient &c_i)
: time(c_r.GetTime()),
re_part_coef_(*this), im_part_coef_(*this),
real_coef_(c_r), imag_coef_(c_i)
{
c_i.SetTime(time);
}
complex_t
ComplexCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
// Avoid circular dependency
MFEM_VERIFY(std::addressof(real_coef_) != std::addressof(re_part_coef_) &&
std::addressof(imag_coef_) != std::addressof(im_part_coef_),
"Classes dervied from ComplexCoefficient must either "
"implement an Eval method or supply Coefficients "
"for both the real and imaginary parts of the field.");
return complex_t(real_coef_.Eval(T, ip), imag_coef_.Eval(T, ip));
}
ComplexVectorCoefficient::ComplexVectorCoefficient(VectorCoefficient &v_r,
VectorCoefficient &v_i)
: vdim(v_r.GetVDim()), time(v_r.GetTime()),
re_part_vcoef_(*this), im_part_vcoef_(*this),
real_vcoef_(v_r), imag_vcoef_(v_i)
{
MFEM_ASSERT(v_r.GetVDim() == v_i.GetVDim(), "ComplexVectorCoefficient"
" - incompatible vector dimensions of real and imaginary parts.");
v_i.SetTime(time);
}
void ComplexVectorCoefficient::Eval(ComplexVector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
// Avoid circular dependency
MFEM_VERIFY(std::addressof(real_vcoef_) != std::addressof(re_part_vcoef_) &&
std::addressof(imag_vcoef_) != std::addressof(im_part_vcoef_),
"Classes dervied from ComplexVectorCoefficient must either "
"implement an Eval method or supply VectorCoefficients "
"for both the real and imaginary parts of the field.");
V_r_.SetSize(vdim);
V_i_.SetSize(vdim);
real_vcoef_.Eval(V_r_, T, ip);
imag_vcoef_.Eval(V_i_, T, ip);
V.Set(V_r_, V_i_);
}
ComplexConstantCoefficient::ComplexConstantCoefficient(
const complex_t z)
: val(z), real_coef(z.real()), imag_coef(z.imag())
{
real_coef_ = real_coef;
imag_coef_ = imag_coef;
}
ComplexConstantCoefficient::ComplexConstantCoefficient(
real_t z_r, real_t z_i)
: real_coef(z_r), imag_coef(z_i)
{
val = complex_t(z_r, z_i);
real_coef_ = real_coef;
imag_coef_ = imag_coef;
}
complex_t ComplexFunctionCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
real_t x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
if (Function)
{
return Function(transip);
}
else
{
return TDFunction(transip, GetTime());
}
}
void ComplexVectorFunctionCoefficient::Eval(ComplexVector &V,
ElementTransformation &T,
const IntegrationPoint &ip)
{
real_t x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
V.SetSize(vdim);
if (Function)
{
Function(transip, V);
}
else
{
TDFunction(transip, GetTime(), V);
}
if (Q)
{
V *= Q->Eval(T, ip, GetTime());
}
}
} // end namespace mfem
+523
View File
@@ -0,0 +1,523 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_COMPLEX_COEFFICIENT
#define MFEM_COMPLEX_COEFFICIENT
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "coefficient.hpp"
#include "intrules.hpp"
#include "eltrans.hpp"
namespace mfem
{
class ComplexCoefficient;
class ComplexVectorCoefficient;
class ComplexMatrixCoefficient;
/// Standard Coefficient which returns the real part of a ComplexCoefficient
class RealPartCoefficient : public Coefficient
{
private:
ComplexCoefficient &complex_coef_;
public:
RealPartCoefficient(ComplexCoefficient & complex_coef)
: complex_coef_(complex_coef) {}
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Standard Coefficient which returns the imaginary part of a
/// ComplexCoefficient
class ImagPartCoefficient : public Coefficient
{
private:
ComplexCoefficient &complex_coef_;
public:
ImagPartCoefficient(ComplexCoefficient & complex_coef)
: complex_coef_(complex_coef) {}
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
typedef ImagPartCoefficient ImaginaryPartCoefficient;
class RealPartVectorCoefficient : public VectorCoefficient
{
private:
ComplexVectorCoefficient &complex_vcoef_;
mutable ComplexVector val_;
public:
RealPartVectorCoefficient(ComplexVectorCoefficient & complex_vcoef);
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
};
class ImagPartVectorCoefficient : public VectorCoefficient
{
private:
ComplexVectorCoefficient &complex_vcoef_;
mutable ComplexVector val_;
public:
ImagPartVectorCoefficient(ComplexVectorCoefficient & complex_vcoef);
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
};
typedef ImagPartVectorCoefficient ImaginaryPartVectorCoefficient;
class RealPartMatrixCoefficient : public MatrixCoefficient
{
private:
ComplexMatrixCoefficient &complex_mcoef_;
mutable ComplexTypeDenseMatrix val_;
public:
RealPartMatrixCoefficient(ComplexMatrixCoefficient & complex_mcoef);
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
class ImagPartMatrixCoefficient : public MatrixCoefficient
{
private:
ComplexMatrixCoefficient &complex_mcoef_;
mutable ComplexTypeDenseMatrix val_;
public:
ImagPartMatrixCoefficient(ComplexMatrixCoefficient & complex_mcoef);
void Eval(DenseMatrix &V, ElementTransformation &T,
const IntegrationPoint &ip);
};
typedef ImagPartMatrixCoefficient ImaginaryPartMatrixCoefficient;
/** @brief Base class ComplexCoefficients that optionally depend on space and
time. These are used by the SesquilinearForm, ComplexLinearForm, and
ComplexGridFunction classes to represent the physical coefficients in
the PDEs that are being discretized. This class can also be used in a more
general way to represent functions that don't necessarily belong to a FE
space, e.g., to project onto ComplexGridFunctions to use as initial
conditions, exact solutions, etc. See, e.g., ex22 for these uses. */
class ComplexCoefficient
{
protected:
real_t time;
private:
RealPartCoefficient re_part_coef_;
ImagPartCoefficient im_part_coef_;
protected:
Coefficient &real_coef_;
Coefficient &imag_coef_;
public:
ComplexCoefficient();
ComplexCoefficient(Coefficient &c_r, Coefficient &c_i);
/// Set the time for time dependent coefficients
virtual void SetTime(real_t t)
{ time = t; real_coef_.SetTime(t); imag_coef_.SetTime(t); }
/// Get the time for time dependent coefficients
real_t GetTime() { return time; }
/** @brief Evaluate the coefficient in the element described by @a T at the
point @a ip. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual complex_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
/** @brief Evaluate the coefficient in the element described by @a T at the
point @a ip at time @a t. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
complex_t Eval(ElementTransformation &T,
const IntegrationPoint &ip, real_t t)
{
SetTime(t);
return Eval(T, ip);
}
/** @brief Access a standard Coefficient object reproducing the real part of
the complex-valued field */
/** @note By default this method returns an internal object which
computes the complex value using the above Eval method and
returns its real part. Custom implementations may choose to
override this method with a more efficient real-valued
coefficient. */
virtual Coefficient & real() { return real_coef_; }
/** @brief Access a standard Coefficient object reproducing the imaginary
part of the complex-valued field */
/** @note By default this method returns an internal object which
computes the complex value using the above Eval method and
returns its imaginary part. Custom implementations may choose to
override this method with a more efficient real-valued
coefficient. */
virtual Coefficient & imag() { return imag_coef_; }
virtual ~ComplexCoefficient() { }
};
/** @brief Base class ComplexVectorCoefficients that optionally depend
on space and time. These are used by the SesquilinearForm,
ComplexLinearForm, and ComplexGridFunction classes to represent
the physical vector-valued coefficients in the PDEs that are being
discretized. This class can also be used in a more general way to
represent functions that don't necessarily belong to a FE space,
e.g., to project onto ComplexGridFunctions to use as initial
conditions, exact solutions, etc. See, e.g., ex22 for these
uses. */
class ComplexVectorCoefficient
{
protected:
int vdim;
real_t time;
private:
RealPartVectorCoefficient re_part_vcoef_;
ImagPartVectorCoefficient im_part_vcoef_;
protected:
VectorCoefficient &real_vcoef_;
VectorCoefficient &imag_vcoef_;
mutable Vector V_r_;
mutable Vector V_i_;
public:
ComplexVectorCoefficient(int vd)
: vdim(vd), time(0.),
re_part_vcoef_(*this), im_part_vcoef_(*this),
real_vcoef_(re_part_vcoef_), imag_vcoef_(im_part_vcoef_)
{ }
ComplexVectorCoefficient(VectorCoefficient &v_r, VectorCoefficient &v_i);
/// Set the time for time dependent coefficients
virtual void SetTime(real_t t)
{ time = t; real_vcoef_.SetTime(t); imag_vcoef_.SetTime(t); }
/// Get the time for time dependent coefficients
real_t GetTime() { return time; }
/// Returns dimension of the vector.
int GetVDim() { return vdim; }
/** @brief Evaluate the vector coefficient in the element described by @a T
at the point @a ip, storing the result in @a V. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual void Eval(ComplexVector &V, ElementTransformation &T,
const IntegrationPoint &ip);
/** @brief Evaluate the vector coefficient in the element described by @a T
at the point @a ip at time @a t, storing the result in @a V. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
void Eval(ComplexVector &V, ElementTransformation &T,
const IntegrationPoint &ip, real_t t)
{
SetTime(t);
Eval(V, T, ip);
}
/** @brief Access a standard Coefficient object reproducing the real part of
the complex-valued field */
/** @note By default this method returns an internal object which
computes the complex value using the above Eval method and
returns its real part. Custom implementations may choose to
override this method with a more efficient real-valued
coefficient. */
virtual VectorCoefficient & real() { return real_vcoef_; }
/** @brief Access a standard Coefficient object reproducing the imaginary
part of the complex-valued field */
/** @note By default this method returns an internal object which
computes the complex value using the above Eval method and
returns its imaginary part. Custom implementations may choose to
override this method with a more efficient real-valued
coefficient. */
virtual VectorCoefficient & imag() { return imag_vcoef_; }
virtual ~ComplexVectorCoefficient() { }
};
/** @brief Base class ComplexMatrixCoefficients that optionally depend
on space and time. These are used by the SesquilinearForm,
ComplexLinearForm, and ComplexGridFunction classes to represent
the physical matrix-valued coefficients in the PDEs that are being
discretized. This class can also be used in a more general way to
represent functions that don't necessarily belong to a FE space.
See, e.g., ex22 for these uses. */
class ComplexMatrixCoefficient
{
protected:
int height, width;
real_t time;
private:
RealPartMatrixCoefficient re_part_mcoef_;
ImagPartMatrixCoefficient im_part_mcoef_;
protected:
MatrixCoefficient &real_mcoef_;
MatrixCoefficient &imag_mcoef_;
mutable DenseMatrix M_r_;
mutable DenseMatrix M_i_;
public:
/// Construct a dim x dim matrix coefficient.
explicit ComplexMatrixCoefficient(int dim)
: height(dim), width(dim), time(0.),
re_part_mcoef_(*this), im_part_mcoef_(*this),
real_mcoef_(re_part_mcoef_), imag_mcoef_(im_part_mcoef_)
{ }
/// Construct a h x w matrix coefficient.
ComplexMatrixCoefficient(int h, int w) :
height(h), width(w), time(0.),
re_part_mcoef_(*this), im_part_mcoef_(*this),
real_mcoef_(re_part_mcoef_), imag_mcoef_(im_part_mcoef_)
{ }
/// Set the time for time dependent coefficients
virtual void SetTime(real_t t) { time = t; }
/// Get the time for time dependent coefficients
real_t GetTime() { return time; }
/// Get the height of the matrix.
int GetHeight() const { return height; }
/// Get the width of the matrix.
int GetWidth() const { return width; }
/// For backward compatibility get the width of the matrix.
int GetVDim() const { return width; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
at the point @a ip, storing the result in @a K. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual void Eval(ComplexTypeDenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) = 0;
/** @brief Access a standard Coefficient object reproducing the real part of
the complex-valued field */
/** @note By default this method returns an internal object which
computes the complex value using the above Eval method and
returns its real part. Custom implementations may choose to
override this method with a more efficient real-valued
coefficient. */
virtual MatrixCoefficient & real() { return real_mcoef_; }
/** @brief Access a standard Coefficient object reproducing the imaginary
part of the complex-valued field */
/** @note By default this method returns an internal object which
computes the complex value using the above Eval method and
returns its imaginary part. Custom implementations may choose to
override this method with a more efficient real-valued
coefficient. */
virtual MatrixCoefficient & imag() { return imag_mcoef_; }
virtual ~ComplexMatrixCoefficient() { }
};
/// A complex-valued coefficient that is constant across space and time
class ComplexConstantCoefficient : public ComplexCoefficient
{
private:
complex_t val;
ConstantCoefficient real_coef;
ConstantCoefficient imag_coef;
public:
ComplexConstantCoefficient(const complex_t z);
ComplexConstantCoefficient(real_t z_r, real_t z_i = 0.);
complex_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) { return val; }
};
/// Complex-valued vector coefficient that is constant in space and time.
class ComplexVectorConstantCoefficient : public ComplexVectorCoefficient
{
private:
ComplexVector vec;
public:
/// Construct the coefficient with constant vector @a v.
ComplexVectorConstantCoefficient(const ComplexVector &v)
: ComplexVectorCoefficient(v.Size()), vec(v) { }
/// Construct the coefficient with constant vector @a v.
ComplexVectorConstantCoefficient(const Vector &v)
: ComplexVectorCoefficient(v.Size()), vec(v) { }
using ComplexVectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
void Eval(ComplexVector &V, ElementTransformation &T,
const IntegrationPoint &ip) override { V = vec; }
/// Return a reference to the constant vector in this class.
const ComplexVector& GetVec() const { return vec; }
};
/// Complex-valued vector coefficient that is constant in space and time.
class ComplexMatrixConstantCoefficient : public ComplexMatrixCoefficient
{
private:
ComplexTypeDenseMatrix mat;
public:
/// Construct the coefficient with constant vector @a v.
ComplexMatrixConstantCoefficient(const ComplexTypeDenseMatrix &m)
: ComplexMatrixCoefficient(m.Height(), m.Width()), mat(m) { }
/// Construct the coefficient with constant vector @a v.
ComplexMatrixConstantCoefficient(const DenseMatrix &m)
: ComplexMatrixCoefficient(m.Height(), m.Width()), mat(m) { }
using ComplexMatrixCoefficient::Eval;
/// Evaluate the matrix coefficient at @a ip.
void Eval(ComplexTypeDenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override { M = mat; }
/// Return a reference to the constant matrix in this class.
const ComplexTypeDenseMatrix& GetMat() const { return mat; }
};
/// A general complex-valued function coefficient
class ComplexFunctionCoefficient : public ComplexCoefficient
{
protected:
std::function<complex_t(const Vector &)> Function;
std::function<complex_t(const Vector &, real_t)> TDFunction;
public:
/// Define a time-independent coefficient from a std function
/** \param F time-independent std::function */
ComplexFunctionCoefficient(std::function<complex_t
(const Vector &)> F)
: Function(std::move(F))
{ }
/// Define a time-dependent coefficient from a std function
/** \param TDF time-dependent function */
ComplexFunctionCoefficient(std::function<complex_t
(const Vector &, real_t)> TDF)
: TDFunction(std::move(TDF))
{ }
/// (DEPRECATED) Define a time-independent coefficient from a C-function
/** @deprecated Use the method where the C-function, @a f, uses a const
Vector argument instead of Vector. */
MFEM_DEPRECATED ComplexFunctionCoefficient(complex_t
(*f)(Vector &))
{
// Cast first to (void*) to suppress a warning from newer version of
// Clang when using -Wextra.
Function = reinterpret_cast<complex_t(*)
(const Vector&)>((void*)f);
TDFunction = NULL;
}
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
/** @deprecated Use the method where the C-function, @a tdf, uses a const
Vector argument instead of Vector. */
MFEM_DEPRECATED ComplexFunctionCoefficient(complex_t
(*tdf)(Vector &, real_t))
{
Function = NULL;
// Cast first to (void*) to suppress a warning from newer version of
// Clang when using -Wextra.
TDFunction =
reinterpret_cast<complex_t(*)(const Vector&,
real_t)>((void*)tdf);
}
/// Evaluate the coefficient at @a ip.
complex_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
};
/// A general vector function coefficient
class ComplexVectorFunctionCoefficient : public ComplexVectorCoefficient
{
private:
std::function<void(const Vector &, ComplexVector &)> Function;
std::function<void(const Vector &, real_t, ComplexVector &)> TDFunction;
ComplexCoefficient *Q;
public:
/// Define a time-independent complex-valued vector coefficient
/// from a std function
/** \param dim - the size of the vector
\param F - time-independent function
\param q - optional scalar Coefficient to scale the vector coefficient */
ComplexVectorFunctionCoefficient(int dim,
std::function<void(const Vector &,
ComplexVector &)> F,
ComplexCoefficient *q = nullptr)
: ComplexVectorCoefficient(dim), Function(std::move(F)), Q(q)
{ }
/// Define a time-dependent complex-valued vector coefficient from
/// a std function
/** \param dim - the size of the vector
\param TDF - time-dependent function
\param q - optional scalar ComplexCoefficient to scale the vector coefficient */
ComplexVectorFunctionCoefficient(int dim,
std::function<void(const Vector &, real_t,
ComplexVector &)> TDF,
ComplexCoefficient *q = nullptr)
: ComplexVectorCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
using ComplexVectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
void Eval(ComplexVector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual ~ComplexVectorFunctionCoefficient() { }
};
} // end namespace mfem
#endif
+240
View File
@@ -96,6 +96,23 @@ ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff)
{
gfr->SyncMemory(*this);
gfi->SyncMemory(*this);
gfr->ProjectCoefficient(real_coeff);
*gfi = 0.0;
gfr->SyncAliasMemory(*this);
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectCoefficient(ComplexCoefficient &coeff)
{
this->ProjectCoefficient(coeff.real(), coeff.imag());
}
void
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff)
@@ -108,6 +125,23 @@ ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff)
{
gfr->SyncMemory(*this);
gfi->SyncMemory(*this);
gfr->ProjectCoefficient(real_vcoeff);
*gfi = 0.0;
gfr->SyncAliasMemory(*this);
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectCoefficient(ComplexVectorCoefficient &vcoeff)
{
this->ProjectCoefficient(vcoeff.real(), vcoeff.imag());
}
void
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
@@ -121,6 +155,26 @@ ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Array<int> &attr)
{
ConstantCoefficient zero_coeff(0.0);
gfr->SyncMemory(*this);
gfi->SyncMemory(*this);
gfr->ProjectBdrCoefficient(real_coeff, attr);
gfi->ProjectBdrCoefficient(zero_coeff, attr);
gfr->SyncAliasMemory(*this);
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectBdrCoefficient(ComplexCoefficient &coeff,
Array<int> &attr)
{
this->ProjectBdrCoefficient(coeff.real(), coeff.imag(), attr);
}
void
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff,
@@ -134,6 +188,28 @@ ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
Array<int> &attr)
{
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
VectorConstantCoefficient zero_vcoeff(zero_vec);
gfr->SyncMemory(*this);
gfi->SyncMemory(*this);
gfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
gfi->ProjectBdrCoefficientNormal(zero_vcoeff, attr);
gfr->SyncAliasMemory(*this);
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectBdrCoefficientNormal(
ComplexVectorCoefficient &vcoeff,
Array<int> &attr)
{
this->ProjectBdrCoefficientNormal(vcoeff.real(), vcoeff.imag(), attr);
}
void
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&real_vcoeff,
@@ -149,6 +225,80 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&real_vcoeff,
Array<int> &attr)
{
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
VectorConstantCoefficient zero_vcoeff(zero_vec);
gfr->SyncMemory(*this);
gfi->SyncMemory(*this);
gfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
gfi->ProjectBdrCoefficientTangent(zero_vcoeff, attr);
gfr->SyncAliasMemory(*this);
gfi->SyncAliasMemory(*this);
}
void
ComplexGridFunction::ProjectBdrCoefficientTangent(
ComplexVectorCoefficient &vcoeff,
Array<int> &attr)
{
this->ProjectBdrCoefficientTangent(vcoeff.real(), vcoeff.imag(), attr);
}
real_t
ComplexGridFunction::ComputeL2Error(Coefficient &re_exsol,
Coefficient &im_exsol,
const IntegrationRule *irs[],
const Array<int> *elems) const
{
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
real_t err_i = gfi->ComputeL2Error(im_exsol, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
real_t
ComplexGridFunction::ComputeL2Error(Coefficient &re_exsol,
const IntegrationRule *irs[],
const Array<int> *elems) const
{
ConstantCoefficient zero_coef(0.0);
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
real_t err_i = gfi->ComputeL2Error(zero_coef, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
real_t
ComplexGridFunction::ComputeL2Error(VectorCoefficient &re_exsol,
VectorCoefficient &im_exsol,
const IntegrationRule *irs[],
const Array<int> *elems) const
{
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
real_t err_i = gfi->ComputeL2Error(im_exsol, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
real_t
ComplexGridFunction::ComputeL2Error(VectorCoefficient &re_exsol,
const IntegrationRule *irs[],
const Array<int> *elems) const
{
Vector zero_vec(re_exsol.GetVDim()); zero_vec = 0.0;
VectorConstantCoefficient zero_coef(zero_vec);
real_t err_r = gfr->ComputeL2Error(re_exsol, irs, elems);
real_t err_i = gfi->ComputeL2Error(zero_coef, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention convention)
@@ -731,6 +881,17 @@ ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff)
{
pgfr->SyncMemory(*this);
pgfi->SyncMemory(*this);
pgfr->ProjectCoefficient(real_coeff);
*pgfi = 0.0;
pgfr->SyncAliasMemory(*this);
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff)
@@ -743,6 +904,17 @@ ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff)
{
pgfr->SyncMemory(*this);
pgfi->SyncMemory(*this);
pgfr->ProjectCoefficient(real_vcoeff);
*pgfi = 0.0;
pgfr->SyncAliasMemory(*this);
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
@@ -756,6 +928,19 @@ ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Array<int> &attr)
{
ConstantCoefficient zero_coeff(0.0);
pgfr->SyncMemory(*this);
pgfi->SyncMemory(*this);
pgfr->ProjectBdrCoefficient(real_coeff, attr);
pgfi->ProjectBdrCoefficient(zero_coeff, attr);
pgfr->SyncAliasMemory(*this);
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
&real_vcoeff,
@@ -771,6 +956,21 @@ ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
&real_vcoeff,
Array<int> &attr)
{
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
VectorConstantCoefficient zero_vcoeff(zero_vec);
pgfr->SyncMemory(*this);
pgfi->SyncMemory(*this);
pgfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
pgfi->ProjectBdrCoefficientNormal(zero_vcoeff, attr);
pgfr->SyncAliasMemory(*this);
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&real_vcoeff,
@@ -786,6 +986,21 @@ ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&real_vcoeff,
Array<int> &attr)
{
Vector zero_vec(real_vcoeff.GetVDim()); zero_vec = 0.;
VectorConstantCoefficient zero_vcoeff(zero_vec);
pgfr->SyncMemory(*this);
pgfi->SyncMemory(*this);
pgfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
pgfi->ProjectBdrCoefficientTangent(zero_vcoeff, attr);
pgfr->SyncAliasMemory(*this);
pgfi->SyncAliasMemory(*this);
}
void
ParComplexGridFunction::Distribute(const Vector *tv)
{
@@ -825,6 +1040,31 @@ ParComplexGridFunction::ParallelProject(Vector &tv) const
tvi.SyncAliasMemory(tv);
}
real_t
ParComplexGridFunction::ComputeL2Error(Coefficient &exsolr,
const IntegrationRule *irs[],
Array<int> *elems) const
{
ConstantCoefficient zeroCoef(0.0);
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
real_t err_i = pgfi->ComputeL2Error(zeroCoef, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
real_t
ParComplexGridFunction::ComputeL2Error(VectorCoefficient &exsolr,
const IntegrationRule *irs[],
Array<int> *elems) const
{
Vector zeroVec(exsolr.GetVDim()); zeroVec = 0.0;
VectorConstantCoefficient zeroCoef(zeroVec);
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
real_t err_i = pgfi->ComputeL2Error(zeroCoef, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
ComplexOperator::Convention
+1307 -21
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -912,7 +912,7 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
if (vdim == 1) // scalar case
{
n_field["values"].set_external(const_cast<real_t *>(gf->HostRead()),
n_field["values"].set_external(gf->GetData(),
ndofs);
}
else // vector case
@@ -925,18 +925,18 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
int vdim_stride = (ordering == Ordering::byNODES ? ndofs : 1);
index_t offset = 0;
index_t stride = sizeof(real_t) * entry_stride;
index_t stride = sizeof(double) * entry_stride;
for (int d = 0; d < vdim; d++)
{
std::ostringstream oss;
oss << "v" << d;
std::string comp_name = oss.str();
n_field["values"][comp_name].set_external(const_cast<real_t *>(gf->HostRead()),
n_field["values"][comp_name].set_external(gf->GetData(),
ndofs,
offset,
stride);
offset += sizeof(real_t) * vdim_stride;
offset += sizeof(double) * vdim_stride;
}
}
-266
View File
@@ -1,266 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "derefmat_op.hpp"
#include "fes_kernels.hpp"
/// \cond DO_NOT_DOCUMENT
namespace mfem
{
namespace internal
{
template <Ordering::Type Order, bool Atomic>
static void DerefMultKernelImpl(const DerefineMatrixOp &op, const Vector &x,
Vector &y)
{
DerefineMatrixOpMultFunctor<Order, Atomic> func;
func.xptr = x.Read();
y.UseDevice();
y = 0.;
func.yptr = y.ReadWrite();
func.bsptr = op.block_storage.Read();
func.boptr = op.block_offsets.Read();
func.brptr = op.block_row_idcs_offsets.Read();
func.bcptr = op.block_col_idcs_offsets.Read();
func.rptr = op.row_idcs.Read();
func.cptr = op.col_idcs.Read();
func.vdims = op.fespace->GetVDim();
func.nblocks = op.block_offsets.Size();
func.width = op.Width() / func.vdims;
func.height = op.Height() / func.vdims;
func.Run(op.max_rows);
}
} // namespace internal
DerefineMatrixOp::DerefineMatrixOp(FiniteElementSpace &fespace_, int old_ndofs,
const Table *old_elem_dof,
const Table *old_elem_fos)
: Operator(fespace_.GetVSize(), old_ndofs * fespace_.GetVDim()),
fespace(&fespace_)
{
static Kernels kernels;
constexpr int max_team_size = 256;
/// TODO: Implement DofTransformation support
MFEM_VERIFY(fespace->Nonconforming(),
"Not implemented for conforming meshes.");
MFEM_VERIFY(old_ndofs, "Missing previous (finer) space.");
MFEM_VERIFY(fespace->GetNDofs() <= old_ndofs,
"Previous space is not finer.");
const CoarseFineTransformations &dtrans =
fespace->GetMesh()->ncmesh->GetDerefinementTransforms();
MFEM_ASSERT(dtrans.embeddings.Size() == old_elem_dof->Size(), "");
const bool is_dg = fespace->FEColl()->GetContType()
== FiniteElementCollection::DISCONTINUOUS;
DenseMatrix localRVO; // for variable-order only
DenseTensor localR[Geometry::NumGeom];
int total_rows = 0;
int total_cols = 0;
block_offsets.SetSize(dtrans.embeddings.Size());
block_offsets.HostWrite();
if (fespace->IsVariableOrder())
{
// TODO: any potential for some compression here?
// determine storage size and offsets
block_offsets[0] = 0;
int total_size = 0;
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
{
const Embedding &emb = dtrans.embeddings[k];
const FiniteElement *fe = fespace->GetFE(emb.parent);
const int ldof = fe->GetDof();
if (k + 1 < dtrans.embeddings.Size())
{
block_offsets[k + 1] = block_offsets[k] + ldof * ldof;
}
total_rows += ldof;
total_cols += ldof;
total_size += ldof * ldof;
}
block_storage.SetSize(total_size);
}
else
{
// compression scheme:
// block_offsets is the start of each block, potentially repeated
// only need to store localR for used shapes
Mesh::GeometryList elem_geoms(*fespace->GetMesh());
int geom_offsets[Geometry::NumGeom];
{
int size = 0;
for (int i = 0; i < elem_geoms.Size(); ++i)
{
fespace->GetLocalDerefinementMatrices(elem_geoms[i],
localR[elem_geoms[i]]);
geom_offsets[elem_geoms[i]] = size;
size += localR[elem_geoms[i]].TotalSize();
}
block_storage.SetSize(size);
// copy blocks into block_storage
auto bs_ptr = block_storage.HostWrite();
for (int i = 0; i < elem_geoms.Size(); ++i)
{
std::copy(localR[elem_geoms[i]].Data(),
localR[elem_geoms[i]].Data()
+ localR[elem_geoms[i]].TotalSize(),
bs_ptr);
bs_ptr += localR[elem_geoms[i]].TotalSize();
}
}
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
{
const Embedding &emb = dtrans.embeddings[k];
Geometry::Type geom =
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
auto size = localR[geom].SizeI() * localR[geom].SizeJ();
total_rows += localR[geom].SizeI();
total_cols += localR[geom].SizeJ();
// set block offsets and sizes
block_offsets[k] = geom_offsets[geom] + size * emb.matrix;
}
}
row_idcs.SetSize(total_rows);
row_idcs.HostWrite();
col_idcs.SetSize(total_cols);
col_idcs.HostWrite();
block_row_idcs_offsets.SetSize(dtrans.embeddings.Size() + 1);
block_row_idcs_offsets.HostWrite();
block_col_idcs_offsets.SetSize(dtrans.embeddings.Size() + 1);
block_col_idcs_offsets.HostWrite();
block_row_idcs_offsets[0] = 0;
block_col_idcs_offsets[0] = 0;
// compute index information
Array<int> dofs, old_dofs;
max_rows = 1;
{
Array<int> mark(fespace->GetNDofs());
mark = 0;
auto bs_ptr = block_storage.HostWrite();
int ridx = 0;
int cidx = 0;
int num_marked = 0;
for (int k = 0; k < dtrans.embeddings.Size(); k++)
{
const Embedding &emb = dtrans.embeddings[k];
Geometry::Type geom =
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
if (fespace->IsVariableOrder())
{
const FiniteElement *fe = fespace->GetFE(emb.parent);
const DenseTensor &pmats = dtrans.point_matrices[geom];
const int ldof = fe->GetDof();
IsoparametricTransformation isotr;
isotr.SetIdentityTransformation(geom);
localRVO.SetSize(ldof, ldof);
isotr.SetPointMat(pmats(emb.matrix));
// Local restriction is size ldofxldof assuming that the parent
// and child are of same polynomial order.
fe->GetLocalRestriction(isotr, localRVO);
// copy block
auto size = localRVO.Height() * localRVO.Width();
std::copy(localRVO.Data(), localRVO.Data() + size, bs_ptr);
bs_ptr += size;
}
DenseMatrix &lR =
fespace->IsVariableOrder() ? localRVO : localR[geom](emb.matrix);
block_row_idcs_offsets[k + 1] =
block_row_idcs_offsets[k] + lR.Height();
block_col_idcs_offsets[k + 1] = block_col_idcs_offsets[k] + lR.Width();
max_rows = std::max(lR.Height(), max_rows);
// index information
fespace->elem_dof->GetRow(emb.parent, dofs);
old_elem_dof->GetRow(k, old_dofs);
MFEM_VERIFY(old_dofs.Size() == dofs.Size(),
"Parent and child must have same #dofs.");
for (int i = 0; i < lR.Height(); ++i, ++ridx)
{
if (!std::isfinite(lR(i, 0)))
{
row_idcs[ridx] = INT_MAX;
continue;
}
int r = dofs[i];
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
row_idcs[ridx] = r;
mark[m] = 1;
++num_marked;
}
else
{
row_idcs[ridx] = INT_MAX;
}
}
for (int i = 0; i < lR.Width(); ++i, ++cidx)
{
col_idcs[cidx] = old_dofs[i];
}
}
if (!is_dg && !fespace->IsVariableOrder())
{
MFEM_VERIFY(num_marked * fespace->GetVDim() == Height(),
"internal error: not all rows were set.");
}
}
// if not using GPU, set max_rows/max_cols to zero
if (Device::Allows(Backend::DEVICE_MASK))
{
max_rows = std::min(max_rows, max_team_size);
}
else
{
max_rows = 1;
}
}
void DerefineMatrixOp::Mult(const Vector &x, Vector &y) const
{
const bool is_dg = fespace->FEColl()->GetContType()
== FiniteElementCollection::DISCONTINUOUS;
// DG needs atomic summation
MultKernel::Run(fespace->GetOrdering(), is_dg, *this, x, y);
}
DerefineMatrixOp::Kernels::Kernels()
{
MultKernel::Specialization<Ordering::byNODES, false>::Add();
MultKernel::Specialization<Ordering::byVDIM, false>::Add();
MultKernel::Specialization<Ordering::byNODES, true>::Add();
MultKernel::Specialization<Ordering::byVDIM, true>::Add();
}
template <Ordering::Type Order, bool Atomic>
DerefineMatrixOp::MultKernelType DerefineMatrixOp::MultKernel::Kernel()
{
return internal::DerefMultKernelImpl<Order, Atomic>;
}
DerefineMatrixOp::MultKernelType
DerefineMatrixOp::MultKernel::Fallback(Ordering::Type, bool)
{
MFEM_ABORT("invalid MultKernel parameters");
}
} // namespace mfem
/// \endcond DO_NOT_DOCUMENT
-65
View File
@@ -1,65 +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_DEREFMAT_OP
#define MFEM_DEREFMAT_OP
#include "fespace.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
struct DerefineMatrixOp : public Operator
{
FiniteElementSpace *fespace;
/// offsets into block_storage
Array<int> block_offsets;
/// offsets into row_idcs
Array<int> block_row_idcs_offsets;
/// offsets into col_idcs
Array<int> block_col_idcs_offsets;
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
/// negative means the row data should be negated.
Array<int> row_idcs;
/// mapping for col dofs, negative means the col data should be negated.
Array<int> col_idcs;
/// dense block matrices which can be reused to construct the full matrix
/// operation. These are stored contiguously and blocks have no restrictions
/// on shape (can be rectangle and differ from block to block).
Vector block_storage;
/// maximum height of any block in block_storage for GPU
/// parallelization, or 1 for CPU runs.
int max_rows;
using MultKernelType = void (*)(const DerefineMatrixOp &, const Vector &,
Vector &);
/// template args: ordering, atomic
MFEM_REGISTER_KERNELS(MultKernel, MultKernelType, (Ordering::Type, bool));
struct Kernels
{
Kernels();
};
void Mult(const Vector &x, Vector &y) const;
DerefineMatrixOp(FiniteElementSpace &fespace_, int old_ndofs,
const Table *old_elem_dof, const Table *old_elem_fos);
};
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
+4 -4
View File
@@ -327,8 +327,8 @@ void print_mpi_sync(const std::string& msg)
// First gather string lengths
size_t msg_len = msg.length();
std::vector<size_t> lengths(nranks);
MPI_Gather(&msg_len, 1, MPITypeMap<size_t>::mpi_type,
lengths.data(), 1, MPITypeMap<size_t>::mpi_type,
MPI_Gather(&msg_len, 1, MPI_INT,
lengths.data(), 1, MPI_INT,
0, MPI_COMM_WORLD);
if (myrank == 0)
@@ -568,7 +568,7 @@ struct ThreadBlocks
int z = 1;
};
#if defined(MFEM_USE_CUDA_OR_HIP)
#if (defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
template <typename func_t>
__global__ void forall_kernel_shmem(func_t f, int n)
{
@@ -591,7 +591,7 @@ void forall(func_t f,
if (Device::Allows(Backend::CUDA_MASK) ||
Device::Allows(Backend::HIP_MASK))
{
#if defined(MFEM_USE_CUDA_OR_HIP)
#if (defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
// int gridsize = (N + Z - 1) / Z;
int num_bytes = num_shmem * sizeof(decltype(shmem));
dim3 block_size(blocks.x, blocks.y, blocks.z);
-249
View File
@@ -1,249 +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_FES_KERNELS_HPP
#define MFEM_FES_KERNELS_HPP
#include "../general/forall.hpp"
#include <climits>
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
///
/// Implements matrix-vector multiply $y = A x$ for a sparse matrix composed of
/// a sum of smaller dense blocks. There is additional permutation/sign
/// information associated with each block. The base class only implements
/// helper routines such as computing block widths, index into x, index into y,
/// and column in A given sub-block information.
/// @sa DerefineMatrixOpMultFunctor
///
/// @tparam Order vdim ordering for x and y. Note that for Diag = false this is
/// ignored for x as x has a special interleaved order.
/// @tparam Base used for the curious recurring template pattern (CRTP) so the
/// base class can access child class fields without virtual functions
/// @tparam Diag true if this corresponds to the diagonal block (coarse element
/// and fine element are on our rank), false otherwise (coarse element is on our
/// rank, fine element is on a different rank).
///
template <Ordering::Type Order, class Base, bool Diag = true>
struct DerefineMatrixOpFunctorBase;
template <class Base>
struct DerefineMatrixOpFunctorBase<Ordering::byNODES, Base, true>
{
/// block column indices offsets
const int *bcptr;
/// column indices
const int *cptr;
int MFEM_HOST_DEVICE BlockWidth(int k) const
{
return bcptr[k + 1] - bcptr[k];
}
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
{
col = cptr[bcptr[k] + j];
if (col < 0)
{
col = -1 - col;
sign = -sign;
}
}
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int) const
{
return col + vdim * static_cast<const Base *>(this)->width;
}
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
{
return row + vdim * static_cast<const Base *>(this)->height;
}
};
template <class Base>
struct DerefineMatrixOpFunctorBase<Ordering::byVDIM, Base, true>
{
/// block column indices offsets
const int *bcptr;
/// column indices
const int *cptr;
int MFEM_HOST_DEVICE BlockWidth(int k) const
{
return bcptr[k + 1] - bcptr[k];
}
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
{
col = cptr[bcptr[k] + j];
if (col < 0)
{
col = -1 - col;
sign = -sign;
}
}
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int) const
{
return vdim + col * static_cast<const Base *>(this)->vdims;
}
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
{
return vdim + row * static_cast<const Base *>(this)->vdims;
}
};
template <class Base>
struct DerefineMatrixOpFunctorBase<Ordering::byNODES, Base, false>
{
/// receive segment offsets
const int *segptr;
/// receive segment index
const int *rsptr;
/// off-diagonal block column offsets
const int *coptr;
/// off-diagonal block widths
const int *bwptr;
int MFEM_HOST_DEVICE BlockWidth(int k) const { return bwptr[k]; }
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
{
col = coptr[k] + j;
}
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int k) const
{
int tmp = rsptr[k];
int segwidth = segptr[tmp + 1] - segptr[tmp];
return segptr[tmp] * static_cast<const Base *>(this)->vdims + col +
vdim * segwidth;
}
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
{
return row + vdim * static_cast<const Base *>(this)->height;
}
};
template <class Base>
struct DerefineMatrixOpFunctorBase<Ordering::byVDIM, Base, false>
{
/// receive segment offsets
const int *segptr;
/// receive segment index
const int *rsptr;
/// off-diagonal block column offsets
const int *coptr;
/// off-diagonal block widths
const int *bwptr;
int MFEM_HOST_DEVICE BlockWidth(int k) const { return bwptr[k]; }
void MFEM_HOST_DEVICE Col(int j, int k, int &col, int &sign) const
{
col = coptr[k] + j;
}
int MFEM_HOST_DEVICE IndexX(int col, int vdim, int k) const
{
int tmp = rsptr[k];
int segwidth = segptr[tmp + 1] - segptr[tmp];
return segptr[tmp] * static_cast<const Base *>(this)->vdims + col +
vdim * segwidth;
}
int MFEM_HOST_DEVICE IndexY(int row, int vdim) const
{
return vdim + row * static_cast<const Base *>(this)->vdims;
}
};
/// internally used to implement the derefinement operator Mult diagonal
/// block
template <Ordering::Type Order, bool Atomic, bool Diag = true>
struct DerefineMatrixOpMultFunctor
: public DerefineMatrixOpFunctorBase<
Order, DerefineMatrixOpMultFunctor<Order, Atomic, Diag>, Diag>
{
const real_t *xptr;
real_t *yptr;
/// block storage
const real_t *bsptr;
/// block offsets
const int *boptr;
/// block row index offsets
const int *brptr;
/// row indices
const int *rptr;
// number of blocks
int nblocks;
// number of components
int vdims;
/// overall operator height (for vdim = 1)
int height;
/// overall operator width (for vdim = 1)
int width;
void MFEM_HOST_DEVICE operator()(int kidx) const
{
int k = kidx % nblocks;
int vdim = kidx / nblocks;
int block_height = brptr[k + 1] - brptr[k];
int block_width = this->BlockWidth(k);
MFEM_FOREACH_THREAD(i, x, block_height)
{
int row = rptr[brptr[k] + i];
int rsign = 1;
if (row < 0)
{
row = -1 - row;
rsign = -1;
}
if (row < INT_MAX)
{
// row not marked as unused
real_t sum = 0;
for (int j = 0; j < block_width; ++j)
{
int col, sign = rsign;
this->Col(j, k, col, sign);
sum += sign * bsptr[boptr[k] + i + j * block_height] *
xptr[this->IndexX(col, vdim, k)];
}
#if defined(__CUDA_ARCH__) or defined(__HIP_DEVICE_COMPILE__)
if (Atomic)
{
atomicAdd(yptr + this->IndexY(row, vdim), sum);
}
else
#endif
{
yptr[this->IndexY(row, vdim)] += sum;
}
}
}
}
/// N is the max block row size (doesn't have to be a power of 2)
void Run(int N) const { forall_2D(nblocks * vdims, N, 1, *this); }
};
} // namespace internal
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
+7 -21
View File
@@ -17,9 +17,6 @@
#include "fem.hpp"
#include "ceed/interface/util.hpp"
#include "derefmat_op.hpp"
#include <algorithm>
#include <cmath>
#include <cstdarg>
@@ -27,9 +24,9 @@ using namespace std;
namespace mfem
{
template <>
void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
Array<int> &dofs)
template <> void Ordering::
DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim, Array<int> &dofs)
{
// static method
int size = dofs.Size();
@@ -43,9 +40,8 @@ void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
}
}
template <>
void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
Array<int> &dofs)
template <> void Ordering::
DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim, Array<int> &dofs)
{
// static method
int size = dofs.Size();
@@ -59,6 +55,7 @@ void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
}
}
FiniteElementSpace::FiniteElementSpace()
: mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0),
@@ -101,10 +98,7 @@ FiniteElementSpace::FiniteElementSpace(const FiniteElementSpace &orig,
FiniteElementSpace::FiniteElementSpace(Mesh *mesh,
const FiniteElementCollection *fec,
int vdim, int ordering)
{
MFEM_PERF_FUNCTION;
Constructor(mesh, NULL, fec, vdim, ordering);
}
{ Constructor(mesh, NULL, fec, vdim, ordering); }
FiniteElementSpace::FiniteElementSpace(Mesh *mesh, NURBSExtension *ext,
const FiniteElementCollection *fec,
@@ -396,8 +390,6 @@ void FiniteElementSpace::BuildElementToDofTable() const
{
if (elem_dof) { return; }
MFEM_PERF_FUNCTION;
// TODO: can we call GetElementDofs only once per element?
Table *el_dof = new Table;
Table *el_fos = (mesh->Dimension() > 2) ? (new Table) : NULL;
@@ -2753,8 +2745,6 @@ void FiniteElementSpace::BuildNURBSFaceToDofTable() const
void FiniteElementSpace::Construct()
{
MFEM_PERF_FUNCTION;
// This method should be used only for non-NURBS spaces.
MFEM_VERIFY(!NURBSext, "internal error");
@@ -4254,11 +4244,7 @@ void FiniteElementSpace::Update(bool want_transform)
case Mesh::DEREFINE:
{
BuildConformingInterpolation();
#if 0
Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
#else
Th.Reset(new DerefineMatrixOp(*this, old_ndofs, old_elem_dof, old_elem_fos));
#endif
if (IsVariableOrder())
{
if (cP && cR_hp)
+1 -2
View File
@@ -113,7 +113,7 @@ class QuadratureSpace;
class QuadratureInterpolator;
class FaceQuadratureInterpolator;
class PRefinementTransferOperator;
struct DerefineMatrixOp;
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
mesh, mainly managing the set of degrees of freedom.
@@ -246,7 +246,6 @@ class FiniteElementSpace
friend class PRefinementTransferOperator;
friend void Mesh::Swap(Mesh &, bool);
friend class LORBase;
friend struct DerefineMatrixOp;
protected:
/// The mesh that FE space lives on (not owned).
+1 -120
View File
@@ -19,7 +19,6 @@
#include "../mesh/nurbs.hpp"
#include "../mesh/vtkhdf.hpp"
#include "../general/text.hpp"
#include "../general/reducers.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
@@ -3327,126 +3326,8 @@ real_t GridFunction::ComputeLpError(const real_t p, Coefficient &exsol,
const IntegrationRule *irs[],
const Array<int> *elems) const
{
MFEM_PERF_FUNCTION;
MFEM_VERIFY(fes->GetVDim() == 1, "invalid vector dimension!");
real_t error = 0.0;
bool device_eval = true;
// TODO: check for cases that are not supported on device:
// * mixed meshes
// * meshes with non-tensor-product elements can have negative weights
// * variable orders
// * weight is not NULL
// * elems is not NULL
// * map type is not VALUE
// * ...
Mesh *mesh = fes->GetMesh();
const FiniteElement *fe = fes->GetTypicalFE();
if (mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
(mesh->Dimension() > 1 && mesh->MeshGenerator() != 2) ||
fes->IsVariableOrder() ||
weight != nullptr ||
elems != nullptr ||
fe->GetMapType() != FiniteElement::MapType::VALUE)
{
device_eval = false;
}
if (device_eval)
{
Geometry::Type geom = mesh->GetTypicalElementGeometry();
const IntegrationRule *ir_p;
if (irs)
{
ir_p = irs[geom];
}
else
{
int intorder = 2*fe->GetOrder() + 3; // <----------
ir_p = &(IntRules.Get(geom, intorder));
}
const IntegrationRule &ir = *ir_p;
QuadratureSpace qs(*mesh, ir);
CoefficientVector coeff(exsol, qs, CoefficientStorage::FULL);
const QVectorLayout ql = QVectorLayout::byNODES;
const MemoryType d_mt = MemoryType::DEFAULT;
Vector q_vals;
// TODO: make this a method
{
// const FiniteElement *fe = fes->GetTypicalFE();
const int vdim = fes->GetVDim();
const int NE = fes->GetNE();
const int ND = fe->GetDof();
const int NQ = ir.GetNPoints();
MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
Device::GetDeviceMemoryType();
// byNODES : NQPT x VDIM x NE
// byVDIM : VDIM x NQPT x NE
q_vals.SetSize(vdim*NQ*NE, my_d_mt);
const QuadratureInterpolator &qi = *fes->GetQuadratureInterpolator(ir);
qi.SetOutputLayout(ql);
const bool use_tensor_products = UsesTensorBasis(*fes);
qi.DisableTensorProducts(!use_tensor_products);
const ElementDofOrdering e_ordering =
use_tensor_products ?
ElementDofOrdering::LEXICOGRAPHIC :
ElementDofOrdering::NATIVE;
const Operator *elem_restr = fes->GetElementRestriction(e_ordering);
if (fe->GetMapType() == FiniteElement::MapType::INTEGRAL)
{
// Pre-compute the geometric factors in order to set the desired
// MemoryType they use:
fes->GetMesh()->GetGeometricFactors(
ir, GeometricFactors::DETERMINANTS, my_d_mt);
}
if (elem_restr)
{
Vector f_e(vdim*ND*NE, my_d_mt);
elem_restr->Mult(*this, f_e);
qi.PhysValues(f_e, q_vals);
}
else
{
qi.PhysValues(*this, q_vals);
}
}
const real_t *exact_d = coeff.Read();
const real_t *gridf_d = q_vals.Read();
// FIXME: reuse the workspace vector from vector.cpp?
static Array<real_t> workspace;
if (p < infinity())
{
MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
Device::GetDeviceMemoryType();
const GeometricFactors *geom_factors =
fes->GetMesh()->GetGeometricFactors(
ir, GeometricFactors::DETERMINANTS, my_d_mt);
const real_t *detJ_d = geom_factors->detJ.Read();
const real_t *w_d = ir.GetWeights().Read();
const int NQ = ir.GetNPoints();
mfem::reduce(q_vals.Size(), error,
[=] MFEM_HOST_DEVICE(int i, real_t &r)
{
const real_t diff = fabs(exact_d[i] - gridf_d[i]);
r += w_d[i%NQ] * detJ_d[i] * pow(diff, p);
}, SumReducer<real_t> {}, true, workspace);
error = pow(error, 1./p);
}
else
{
mfem::reduce(q_vals.Size(), error,
[=] MFEM_HOST_DEVICE(int i, real_t &r)
{
const real_t diff = fabs(exact_d[i] - gridf_d[i]);
r = fmax(r, diff);
}, MaxReducer<real_t> {}, true, workspace);
}
return error;
}
const FiniteElement *fe;
ElementTransformation *T;
Vector vals;
+31 -34
View File
@@ -161,8 +161,7 @@ static void EADiffusionAssemble3D(const int NE,
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 6, NE);
auto A = Reshape(add ? eadata.ReadWrite() : eadata.Write(),
D1D, D1D, D1D, D1D, D1D, D1D, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
@@ -247,60 +246,58 @@ void DiffusionIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &ea_data,
const bool add)
{
MFEM_PERF_FUNCTION;
AssemblePA(fes);
ne = fes.GetMesh()->GetNE();
const Array<real_t> &B = maps->B;
const Array<real_t> &G = maps->G;
decltype(&EADiffusionAssemble1D<>) kernel = nullptr;
if (dim == 1)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: kernel = EADiffusionAssemble1D<2,2>;
case 0x33: kernel = EADiffusionAssemble1D<3,3>;
case 0x44: kernel = EADiffusionAssemble1D<4,4>;
case 0x55: kernel = EADiffusionAssemble1D<5,5>;
case 0x66: kernel = EADiffusionAssemble1D<6,6>;
case 0x77: kernel = EADiffusionAssemble1D<7,7>;
case 0x88: kernel = EADiffusionAssemble1D<8,8>;
case 0x99: kernel = EADiffusionAssemble1D<9,9>;
default: kernel = EADiffusionAssemble1D<>;
case 0x22: return EADiffusionAssemble1D<2,2>(ne,B,G,pa_data,ea_data,add);
case 0x33: return EADiffusionAssemble1D<3,3>(ne,B,G,pa_data,ea_data,add);
case 0x44: return EADiffusionAssemble1D<4,4>(ne,B,G,pa_data,ea_data,add);
case 0x55: return EADiffusionAssemble1D<5,5>(ne,B,G,pa_data,ea_data,add);
case 0x66: return EADiffusionAssemble1D<6,6>(ne,B,G,pa_data,ea_data,add);
case 0x77: return EADiffusionAssemble1D<7,7>(ne,B,G,pa_data,ea_data,add);
case 0x88: return EADiffusionAssemble1D<8,8>(ne,B,G,pa_data,ea_data,add);
case 0x99: return EADiffusionAssemble1D<9,9>(ne,B,G,pa_data,ea_data,add);
default: return EADiffusionAssemble1D(ne,B,G,pa_data,ea_data,add,
dofs1D,quad1D);
}
}
else if (dim == 2)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: kernel = EADiffusionAssemble2D<2,2>;
case 0x33: kernel = EADiffusionAssemble2D<3,3>;
case 0x44: kernel = EADiffusionAssemble2D<4,4>;
case 0x55: kernel = EADiffusionAssemble2D<5,5>;
case 0x66: kernel = EADiffusionAssemble2D<6,6>;
case 0x77: kernel = EADiffusionAssemble2D<7,7>;
case 0x88: kernel = EADiffusionAssemble2D<8,8>;
case 0x99: kernel = EADiffusionAssemble2D<9,9>;
default: kernel = EADiffusionAssemble2D<>;
case 0x22: return EADiffusionAssemble2D<2,2>(ne,B,G,pa_data,ea_data,add);
case 0x33: return EADiffusionAssemble2D<3,3>(ne,B,G,pa_data,ea_data,add);
case 0x44: return EADiffusionAssemble2D<4,4>(ne,B,G,pa_data,ea_data,add);
case 0x55: return EADiffusionAssemble2D<5,5>(ne,B,G,pa_data,ea_data,add);
case 0x66: return EADiffusionAssemble2D<6,6>(ne,B,G,pa_data,ea_data,add);
case 0x77: return EADiffusionAssemble2D<7,7>(ne,B,G,pa_data,ea_data,add);
case 0x88: return EADiffusionAssemble2D<8,8>(ne,B,G,pa_data,ea_data,add);
case 0x99: return EADiffusionAssemble2D<9,9>(ne,B,G,pa_data,ea_data,add);
default: return EADiffusionAssemble2D(ne,B,G,pa_data,ea_data,add,
dofs1D,quad1D);
}
}
else if (dim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x23: kernel = EADiffusionAssemble3D<2,3>;
case 0x34: kernel = EADiffusionAssemble3D<3,4>;
case 0x45: kernel = EADiffusionAssemble3D<4,5>;
case 0x56: kernel = EADiffusionAssemble3D<5,6>;
case 0x67: kernel = EADiffusionAssemble3D<6,7>;
case 0x78: kernel = EADiffusionAssemble3D<7,8>;
case 0x89: kernel = EADiffusionAssemble3D<8,9>;
default: kernel = EADiffusionAssemble3D<>;
case 0x23: return EADiffusionAssemble3D<2,3>(ne,B,G,pa_data,ea_data,add);
case 0x34: return EADiffusionAssemble3D<3,4>(ne,B,G,pa_data,ea_data,add);
case 0x45: return EADiffusionAssemble3D<4,5>(ne,B,G,pa_data,ea_data,add);
case 0x56: return EADiffusionAssemble3D<5,6>(ne,B,G,pa_data,ea_data,add);
case 0x67: return EADiffusionAssemble3D<6,7>(ne,B,G,pa_data,ea_data,add);
case 0x78: return EADiffusionAssemble3D<7,8>(ne,B,G,pa_data,ea_data,add);
case 0x89: return EADiffusionAssemble3D<8,9>(ne,B,G,pa_data,ea_data,add);
default: return EADiffusionAssemble3D(ne,B,G,pa_data,ea_data,add,
dofs1D,quad1D);
}
}
MFEM_VERIFY(kernel != nullptr, "Unknown kernel.");
kernel(ne,B,G,pa_data,ea_data,add,dofs1D,quad1D);
// Free the PA data:
pa_data.Destroy();
MFEM_ABORT("Unknown kernel.");
}
}
-4
View File
@@ -39,8 +39,6 @@ void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
// PA Diffusion Apply kernel
void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
MFEM_PERF_FUNCTION;
if (DeviceCanUseCeed())
{
ceedOp->AddMult(x, y);
@@ -90,8 +88,6 @@ void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
MFEM_PERF_FUNCTION;
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
// Assuming the same element type
+56 -29
View File
@@ -23,8 +23,6 @@ namespace mfem
void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
MFEM_PERF_FUNCTION;
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
@@ -61,23 +59,26 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
const int NE = ne;
const int Q1D = quad1D;
const int NQ = static_cast<int>(std::pow(Q1D, dim));
const bool const_c = coeff.Size() == 1;
const bool by_val = map_type == FiniteElement::VALUE;
const auto W = Reshape(ir->GetWeights().Read(), NQ);
const auto J = Reshape(geom->detJ.Read(), NQ, NE);
const auto C = const_c ? Reshape(coeff.Read(), 1, 1) :
Reshape(coeff.Read(), NQ,NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
mfem::forall_2D(NE, NQ, 1, [=] MFEM_HOST_DEVICE (int e)
{
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
const bool by_val = map_type == FiniteElement::VALUE;
const auto W = Reshape(ir->GetWeights().Read(), NQ);
const auto J = Reshape(geom->detJ.Read(), NQ, NE);
const auto C =
const_c ? Reshape(coeff.Read(), 1, 1) : Reshape(coeff.Read(), NQ, NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
mfem::forall(NQ, NE, [=] MFEM_HOST_DEVICE(int q, int e)
MFEM_FOREACH_THREAD(i, x, NQ)
{
const real_t detJ = J(q, e);
const real_t coeff = const_c ? C(0, 0) : C(q, e);
v(q, e) = W(q) * coeff * (by_val ? detJ : 1.0 / detJ);
});
}
const real_t detJ = J(i,e);
const real_t coeff = const_c ? C(0,0) : C(i,e);
v(i,e) = W(i) * coeff * (by_val ? detJ : 1.0/detJ);
}
});
}
void MassIntegrator::AssemblePABoundary(const FiniteElementSpace &fes)
@@ -108,22 +109,50 @@ void MassIntegrator::AssemblePABoundary(const FiniteElementSpace &fes)
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
const int NE = ne;
const int NQ = nq;
const int Q1D = quad1D;
const bool const_c = coeff.Size() == 1;
const bool by_val = map_type == FiniteElement::VALUE;
if (dim==1)
{
const auto W = Reshape(ir->GetWeights().Read(), NQ);
const auto J = Reshape(face_geom->detJ.Read(), NQ, NE);
const auto C = const_c ? Reshape(coeff.Read(), 1, 1)
: Reshape(coeff.Read(), NQ, NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
mfem::forall(NQ, NE, [=] MFEM_HOST_DEVICE(int q, int e)
const auto W = Reshape(ir->GetWeights().Read(), Q1D);
const auto J = Reshape(face_geom->detJ.Read(), Q1D, NE);
const auto C = const_c ? Reshape(coeff.Read(), 1, 1) :
Reshape(coeff.Read(), Q1D, NE);
auto v = Reshape(pa_data.Write(), Q1D, NE);
mfem::forall_2D(NE, Q1D, 1, [=] MFEM_HOST_DEVICE (int e)
{
const real_t detJ = J(q, e);
const real_t coeff = const_c ? C(0, 0) : C(q, e);
v(q, e) = W(q) * coeff * (by_val ? detJ : 1.0 / detJ);
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
const real_t detJ = J(qx,e);
const real_t coeff = const_c ? C(0,0) : C(qx,e);
v(qx,e) = W(qx) * coeff * (by_val ? detJ : 1.0/detJ);
}
});
}
else if (dim==2)
{
const auto W = Reshape(ir->GetWeights().Read(), Q1D,Q1D);
const auto J = Reshape(face_geom->detJ.Read(), Q1D,Q1D,NE);
const auto C = const_c ? Reshape(coeff.Read(), 1,1,1) :
Reshape(coeff.Read(), Q1D,Q1D,NE);
auto v = Reshape(pa_data.Write(), Q1D,Q1D, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
const real_t detJ = J(qx,qy,e);
const real_t coeff = const_c ? C(0,0,0) : C(qx,qy,e);
v(qx,qy,e) = W(qx,qy) * coeff * (by_val ? detJ : 1.0/detJ);
}
}
});
}
else
{
MFEM_ABORT("Not supported.");
}
}
void MassIntegrator::AssembleDiagonalPA(Vector &diag)
@@ -141,8 +170,6 @@ void MassIntegrator::AssembleDiagonalPA(Vector &diag)
void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
MFEM_PERF_FUNCTION;
if (DeviceCanUseCeed())
{
ceedOp->AddMult(x, y);
-2
View File
@@ -242,8 +242,6 @@ void DomainLFIntegrator::AssembleDevice(const FiniteElementSpace &fes,
const Array<int> &markers,
Vector &b)
{
MFEM_PERF_FUNCTION;
const FiniteElement &fe = *fes.GetTypicalFE();
const int qorder = oa * fe.GetOrder() + ob;
const Geometry::Type gtype = fe.GetGeomType();
+29 -29
View File
@@ -346,13 +346,13 @@ private:
template<typename T>
T operator() (const blitz::TinyVector<T,3>& x) const
{
const int el_order = el->GetOrder();
std::vector<T> u1(el_order+1);
std::vector<T> u2(el_order+1);
std::vector<T> u3(el_order+1);
TmplPoly_1D::CalcBernstein(el_order, x[0], u1.data());
TmplPoly_1D::CalcBernstein(el_order, x[1], u2.data());
TmplPoly_1D::CalcBernstein(el_order, x[2], u3.data());
int el_order=el->GetOrder();
T u1[el_order+1];
T u2[el_order+1];
T u3[el_order+1];
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
TmplPoly_1D::CalcBernstein(el_order, x[2], u3);
const Array<int>& dof_map=el->GetDofMap();
@@ -370,17 +370,17 @@ private:
template<typename T>
blitz::TinyVector<T,3> grad(const blitz::TinyVector<T,3>& x) const
{
const int el_order = el->GetOrder();
std::vector<T> u1(el_order+1);
std::vector<T> u2(el_order+1);
std::vector<T> u3(el_order+1);
std::vector<T> d1(el_order+1);
std::vector<T> d2(el_order+1);
std::vector<T> d3(el_order+1);
int el_order=el->GetOrder();
T u1[el_order+1];
T u2[el_order+1];
T u3[el_order+1];
T d1[el_order+1];
T d2[el_order+1];
T d3[el_order+1];
TmplPoly_1D::CalcBernstein(el_order,x[0], u1.data(), d1.data());
TmplPoly_1D::CalcBernstein(el_order,x[1], u2.data(), d2.data());
TmplPoly_1D::CalcBernstein(el_order,x[2], u3.data(), d3.data());
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
TmplPoly_1D::CalcBernstein(el_order,x[2], u3, d3);
blitz::TinyVector<T,3> res(T(0.0),T(0.0),T(0.0));
@@ -415,11 +415,11 @@ private:
template<typename T>
T operator() (const blitz::TinyVector<T,2>& x) const
{
const int el_order = el->GetOrder();
std::vector<T> u1(el_order+1);
std::vector<T> u2(el_order+1);
TmplPoly_1D::CalcBernstein(el_order, x[0], u1.data());
TmplPoly_1D::CalcBernstein(el_order, x[1], u2.data());
int el_order=el->GetOrder();
T u1[el_order+1];
T u2[el_order+1];
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
const Array<int>& dof_map=el->GetDofMap();
@@ -437,14 +437,14 @@ private:
template<typename T>
blitz::TinyVector<T,2> grad(const blitz::TinyVector<T,2>& x) const
{
const int el_order = el->GetOrder();
std::vector<T> u1(el_order+1);
std::vector<T> u2(el_order+1);
std::vector<T> d1(el_order+1);
std::vector<T> d2(el_order+1);
int el_order=el->GetOrder();
T u1[el_order+1];
T u2[el_order+1];
T d1[el_order+1];
T d2[el_order+1];
TmplPoly_1D::CalcBernstein(el_order,x[0], u1.data(), d1.data());
TmplPoly_1D::CalcBernstein(el_order,x[1], u2.data(), d2.data());
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
blitz::TinyVector<T,2> res(T(0.0),T(0.0));
-3
View File
@@ -161,7 +161,6 @@ bool LinearForm::SupportsDevice() const
void LinearForm::UseFastAssembly(bool use_fa)
{
MFEM_PERF_FUNCTION;
fast_assembly = use_fa;
if (fast_assembly && SupportsDevice() && !ext)
@@ -172,8 +171,6 @@ void LinearForm::UseFastAssembly(bool use_fa)
void LinearForm::Assemble()
{
MFEM_PERF_FUNCTION;
Array<int> vdofs;
ElementTransformation *eltrans;
Vector elemvect;
+33 -19
View File
@@ -15,16 +15,10 @@
namespace mfem
{
LinearFormExtension::LinearFormExtension(LinearForm *lf): lf(lf)
{
MFEM_PERF_FUNCTION;
Update();
}
LinearFormExtension::LinearFormExtension(LinearForm *lf): lf(lf) { Update(); }
void LinearFormExtension::Assemble()
{
MFEM_PERF_FUNCTION;
const FiniteElementSpace &fes = *lf->FESpace();
MFEM_VERIFY(lf->SupportsDevice(), "Not supported.");
MFEM_VERIFY(lf->Size() == fes.GetVSize(), "LinearForm size does not "
@@ -57,7 +51,7 @@ void LinearFormExtension::Assemble()
{
// scan the attributes to set the markers to 0 or 1
const int NE = fes.GetNE();
const auto attr = attributes->Read();
const auto attr = attributes.Read();
const auto dimk = domain_integs_marker_k->Read();
auto markers_w = markers.Write();
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
@@ -99,14 +93,13 @@ void LinearFormExtension::Assemble()
else
{
// scan the attributes to set the markers to 0 or 1
const int NBE = bdr_face_attributes->Size();
const auto attr = bdr_face_attributes->Read();
const int NBE = bdr_attributes.Size();
const auto attr = bdr_attributes.Read();
const auto attr_markers = boundary_integs_marker_k->Read();
auto markers_w = bdr_markers.Write();
mfem::forall(NBE, [=] MFEM_HOST_DEVICE(int e)
mfem::forall(NBE, [=] MFEM_HOST_DEVICE (int e)
{
markers_w[e] =
attr[e] > 0 ? (attr_markers[attr[e] - 1] == 1) : false;
markers_w[e] = attr_markers[attr[e]-1] == 1;
});
}
@@ -119,8 +112,6 @@ void LinearFormExtension::Assemble()
void LinearFormExtension::Update()
{
MFEM_PERF_FUNCTION;
const FiniteElementSpace &fes = *lf->FESpace();
const Mesh &mesh = *fes.GetMesh();
constexpr ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC;
@@ -134,7 +125,8 @@ void LinearFormExtension::Update()
//markers.UseDevice(true);
// Gather the attributes on the host from all the elements
attributes = &mesh.GetElementAttributes();
attributes.SetSize(NE);
for (int i = 0; i < NE; ++i) { attributes[i] = mesh.GetAttribute(i); }
elem_restrict_lex = fes.GetElementRestriction(ordering);
MFEM_VERIFY(elem_restrict_lex, "Element restriction not available");
@@ -144,12 +136,34 @@ void LinearFormExtension::Update()
if (lf->boundary_integs.Size() > 0)
{
bdr_face_attributes = &mesh.GetBdrFaceAttributes();
const int nf_bdr = bdr_face_attributes->Size();
const int nf_bdr = fes.GetNFbyType(FaceType::Boundary);
bdr_markers.SetSize(nf_bdr);
// bdr_markers.UseDevice(true);
// The face restriction will give us "face E-vectors" on the boundary that
// are numbered in the order of the faces of mesh. This numbering will be
// different than the numbering of the boundary elements. We compute
// mappings so that the array `bdr_attributes[i]` gives the boundary
// attribute of the `i`th boundary face in the mesh face order.
std::unordered_map<int,int> f_to_be;
for (int i = 0; i < mesh.GetNBE(); ++i)
{
const int f = mesh.GetBdrElementFaceIndex(i);
f_to_be[f] = i;
}
MFEM_VERIFY(size_t(nf_bdr) == f_to_be.size(), "Incompatible sizes");
bdr_attributes.SetSize(nf_bdr);
int f_ind = 0;
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
if (f_to_be.find(f) != f_to_be.end())
{
const int be = f_to_be[f];
bdr_attributes[f_ind] = mesh.GetBdrAttribute(be);
++f_ind;
}
}
bdr_restrict_lex =
dynamic_cast<const FaceRestriction*>(
fes.GetFaceRestriction(ordering, FaceType::Boundary,
+1 -3
View File
@@ -14,7 +14,6 @@
#include "../general/array.hpp"
#include "../linalg/vector.hpp"
#include "fespace.hpp"
namespace mfem
{
@@ -26,8 +25,7 @@ class LinearForm;
class LinearFormExtension
{
/// Attributes of all mesh elements.
const Array<int> *attributes; // Not owned
const Array<int> *bdr_face_attributes; // Not owned
Array<int> attributes, bdr_attributes;
/// Temporary markers for device kernels.
Array<int> markers, bdr_markers;
-2
View File
@@ -365,8 +365,6 @@ FiniteElementSpace &LORBase::GetFESpace() const
void LORBase::AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs)
{
MFEM_PERF_FUNCTION;
A.Clear();
delete a;
if (BatchedLORAssembly::FormIsSupported(a_ho))
-4
View File
@@ -360,8 +360,6 @@ void BatchedLORAssembly::FillJAndData(SparseMatrix &A) const
void BatchedLORAssembly::SparseIJToCSR(OperatorHandle &A) const
{
MFEM_PERF_FUNCTION;
const int nvdof = fes_ho.GetVSize();
// If A contains an existing SparseMatrix, reuse it (and try to reuse its
@@ -419,8 +417,6 @@ static void Assemble_(LOR_KERNEL &kernel, int dim, int sdim, int order)
template <typename LOR_KERNEL>
void BatchedLORAssembly::AssemblyKernel(BilinearForm &a)
{
MFEM_PERF_FUNCTION;
LOR_KERNEL kernel(a, fes_ho, X_vert, sparse_ij, sparse_mapping);
const int dim = fes_ho.GetMesh()->Dimension();
-2
View File
@@ -184,8 +184,6 @@ void BatchedLOR_H1::Assemble2D()
template <int ORDER>
void BatchedLOR_H1::Assemble3D()
{
MFEM_PERF_FUNCTION;
const int nel_ho = fes_ho.GetNE();
static constexpr int nv = 8;
static constexpr int dim = 3;
+54 -139
View File
@@ -10,7 +10,6 @@
// CONTRIBUTING.md for details.
#include "multigrid.hpp"
#include "../general/annotation.hpp"
namespace mfem
{
@@ -18,10 +17,7 @@ namespace mfem
MultigridBase::MultigridBase()
: cycleType(CycleType::VCYCLE), preSmoothingSteps(1), postSmoothingSteps(1),
nrhs(0)
{
coarse_solver = nullptr;
own_coarse_solver = false;
}
{}
MultigridBase::MultigridBase(const Array<Operator*>& operators_,
const Array<Solver*>& smoothers_,
@@ -33,18 +29,12 @@ MultigridBase::MultigridBase(const Array<Operator*>& operators_,
{
operators_.Copy(operators);
smoothers_.Copy(smoothers);
coarse_solver = nullptr;
ownedOperators_.Copy(ownedOperators);
ownedSmoothers_.Copy(ownedSmoothers);
own_coarse_solver = false;
}
MultigridBase::~MultigridBase()
{
if (own_coarse_solver)
{
delete coarse_solver;
}
for (int i = 0; i < operators.Size(); ++i)
{
if (ownedOperators[i])
@@ -66,17 +56,16 @@ void MultigridBase::InitVectors() const
X.SetSize(M, nrhs);
Y.SetSize(M, nrhs);
R.SetSize(M, nrhs);
for (int i = 0; i < M; ++i)
Z.SetSize(M, nrhs);
for (int i = 0; i < X.NumRows(); ++i)
{
const int n = operators[i]->Height();
for (int j = 0; j < nrhs; ++j)
for (int j = 0; j < X.NumCols(); ++j)
{
if (i < M - 1)
{
X(i, j) = new Vector(n);
Y(i, j) = new Vector(n);
}
X(i, j) = new Vector(n);
Y(i, j) = new Vector(n);
R(i, j) = new Vector(n);
Z(i, j) = new Vector(n);
}
}
}
@@ -87,12 +76,10 @@ void MultigridBase::EraseVectors() const
{
for (int j = 0; j < X.NumCols(); ++j)
{
if (i < X.NumRows() - 1)
{
delete X(i, j);
delete Y(i, j);
}
delete X(i, j);
delete Y(i, j);
delete R(i, j);
delete Z(i, j);
}
}
}
@@ -108,12 +95,6 @@ void MultigridBase::AddLevel(Operator* op, Solver* smoother,
ownedSmoothers.Append(ownSmoother);
}
void MultigridBase::AddCoarseSolver(Solver *c_solver, bool own_c_solver)
{
coarse_solver = c_solver;
own_coarse_solver = own_c_solver;
}
void MultigridBase::SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
int postSmoothingSteps_)
{
@@ -124,24 +105,25 @@ void MultigridBase::SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
void MultigridBase::Mult(const Vector& x, Vector& y) const
{
const Vector *x_array[1] = { &x };
Array<const Vector*> X_(x_array, 1); // no heap allocation
Vector *y_array[1] = { &y };
Array<Vector*> Y_(y_array, 1); // no heap allocation
Array<const Vector*> X_(1);
Array<Vector*> Y_(1);
X_[0] = &x;
Y_[0] = &y;
ArrayMult(X_, Y_);
}
void MultigridBase::ArrayMult(const Array<const Vector*>& X_,
Array<Vector*>& Y_) const
{
MFEM_PERF_FUNCTION;
MFEM_ASSERT(operators.Size() > 0,
"Multigrid solver does not have operators set!");
MFEM_ASSERT(X_.Size() == Y_.Size(),
"Number of columns mismatch in MultigridBase::Mult!");
if (iterative_mode)
{
MFEM_WARNING("Multigrid solver does not use iterative_mode and ignores "
"the initial guess!");
}
// Add capacity as necessary
nrhs = X_.Size();
@@ -152,163 +134,96 @@ void MultigridBase::ArrayMult(const Array<const Vector*>& X_,
for (int j = 0; j < nrhs; ++j)
{
MFEM_ASSERT(X_[j] && Y_[j], "Missing Vector in MultigridBase::Mult!");
X(M - 1, j) = const_cast<Vector*>(X_[j]);
Y(M - 1, j) = Y_[j];
*X(M - 1, j) = *X_[j];
*Y(M - 1, j) = 0.0;
}
Cycle(M - 1);
for (int j = 0; j < nrhs; ++j)
{
*Y_[j] = *Y(M - 1, j);
}
const bool zero = !iterative_mode;
Cycle(M - 1, zero);
}
void MultigridBase::SmoothingStep(int level, bool zero, bool transpose) const
{
MFEM_PERF_FUNCTION;
// y = y + S (x - A y) or y = y + S^T (x - A y)
// Note: 'zero' == true means that Y(level,*) are not initialized and we
// should assume that the input they typically provide to this call is zeros.
// We can't use the smoothers' iterative mode since we don't know if they
// actually support it, so we always turn the iterative mode off to properly
// use smoothers that do support it.
smoothers[level]->iterative_mode = false;
if (zero)
{
MFEM_ASSERT(!transpose, "internal error!");
const Array<const Vector *> cX_((const Vector **)(X[level]), nrhs);
Array<Vector *> Y_(Y[level], nrhs);
GetSmootherAtLevel(level)->ArrayMult(cX_, Y_);
Array<Vector *> X_(X[level], nrhs), Y_(Y[level], nrhs);
GetSmootherAtLevel(level)->ArrayMult(X_, Y_);
}
else
{
const Array<const Vector *> cY_((const Vector **)(Y[level]), nrhs),
cR_((const Vector **)(R[level]), nrhs);
Array<Vector *> Y_(Y[level], nrhs), R_(R[level], nrhs);
GetOperatorAtLevel(level)->ArrayMult(cY_, R_);
Array<Vector *> Y_(Y[level], nrhs), R_(R[level], nrhs),
Z_(Z[level], nrhs);
for (int j = 0; j < nrhs; ++j)
{
// *R_[j] = *X(level, j) - *R_[j]
subtract(*X(level, j), *R_[j], *R_[j]);
*R_[j] = *X(level, j);
}
GetOperatorAtLevel(level)->ArrayAddMult(Y_, R_, -1.0);
if (transpose)
{
GetSmootherAtLevel(level)->ArrayAddMultTranspose(cR_, Y_);
GetSmootherAtLevel(level)->ArrayMultTranspose(R_, Z_);
}
else
{
GetSmootherAtLevel(level)->ArrayAddMult(cR_, Y_);
GetSmootherAtLevel(level)->ArrayMult(R_, Z_);
}
}
}
void MultigridBase::CoarseSolve(bool zero) const
{
MFEM_PERF_FUNCTION;
// See the comment about iterative mode in SmoothingStep()
coarse_solver->iterative_mode = false;
if (zero)
{
const Array<const Vector *> cX_((const Vector **)(X[0]), nrhs);
Array<Vector *> Y_(Y[0], nrhs);
coarse_solver->ArrayMult(cX_, Y_);
}
else
{
const Array<const Vector *> cY_((const Vector **)(Y[0]), nrhs),
cR_((const Vector **)(R[0]), nrhs);
Array<Vector *> Y_(Y[0], nrhs), R_(R[0], nrhs);
GetOperatorAtLevel(0)->ArrayMult(cY_, R_);
for (int j = 0; j < nrhs; ++j)
{
// *R_[j] = *X(0, j) - *R_[j]
subtract(*X(0, j), *R_[j], *R_[j]);
*Y_[j] += *Z_[j];
}
coarse_solver->ArrayAddMult(cR_, Y_);
}
}
void MultigridBase::Cycle(int level, bool zero) const
void MultigridBase::Cycle(int level) const
{
// Note: 'zero' == true means that Y(level,*) are not initialized and we
// should assume that the input they typically provide to this call is zeros.
// Coarse solve
if (level == 0 && !coarse_solver)
if (level == 0)
{
SmoothingStep(0, zero, false);
SmoothingStep(0, true, false);
return;
}
// Pre-smooth
for (int i = 0; i < preSmoothingSteps; ++i)
{
SmoothingStep(level, zero && (i == 0), false);
}
// Coarse solve with 'coarse_solver'
if (level == 0)
{
CoarseSolve(preSmoothingSteps == 0 && zero);
goto mg_post_smooth;
SmoothingStep(level, (cycleType == CycleType::VCYCLE && i == 0), false);
}
// Compute residual and restrict
if (preSmoothingSteps == 0 && zero)
{
const Array<const Vector *> cX_l((const Vector **)(X[level]), nrhs);
Array<Vector *> X_lm1(X[level - 1], nrhs);
GetProlongationAtLevel(level - 1)->ArrayMultTranspose(cX_l, X_lm1);
}
else
{
const Array<const Vector *> cY_((const Vector **)(Y[level]), nrhs),
cR_((const Vector **)(R[level]), nrhs);
Array<Vector *> R_(R[level], nrhs), X_(X[level - 1], nrhs);
GetOperatorAtLevel(level)->ArrayMult(cY_, R_);
Array<Vector *> Y_(Y[level], nrhs), R_(R[level], nrhs),
X_(X[level - 1], nrhs);
for (int j = 0; j < nrhs; ++j)
{
// *R_[j] = *X(level, j) - *R_[j]
subtract(*X(level, j), *R_[j], *R_[j]);
*R_[j] = *X(level, j);
}
GetOperatorAtLevel(level)->ArrayAddMult(Y_, R_, -1.0);
GetProlongationAtLevel(level - 1)->ArrayMultTranspose(R_, X_);
for (int j = 0; j < nrhs; ++j)
{
*Y(level - 1, j) = 0.0;
}
GetProlongationAtLevel(level - 1)->ArrayMultTranspose(cR_, X_);
}
// Corrections
Cycle(level - 1, true);
Cycle(level - 1);
if (cycleType == CycleType::WCYCLE)
{
// If the coarse solve at level 0 is "exact" solve, then we don't want to
// repeat it.
// To support multiple level 0 coarse-grid corrections, one can wrap that
// smoother in an SLI solver and use that instead.
if (level > 1) { Cycle(level - 1, false); }
Cycle(level - 1);
}
// Prolongate and add
{
const Array<const Vector *> cY_lm1((const Vector **)(Y[level - 1]), nrhs);
Array<Vector *> Y_l(Y[level], nrhs);
if (preSmoothingSteps == 0 && zero)
Array<Vector *> Y_(Y[level - 1], nrhs), Z_(Z[level], nrhs);
GetProlongationAtLevel(level - 1)->ArrayMult(Y_, Z_);
for (int j = 0; j < nrhs; ++j)
{
GetProlongationAtLevel(level - 1)->ArrayMult(cY_lm1, Y_l);
}
else
{
GetProlongationAtLevel(level - 1)->ArrayAddMult(cY_lm1, Y_l);
*Y(level, j) += *Z_[j];
}
}
mg_post_smooth:
// Post-smooth
for (int i = 0; i < postSmoothingSteps; ++i)
{
+2 -20
View File
@@ -36,14 +36,12 @@ protected:
Array<Solver*> smoothers;
Array<bool> ownedOperators;
Array<bool> ownedSmoothers;
Solver *coarse_solver; /// can be NULL, see AddCoarseSolver()
bool own_coarse_solver;
CycleType cycleType;
int preSmoothingSteps;
int postSmoothingSteps;
mutable Array2D<Vector*> X, Y, R;
mutable Array2D<Vector*> X, Y, R, Z;
mutable int nrhs;
public:
@@ -67,16 +65,6 @@ public:
void AddLevel(Operator* op, Solver* smoother, bool ownOperator,
bool ownSmoother);
/// Adds a coarse solver for level 0 to work in tandem with the smoother
/** If this coarse solver is not given, the smoother at level 0 is used as
the coarse solver. When this coarse solver is given, the smoother at
level 0 is used similar to the smoothers at other levels. Thus, the
action at level 0 consists of:
- pre-smoothing steps with smoother 0,
- solve step with @a c_solver,
- post-smoothing steps with smoother 0. */
void AddCoarseSolver(Solver *c_solver, bool own_c_solver);
/// Returns the number of levels
int NumLevels() const { return operators.Size(); }
@@ -130,14 +118,11 @@ public:
private:
/// Application of a multigrid cycle at particular level
void Cycle(int level, bool zero) const;
void Cycle(int level) const;
/// Application of a pre-/post-smoothing step at particular level
void SmoothingStep(int level, bool zero, bool transpose) const;
/// Perform a coarse solve with 'coarse_solve' (must be non-NULL)
void CoarseSolve(bool zero) const;
/// Allocate or destroy temporary storage
void InitVectors() const;
void EraseVectors() const;
@@ -217,9 +202,6 @@ public:
/// Recover the solution of a linear system formed with FormFineLinearSystem()
void RecoverFineFEMSolution(const Vector& X, const Vector& b, Vector& x);
const Array<int> &GetFineEssentialTrueDofs() const
{ return *essentialTrueDofs.Last(); }
};
} // namespace mfem
-2
View File
@@ -124,8 +124,6 @@ void ParBilinearForm::pAllocMat()
void ParBilinearForm::ParallelRAP(SparseMatrix &loc_A, OperatorHandle &A,
bool steal_loc_A)
{
MFEM_PERF_FUNCTION;
ParFiniteElementSpace &pfespace = *ParFESpace();
// Create a block diagonal parallel matrix
-591
View File
@@ -1,591 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "pderefmat_op.hpp"
#ifdef MFEM_USE_MPI
#include "fes_kernels.hpp"
/// \cond DO_NOT_DOCUMENT
namespace mfem
{
namespace internal
{
template <Ordering::Type Order, bool Atomic>
static void ParDerefMultKernelImpl(const ParDerefineMatrixOp &op,
const Vector &x, Vector &y)
{
// pack sends
if (op.xghost_send.Size())
{
auto src = x.Read();
auto idcs = op.send_permutations.Read();
auto dst = Device::GetGPUAwareMPI() ? op.xghost_send.Write()
: op.xghost_send.HostWrite();
auto vdims = op.fespace->GetVDim();
auto sptr = op.send_segment_idcs.Read();
auto lptr = op.send_segments.Read();
auto old_ndofs = x.Size() / vdims;
forall(op.send_permutations.Size(), [=] MFEM_HOST_DEVICE(int i)
{
int seg = sptr[i];
int width = lptr[seg + 1] - lptr[seg];
auto tdst = dst + i + lptr[seg] * vdims;
int sign = 1;
int col = idcs[i];
if (col < 0)
{
sign = -1;
col = -1 - col;
}
for (int vdim = 0; vdim < vdims; ++vdim)
{
tdst[vdim * width] =
sign
* src[Order == Ordering::byNODES ? (col + vdim * old_ndofs)
: (col * vdims + vdim)];
}
});
// TODO: is this needed so we can send the packed data correctly?
// unclear for GPU-aware MPI, definitely required otherwise
MFEM_DEVICE_SYNC;
}
// initialize off-diagonal receive and send
op.requests.clear();
if (op.xghost_recv.Size())
{
auto vdims = op.fespace->GetVDim();
auto rcv = Device::GetGPUAwareMPI() ? op.xghost_recv.Write()
: op.xghost_recv.HostWrite();
for (int i = 0; i < op.recv_ranks.Size(); ++i)
{
op.requests.emplace_back();
MPI_Irecv(rcv + op.recv_segments[i] * vdims,
(op.recv_segments[i + 1] - op.recv_segments[i]) * vdims,
MPITypeMap<real_t>::mpi_type, op.recv_ranks[i],
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA,
op.fespace->GetComm(), &op.requests.back());
}
}
if (op.xghost_send.Size())
{
auto vdims = op.fespace->GetVDim();
// only is a GPU mem ptr if GPU-aware MPI is enabled
auto dst = Device::GetGPUAwareMPI() ? op.xghost_send.Write()
: op.xghost_send.HostWrite();
for (int i = 0; i < op.send_ranks.Size(); ++i)
{
op.requests.emplace_back();
MPI_Isend(dst + op.send_segments[i] * vdims,
(op.send_segments[i + 1] - op.send_segments[i]) * vdims,
MPITypeMap<real_t>::mpi_type, op.send_ranks[i],
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA,
op.fespace->GetComm(), &op.requests.back());
}
}
{
// diagonal
DerefineMatrixOpMultFunctor<Order, Atomic, true> func;
func.xptr = x.Read();
y.UseDevice();
y = 0.;
func.yptr = y.ReadWrite();
func.bsptr = op.block_storage.Read();
func.boptr = op.block_offsets.Read();
func.brptr = op.block_row_idcs_offsets.Read();
func.bcptr = op.block_col_idcs_offsets.Read();
func.rptr = op.row_idcs.Read();
func.cptr = op.col_idcs.Read();
func.vdims = op.fespace->GetVDim();
func.nblocks = op.block_offsets.Size();
func.width = op.Width() / func.vdims;
func.height = op.Height() / func.vdims;
func.Run(op.max_rows);
}
// wait for comm to finish, if any
if (op.requests.size())
{
MPI_Waitall(op.requests.size(), op.requests.data(), MPI_STATUSES_IGNORE);
if (op.xghost_recv.Size())
{
// off-diagonal kernel
DerefineMatrixOpMultFunctor<Order, Atomic, false> func;
// directly read from host-pinned memory if not using GPU-aware MPI
func.xptr = Device::GetGPUAwareMPI() ? op.xghost_recv.Read()
: op.xghost_recv.HostRead();
func.yptr = y.ReadWrite();
func.bsptr = op.block_storage.Read();
func.boptr = op.off_diag_block_offsets.Read();
func.brptr = op.block_off_diag_row_idcs_offsets.Read();
func.rsptr = op.recv_segment_idcs.Read();
func.segptr = op.recv_segments.Read();
func.coptr = op.block_off_diag_col_offsets.Read();
func.bwptr = op.block_off_diag_widths.Read();
func.rptr = op.row_off_diag_idcs.Read();
func.vdims = op.fespace->GetVDim();
func.nblocks = op.off_diag_block_offsets.Size();
func.width = op.xghost_recv.Size() / func.vdims;
func.height = op.Height() / func.vdims;
func.Run(op.max_rows);
}
}
}
} // namespace internal
template <Ordering::Type Order, bool Atomic>
ParDerefineMatrixOp::MultKernelType ParDerefineMatrixOp::MultKernel::Kernel()
{
return internal::ParDerefMultKernelImpl<Order, Atomic>;
}
ParDerefineMatrixOp::MultKernelType
ParDerefineMatrixOp::MultKernel::Fallback(Ordering::Type, bool)
{
MFEM_ABORT("invalid MultKernel parameters");
}
ParDerefineMatrixOp::Kernels::Kernels()
{
MultKernel::Specialization<Ordering::byNODES, false>::Add();
MultKernel::Specialization<Ordering::byVDIM, false>::Add();
MultKernel::Specialization<Ordering::byNODES, true>::Add();
MultKernel::Specialization<Ordering::byVDIM, true>::Add();
}
void ParDerefineMatrixOp::Mult(const Vector &x, Vector &y) const
{
const bool is_dg = fespace->FEColl()->GetContType()
== FiniteElementCollection::DISCONTINUOUS;
// DG needs atomic summation
MultKernel::Run(fespace->GetOrdering(), is_dg, *this, x, y);
// use this to prevent xghost* from being re-purposed for subsequent Mult
// calls
MFEM_DEVICE_SYNC;
}
ParDerefineMatrixOp::ParDerefineMatrixOp(ParFiniteElementSpace &fespace_,
int old_ndofs,
const Table *old_elem_dof,
const Table *old_elem_fos)
: Operator(fespace_.GetVSize(), old_ndofs * fespace_.GetVDim()),
fespace(&fespace_)
{
static Kernels kernels;
constexpr int max_team_size = 256;
const int NRanks = fespace->GetNRanks();
const int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
MFEM_VERIFY(fespace->Nonconforming(),
"Not implemented for conforming meshes.");
MFEM_VERIFY(fespace->old_dof_offsets[nrk],
"Missing previous (finer) space.");
const int MyRank = fespace->GetMyRank();
ParNCMesh *old_pncmesh = fespace->GetParMesh()->pncmesh;
const CoarseFineTransformations &dtrans =
old_pncmesh->GetDerefinementTransforms();
const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
const bool is_dg = fespace->FEColl()->GetContType()
== FiniteElementCollection::DISCONTINUOUS;
DenseMatrix localRVO; // for variable-order only
DenseTensor localR[Geometry::NumGeom];
int diag_rows = 0;
int off_diag_rows = 0;
int diag_cols = 0;
auto get_ldofs = [&](int k) -> int
{
const Embedding &emb = dtrans.embeddings[k];
if (fespace->IsVariableOrder())
{
const FiniteElement *fe = fespace->GetFE(emb.parent);
return fe->GetDof();
}
else
{
Geometry::Type geom =
fespace->GetParMesh()->GetElementBaseGeometry(emb.parent);
return fespace->FEColl()->FiniteElementForGeometry(geom)->GetDof();
}
};
Array<int> dofs, old_dofs;
max_rows = 1;
// first pass:
// - determine memory block lengths
// - identify dofs in x we need to send/receive
// don't need to send the indices, fine rank will re-arrange and sign
// change x before transmitting the ghost data
// key: coarse rank to send to
// value: old dofs to send (with sign)
std::map<int, std::vector<int>> to_send;
// key: fine rank
// value: indices into dtrans.embeddings
std::map<int, std::vector<int>> od_ks;
// key: fine rank
// value: recv segment length
std::map<int, int> od_seg_lens;
int send_len = 0;
int recv_len = 0;
// size of block_storage, if fespace->IsVariableOrder()
// otherwise unused
int total_size = 0;
int num_diagonal_blocks = 0;
int num_offdiagonal_blocks = 0;
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
{
const Embedding &emb = dtrans.embeddings[k];
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)
{
// this rank needs to send data in x to course_rank
old_elem_dof->GetRow(k, old_dofs);
auto &tmp = to_send[coarse_rank];
send_len += old_dofs.Size();
for (int i = 0; i < old_dofs.Size(); ++i)
{
tmp.emplace_back(old_dofs[i]);
}
}
else if (coarse_rank == MyRank && fine_rank != MyRank)
{
// this rank needs to receive data in x from fine_rank
MFEM_ASSERT(emb.parent >= 0, "");
auto ldofs = get_ldofs(k);
off_diag_rows += ldofs;
recv_len += ldofs;
od_ks[fine_rank].emplace_back(k);
od_seg_lens[fine_rank] += ldofs;
++num_offdiagonal_blocks;
if (fespace->IsVariableOrder())
{
total_size += ldofs * ldofs;
}
}
else if (coarse_rank == MyRank && fine_rank == MyRank)
{
MFEM_ASSERT(emb.parent >= 0, "");
// diagonal
++num_diagonal_blocks;
auto ldofs = get_ldofs(k);
diag_rows += ldofs;
diag_cols += ldofs;
if (fespace->IsVariableOrder())
{
total_size += ldofs * ldofs;
}
}
}
send_segments.SetSize(to_send.size() + 1);
send_segments.HostWrite();
send_ranks.SetSize(to_send.size());
send_ranks.HostWrite();
{
int idx = 0;
send_segments[0] = 0;
for (auto &tmp : to_send)
{
send_ranks[idx] = tmp.first;
send_segments[idx + 1] = send_segments[idx] + tmp.second.size();
++idx;
}
}
recv_segment_idcs.SetSize(off_diag_rows);
recv_segment_idcs.HostWrite();
recv_segments.SetSize(od_ks.size() + 1);
recv_segments.HostWrite();
recv_ranks.SetSize(od_ks.size());
recv_ranks.HostWrite();
// set sizes
row_idcs.SetSize(diag_rows);
row_idcs.HostWrite();
row_off_diag_idcs.SetSize(off_diag_rows);
row_off_diag_idcs.HostWrite();
col_idcs.SetSize(diag_cols);
col_idcs.HostWrite();
block_row_idcs_offsets.SetSize(num_diagonal_blocks + 1);
block_row_idcs_offsets.HostWrite();
block_col_idcs_offsets.SetSize(num_diagonal_blocks + 1);
block_col_idcs_offsets.HostWrite();
block_off_diag_row_idcs_offsets.SetSize(num_offdiagonal_blocks + 1);
block_off_diag_row_idcs_offsets.HostWrite();
block_off_diag_col_offsets.SetSize(num_offdiagonal_blocks);
block_off_diag_col_offsets.HostWrite();
block_off_diag_widths.SetSize(num_offdiagonal_blocks);
block_off_diag_widths.HostWrite();
pack_col_idcs.SetSize(send_len);
// memory manager doesn't appear to have a graceful fallback for
// HOST_PINNED if not built with CUDA or HIP
#if defined(MFEM_USE_CUDA) or defined(MFEM_USE_HIP)
xghost_send.SetSize(send_len * fespace->GetVDim(),
Device::GetGPUAwareMPI() ? MemoryType::DEFAULT
: MemoryType::HOST_PINNED);
xghost_recv.SetSize(recv_len * fespace->GetVDim(),
Device::GetGPUAwareMPI() ? MemoryType::DEFAULT
: MemoryType::HOST_PINNED);
#else
xghost_send.SetSize(send_len * fespace->GetVDim());
xghost_recv.SetSize(recv_len * fespace->GetVDim());
#endif
send_permutations.SetSize(send_len);
send_segment_idcs.SetSize(send_len);
block_offsets.SetSize(num_diagonal_blocks);
block_offsets.HostWrite();
off_diag_block_offsets.SetSize(num_offdiagonal_blocks);
off_diag_block_offsets.HostWrite();
int geom_offsets[Geometry::NumGeom];
real_t *bs_ptr;
if (fespace->IsVariableOrder())
{
block_storage.SetSize(total_size);
bs_ptr = block_storage.HostWrite();
// compute block data later
}
else
{
// compression scheme:
// block_offsets is the start of each block, potentially repeated
// only need to store localR for used shapes
Mesh::GeometryList elem_geoms(*fespace->GetMesh());
int size = 0;
for (int i = 0; i < elem_geoms.Size(); ++i)
{
fespace->GetLocalDerefinementMatrices(elem_geoms[i],
localR[elem_geoms[i]]);
geom_offsets[elem_geoms[i]] = size;
size += localR[elem_geoms[i]].TotalSize();
}
block_storage.SetSize(size);
bs_ptr = block_storage.HostWrite();
// copy blocks into block_storage
for (int i = 0; i < elem_geoms.Size(); ++i)
{
std::copy(localR[elem_geoms[i]].Data(),
localR[elem_geoms[i]].Data()
+ localR[elem_geoms[i]].TotalSize(),
bs_ptr);
bs_ptr += localR[elem_geoms[i]].TotalSize();
}
}
// second pass:
// - initialize buffers
{
auto ptr = send_permutations.HostWrite();
auto ptr2 = send_segment_idcs.HostWrite();
int i = 0;
for (auto &v : to_send)
{
ptr = std::copy(v.second.begin(), v.second.end(), ptr);
for (size_t idx = 0; idx < v.second.size(); ++idx)
{
*ptr2 = i;
++ptr2;
}
++i;
}
}
block_row_idcs_offsets[0] = 0;
block_col_idcs_offsets[0] = 0;
block_off_diag_row_idcs_offsets[0] = 0;
Array<int> mark(fespace->GetNDofs());
mark = 0;
{
int idx = 0;
recv_segments[0] = 0;
for (auto &v : od_seg_lens)
{
recv_ranks[idx] = v.first;
recv_segments[idx + 1] = recv_segments[idx] + v.second;
++idx;
}
}
// key: index into dtrans.embeddings
// value: off-diagonal block offset, od_ridx, seg id
std::unordered_map<int, std::array<int, 3>> ks_map;
{
int od_ridx = 0;
int seg_id = 0;
for (auto &v1 : od_ks)
{
for (auto k : v1.second)
{
auto &tmp = ks_map[k];
tmp[0] = ks_map.size() - 1;
tmp[1] = od_ridx;
tmp[2] = seg_id;
od_ridx += get_ldofs(k);
}
++seg_id;
}
}
int diag_idx = 0;
int var_offset = 0;
int ridx = 0;
int cidx = 0;
// can't break this up into separate diagonals/off-diagonals loops because
// of mark
for (int k = 0; k < dtrans.embeddings.Size(); ++k)
{
const Embedding &emb = dtrans.embeddings[k];
if (emb.parent < 0)
{
continue;
}
int fine_rank = old_ranks[k];
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
: old_pncmesh->ElementRank(emb.parent);
if (coarse_rank == MyRank)
{
// either diagonal or off-diagonal
Geometry::Type geom =
fespace->GetMesh()->GetElementBaseGeometry(emb.parent);
if (fespace->IsVariableOrder())
{
const FiniteElement *fe = fespace->GetFE(emb.parent);
const DenseTensor &pmats = dtrans.point_matrices[geom];
const int ldof = fe->GetDof();
IsoparametricTransformation isotr;
isotr.SetIdentityTransformation(geom);
localRVO.SetSize(ldof, ldof);
isotr.SetPointMat(pmats(emb.matrix));
// Local restriction is size ldofxldof assuming that the parent
// and child are of same polynomial order.
fe->GetLocalRestriction(isotr, localRVO);
// copy block
auto s = localRVO.Height() * localRVO.Width();
std::copy(localRVO.Data(), localRVO.Data() + s, bs_ptr);
bs_ptr += s;
}
DenseMatrix &lR =
fespace->IsVariableOrder() ? localRVO : localR[geom](emb.matrix);
max_rows = std::max(lR.Height(), max_rows);
auto size = lR.Height() * lR.Width();
fespace->elem_dof->GetRow(emb.parent, dofs);
if (fine_rank == MyRank)
{
// diagonal
old_elem_dof->GetRow(k, old_dofs);
MFEM_VERIFY(old_dofs.Size() == dofs.Size(),
"Parent and child must have same #dofs.");
block_row_idcs_offsets[diag_idx + 1] =
block_row_idcs_offsets[diag_idx] + lR.Height();
block_col_idcs_offsets[diag_idx + 1] =
block_col_idcs_offsets[diag_idx] + lR.Width();
if (fespace->IsVariableOrder())
{
block_offsets[diag_idx] = var_offset;
var_offset += size;
}
else
{
block_offsets[diag_idx] = geom_offsets[geom] + size * emb.matrix;
}
for (int i = 0; i < lR.Height(); ++i, ++ridx)
{
if (!std::isfinite(lR(i, 0)))
{
row_idcs[ridx] = INT_MAX;
continue;
}
int r = dofs[i];
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
row_idcs[ridx] = r;
mark[m] = 1;
}
else
{
row_idcs[ridx] = INT_MAX;
}
}
for (int i = 0; i < lR.Width(); ++i, ++cidx)
{
col_idcs[cidx] = old_dofs[i];
}
++diag_idx;
}
else
{
// off-diagonal
auto &tmp = ks_map.at(k);
auto od_idx = tmp[0];
auto od_ridx = tmp[1];
block_off_diag_row_idcs_offsets[od_idx + 1] =
block_off_diag_row_idcs_offsets[od_idx] + lR.Height();
block_off_diag_col_offsets[od_idx] = od_ridx;
block_off_diag_widths[od_idx] = lR.Width();
recv_segment_idcs[od_idx] = tmp[2];
if (fespace->IsVariableOrder())
{
off_diag_block_offsets[od_idx] = var_offset;
var_offset += size;
}
else
{
off_diag_block_offsets[od_idx] =
geom_offsets[geom] + size * emb.matrix;
}
for (int i = 0; i < lR.Height(); ++i, ++od_ridx)
{
if (!std::isfinite(lR(i, 0)))
{
row_off_diag_idcs[od_ridx] = INT_MAX;
continue;
}
int r = dofs[i];
int m = (r >= 0) ? r : (-1 - r);
if (is_dg || !mark[m])
{
row_off_diag_idcs[od_ridx] = r;
mark[m] = 1;
}
else
{
row_off_diag_idcs[od_ridx] = INT_MAX;
}
}
++od_idx;
}
}
}
// if not using GPU, set max_rows/max_cols to zero
if (Device::Allows(Backend::DEVICE_MASK))
{
max_rows = std::min(max_rows, max_team_size);
}
else
{
max_rows = 1;
}
requests.reserve(recv_ranks.Size() + send_ranks.Size());
}
} // namespace mfem
/// \endcond DO_NOT_DOCUMENT
#endif
-111
View File
@@ -1,111 +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_PDEREFMAT_OP
#define MFEM_PDEREFMAT_OP
#include "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#include "kernel_dispatch.hpp"
#include <vector>
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
struct ParDerefineMatrixOp : public Operator
{
ParFiniteElementSpace *fespace;
/// offsets into block_storage for diagonal
Array<int> block_offsets;
/// offsets into row_idcs for diagonal
Array<int> block_row_idcs_offsets;
/// offsets into col_idcs for diagonal
Array<int> block_col_idcs_offsets;
/// offsets into block_storage for off-diagonal
Array<int> off_diag_block_offsets;
/// offsets into row_idcs for off-diagonal
Array<int> block_off_diag_row_idcs_offsets;
Array<int> block_off_diag_col_offsets;
Array<int> block_off_diag_widths;
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
/// negative means the row data should be negated.
/// only for diagonal blocks
Array<int> row_idcs;
/// mapping for col dofs, negative means the col data should be negated.
/// only for diagonal blocks
Array<int> col_idcs;
Array<int> pack_col_idcs;
/// mapping for row dofs, INT_MAX indicates the block row should be ignored.
/// negative means the row data should be negated.
/// only for off-diagonal blocks
Array<int> row_off_diag_idcs;
/// dense block matrices which can be reused to construct the full matrix
/// operation. These are stored contiguously and blocks have no restrictions
/// on shape (can be rectangle and differ from block to block).
/// This is only for the diagonal block.
Vector block_storage;
/// maximum height of any block in block_storage for GPU
/// parallelization, or 1 for CPU runs.
int max_rows;
/// quasi Ordering::byNODES, broken into sections by ranks we need to send
/// the data to
mutable Vector xghost_send;
/// quasi Ordering::byNODES, broken into sections by ranks we received
/// the data from
mutable Vector xghost_recv;
/// maps off-diagonal k to segment
Array<int> recv_segment_idcs;
/// cumulative count of dofs which will be received from other ranks
Array<int> recv_segments;
/// Source rank of each recv segment
Array<int> recv_ranks;
/// What send segment each entry in send_permutations corresponds to
Array<int> send_segment_idcs;
/// cumulative count of dofs which will be sent to other ranks
Array<int> send_segments;
/// Destination rank of each send segment
Array<int> send_ranks;
/// how to permute/sign change values from our local x to send to other ranks
Array<int> send_permutations;
/// internal buffer for MPI requests
mutable std::vector<MPI_Request> requests;
using MultKernelType = void (*)(const ParDerefineMatrixOp &, const Vector &,
Vector &);
/// template args: ordering, atomic
MFEM_REGISTER_KERNELS(MultKernel, MultKernelType, (Ordering::Type, bool));
struct Kernels
{
Kernels();
};
void Mult(const Vector &x, Vector &y) const;
ParDerefineMatrixOp(ParFiniteElementSpace &fespace_, int old_ndofs,
const Table *old_elem_dof, const Table *old_elem_fos);
};
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
#endif
+33 -58
View File
@@ -22,13 +22,12 @@
#include "../mesh/mesh_headers.hpp"
#include "../general/binaryio.hpp"
#include "pderefmat_op.hpp"
#include <limits>
#include <list>
namespace mfem
{
ParFiniteElementSpace::ParFiniteElementSpace(
const ParFiniteElementSpace &orig, ParMesh *pmesh,
const FiniteElementCollection *fec)
@@ -63,11 +62,9 @@ ParFiniteElementSpace::ParFiniteElementSpace(
ParFiniteElementSpace::ParFiniteElementSpace(
ParMesh *pm, const FiniteElementCollection *f, int dim, int ordering)
: FiniteElementSpace((MFEM_PERF_BEGIN(_MFEM_FUNC_NAME), pm),
f, dim, ordering)
: FiniteElementSpace(pm, f, dim, ordering)
{
ParInit(pm);
MFEM_PERF_END(_MFEM_FUNC_NAME);
}
ParFiniteElementSpace::ParFiniteElementSpace(
@@ -94,7 +91,6 @@ ParNURBSExtension *ParFiniteElementSpace::MakeLocalNURBSext(
void ParFiniteElementSpace::ParInit(ParMesh *pm)
{
MFEM_PERF_FUNCTION;
pmesh = pm;
pncmesh = nullptr;
@@ -184,7 +180,6 @@ void ParFiniteElementSpace::CommunicateGhostOrder()
void ParFiniteElementSpace::Construct()
{
MFEM_PERF_FUNCTION;
if (NURBSext)
{
ConstructTrueNURBSDofs();
@@ -843,8 +838,6 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
if (P) { return; }
MFEM_PERF_FUNCTION;
if (!nd_strias)
{
// Safe to assume 1-1 correspondence between shared dofs
@@ -1430,7 +1423,6 @@ const Operator *ParFiniteElementSpace::GetRestrictionOperator() const
if (NRanks == 1)
{
R_transpose.reset(new IdentityOperator(GetTrueVSize()));
Rconf = new IdentityOperator(GetTrueVSize());
}
else
{
@@ -1443,8 +1435,8 @@ const Operator *ParFiniteElementSpace::GetRestrictionOperator() const
R_transpose.reset(
new DeviceConformingProlongationOperator(*this, true));
}
Rconf = new TransposeOperator(*R_transpose);
}
Rconf = new TransposeOperator(*R_transpose);
return Rconf;
}
else
@@ -4495,6 +4487,13 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
return M;
}
struct DerefDofMessage
{
std::vector<HYPRE_BigInt> dofs;
MPI_Request request;
};
HypreParMatrix*
ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
const Table* old_elem_dof,
@@ -4537,13 +4536,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
old_pncmesh->GetDerefinementTransforms();
const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
// key: other rank
// value: send or recieve buffer
std::map<int, std::vector<HYPRE_BigInt>> to_send;
std::map<int, std::vector<HYPRE_BigInt>> to_recv;
// key: index into dtrans.embeddings
// value: [start, stop]
std::unordered_map<int, std::array<size_t, 2>> recv_messages;
std::map<int, DerefDofMessage> messages;
HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
? old_dof_offsets[0] : old_dof_offsets[MyRank];
@@ -4563,46 +4556,30 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
old_elem_dof->GetRow(k, dofs);
DofsToVDofs(dofs, old_ndofs);
std::vector<HYPRE_BigInt>& send_buf = to_send[coarse_rank];
auto pos = send_buf.size();
send_buf.resize(pos + dofs.Size());
DerefDofMessage &msg = messages[k];
msg.dofs.resize(dofs.Size());
for (int i = 0; i < dofs.Size(); i++)
{
send_buf[pos + i] = old_offset + dofs[i];
msg.dofs[i] = old_offset + dofs[i];
}
MPI_Isend(&msg.dofs[0], static_cast<int>(msg.dofs.size()), HYPRE_MPI_BIG_INT,
coarse_rank, 291, MyComm, &msg.request);
}
else if (coarse_rank == MyRank && fine_rank != MyRank)
{
MFEM_ASSERT(emb.parent >= 0, "");
Geometry::Type geom = mesh->GetElementBaseGeometry(emb.parent);
std::vector<HYPRE_BigInt>& recv_buf = to_recv[fine_rank];
auto& msg = recv_messages[k];
msg[0] = recv_buf.size();
recv_buf.resize(recv_buf.size() + ldof[geom] * vdim);
msg[1] = recv_buf.size();
}
}
DerefDofMessage &msg = messages[k];
msg.dofs.resize(ldof[geom]*vdim);
// assume embedding orders are consistent (i.e. what we expect to receive
// first from a given rank is sent first, etc.)
std::vector<MPI_Request> requests;
requests.reserve(to_send.size() + to_recv.size());
// enqueue recvs
for (auto &v : to_recv)
{
requests.emplace_back();
MPI_Irecv(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA, MyComm,
&requests.back());
}
// enqueue sends
for (auto &v : to_send)
{
requests.emplace_back();
MPI_Isend(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
MessageTag::DEREFINEMENT_MATRIX_CONSTRUCTION_DATA, MyComm,
&requests.back());
MPI_Irecv(&msg.dofs[0], ldof[geom]*vdim, HYPRE_MPI_BIG_INT,
fine_rank, 291, MyComm, &msg.request);
}
// TODO: coalesce Isends/Irecvs to the same rank. Typically, on uniform
// derefinement, there should be just one send to MyRank-1 and one recv
// from MyRank+1
}
DenseTensor localR[Geometry::NumGeom];
@@ -4660,7 +4637,10 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
diag->Finalize();
// wait for all sends/receives to complete
MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
for (auto it = messages.begin(); it != messages.end(); ++it)
{
MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
}
// create the off-diagonal part of the derefinement matrix
SparseMatrix *offd = new SparseMatrix(ndofs*vdim, 1);
@@ -4681,14 +4661,13 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
elem_dof->GetRow(emb.parent, dofs);
auto& odofs = to_recv.at(fine_rank);
auto &msg = recv_messages[k];
MFEM_ASSERT(msg[1] > msg[0], "");
DerefDofMessage &msg = messages[k];
MFEM_ASSERT(msg.dofs.size(), "");
for (int vd = 0; vd < vdim; vd++)
{
MFEM_ASSERT(ldof[geom], "");
HYPRE_BigInt *remote_dofs = odofs.data() + msg[0] + vd * ldof[geom];
HYPRE_BigInt* remote_dofs = &msg.dofs[vd*ldof[geom]];
for (int i = 0; i < lR.Height(); i++)
{
@@ -4715,6 +4694,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
}
}
messages.clear();
offd->Finalize(0);
offd->SetWidth(static_cast<int>(col_map.size()));
@@ -4966,13 +4946,8 @@ void ParFiniteElementSpace::Update(bool want_transform)
case Mesh::DEREFINE:
{
#if 0
Th.Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
old_elem_fos));
#else
Th.Reset(new ParDerefineMatrixOp(*this, old_ndofs, old_elem_dof,
old_elem_fos));
#endif
if (Nonconforming())
{
Th.SetOperatorOwner(false);
-3
View File
@@ -24,12 +24,9 @@
namespace mfem
{
struct ParDerefineMatrixOp;
/// Abstract parallel finite element space.
class ParFiniteElementSpace : public FiniteElementSpace
{
friend struct ParDerefineMatrixOp;
private:
/// MPI data.
MPI_Comm MyComm;
-2
View File
@@ -45,8 +45,6 @@ void ParLinearForm::MakeRef(ParFiniteElementSpace *pf, Vector &v, int v_offset)
void ParLinearForm::Assemble()
{
MFEM_PERF_FUNCTION;
LinearForm::Assemble();
if (interior_face_integs.Size())
+10 -10
View File
@@ -214,16 +214,16 @@ public:
inline void QuadratureFunction::GetValues(
int idx, Vector &values)
{
const int s_offset = qspace->Offset(idx);
const int sl_size = qspace->Offset(idx + 1) - s_offset;
const int s_offset = qspace->offsets[idx];
const int sl_size = qspace->offsets[idx+1] - s_offset;
values.MakeRef(*this, vdim*s_offset, vdim*sl_size);
}
inline void QuadratureFunction::GetValues(
int idx, Vector &values) const
{
const int s_offset = qspace->Offset(idx);
const int sl_size = qspace->Offset(idx + 1) - s_offset;
const int s_offset = qspace->offsets[idx];
const int sl_size = qspace->offsets[idx+1] - s_offset;
values.SetSize(vdim*sl_size);
values.HostWrite();
const real_t *q = HostRead() + vdim*s_offset;
@@ -236,14 +236,14 @@ inline void QuadratureFunction::GetValues(
inline void QuadratureFunction::GetValues(
int idx, const int ip_num, Vector &values)
{
const int s_offset = qspace->Offset(idx) * vdim + ip_num * vdim;
const int s_offset = qspace->offsets[idx] * vdim + ip_num * vdim;
values.MakeRef(*this, s_offset, vdim);
}
inline void QuadratureFunction::GetValues(
int idx, const int ip_num, Vector &values) const
{
const int s_offset = qspace->Offset(idx) * vdim + ip_num * vdim;
const int s_offset = qspace->offsets[idx] * vdim + ip_num * vdim;
values.SetSize(vdim);
values.HostWrite();
const real_t *q = HostRead() + s_offset;
@@ -256,8 +256,8 @@ inline void QuadratureFunction::GetValues(
inline void QuadratureFunction::GetValues(
int idx, DenseMatrix &values)
{
const int s_offset = qspace->Offset(idx);
const int sl_size = qspace->Offset(idx + 1) - s_offset;
const int s_offset = qspace->offsets[idx];
const int sl_size = qspace->offsets[idx+1] - s_offset;
// Make the values matrix memory an alias of the quadrature function memory
Memory<real_t> &values_mem = values.GetMemory();
values_mem.Delete();
@@ -268,8 +268,8 @@ inline void QuadratureFunction::GetValues(
inline void QuadratureFunction::GetValues(
int idx, DenseMatrix &values) const
{
const int s_offset = qspace->Offset(idx);
const int sl_size = qspace->Offset(idx + 1) - s_offset;
const int s_offset = qspace->offsets[idx];
const int sl_size = qspace->offsets[idx+1] - s_offset;
values.SetSize(vdim, sl_size);
values.HostWrite();
const real_t *q = HostRead() + vdim*s_offset;
+8 -25
View File
@@ -96,33 +96,17 @@ void QuadratureSpaceBase::Integrate(VectorCoefficient &coeff,
void QuadratureSpace::ConstructOffsets()
{
MFEM_PERF_FUNCTION;
const int num_elem = mesh.GetNE();
ne = num_elem;
if (mesh.GetNumGeometries(mesh.Dimension()) == 1)
offsets.SetSize(num_elem + 1);
int offset = 0;
for (int i = 0; i < num_elem; i++)
{
Array<Geometry::Type> geoms;
mesh.GetGeometries(mesh.Dimension(), geoms);
offsets.SetSize(1);
offsets.HostWrite();
offsets[0] = int_rule[geoms[0]]->GetNPoints();
size = num_elem * offsets[0];
}
else
{
offsets.SetSize(num_elem + 1);
int offset = 0;
for (int i = 0; i < num_elem; i++)
{
offsets[i] = offset;
const Geometry::Type geom = mesh.GetElementBaseGeometry(i);
MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule.");
offset += int_rule[geom]->GetNPoints();
}
offsets[num_elem] = offset;
size = offsets.Last();
offsets[i] = offset;
int geom = mesh.GetElementBaseGeometry(i);
MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule.");
offset += int_rule[geom]->GetNPoints();
}
offsets[num_elem] = size = offset;
}
void QuadratureSpace::Construct()
@@ -204,7 +188,6 @@ void FaceQuadratureSpace::ConstructOffsets()
{
face_indices.SetSize(num_faces);
offsets.SetSize(num_faces + 1);
ne = num_faces;
int offset = 0;
int f_idx = 0;
for (int i = 0; i < mesh.GetNumFacesWithGhost(); i++)
+4 -26
View File
@@ -30,17 +30,13 @@ protected:
Mesh &mesh; ///< The underlying mesh.
int order; ///< The order of integration rule.
int size; ///< Total number of quadrature points.
int ne; ///< Actual number of entities
mutable Vector weights; ///< Integration weights.
mutable long nodes_sequence = 0; ///< Nodes counter for cache invalidation.
/// @brief Entity quadrature point offset array.
///
/// Supports a constant compression scheme for meshes which have a single
/// geometry type. When compressed, will have a single value. The true offset
/// can be computed as i * offsets[0], where i is the entity index. Otherwise
/// has size num_entities + 1.
/// @brief Entity quadrature point offset array, of size num_entities + 1.
///
/// The quadrature point values for entity i are stored in the indices between
/// offsets[i] and offsets[i+1].
Array<int> offsets;
/// The quadrature rules used for each geometry type.
const IntegrationRule *int_rule[Geometry::NumGeom];
@@ -63,24 +59,6 @@ protected:
void ConstructWeights() const;
public:
/// @brief Gets the offset for a given entity @a idx.
///
/// The quadrature point values for entity i are stored in the indices
/// between Offset(i) and Offset(i+1)
int Offset(int idx) const
{
return (offsets.Size() == 1) ? (idx * offsets[0]) : offsets[idx];
}
/// @brief Entity quadrature point offset array.
///
/// Supports a constant compression scheme for meshes which have a single
/// geometry type. When compressed, will have a single value. The true offset
/// can be computed as i * offsets[0], where i is the entity index. Otherwise
/// has size num_entities + 1.
///
const Array<int> &Offsets() const { return offsets; }
/// Return the total number of quadrature points.
int GetSize() const { return size; }
@@ -88,7 +66,7 @@ public:
int GetOrder() const { return order; }
/// Return the number of entities.
int GetNE() const { return ne; }
int GetNE() const { return offsets.Size() - 1; }
/// Returns the mesh.
inline Mesh *GetMesh() const { return &mesh; }
-1
View File
@@ -503,7 +503,6 @@ void QuadratureInterpolator::Mult(const Vector &e_vec,
Vector &q_der,
Vector &q_det) const
{
MFEM_PERF_FUNCTION;
using namespace internal::quadrature_interpolator;
const int ne = fespace->GetNE();
+1 -6
View File
@@ -25,7 +25,7 @@ namespace mfem
ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
ElementDofOrdering e_ordering)
: fes((MFEM_PERF_BEGIN(_MFEM_FUNC_NAME), f)),
: fes(f),
ne(fes.GetNE()),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
@@ -104,13 +104,10 @@ ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
offsets[i] = offsets[i - 1];
}
offsets[0] = 0;
MFEM_PERF_END(_MFEM_FUNC_NAME);
}
void ElementRestriction::Mult(const Vector& x, Vector& y) const
{
MFEM_PERF_FUNCTION;
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
@@ -155,8 +152,6 @@ void ElementRestriction::AbsMult(const Vector& x, Vector& y) const
template <bool ADD>
void ElementRestriction::TAddMultTranspose(const Vector& x, Vector& y) const
{
MFEM_PERF_FUNCTION;
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
+17 -196
View File
@@ -13,7 +13,6 @@
#include "bilinearform.hpp"
#include "pbilinearform.hpp"
#include "../general/forall.hpp"
#include "kernels.hpp"
namespace mfem
{
@@ -2323,76 +2322,6 @@ void Prolongation2D(const int NE, const int D1D, const int Q1D,
});
}
template <int DLO, int DHI>
static void SmemProlongation3D(const int NE,
const Vector& localL, Vector& localH,
const Array<real_t> &b, const Vector& mask)
{
MFEM_PERF_FUNCTION;
auto u_lo = Reshape(localL.Read(), DLO, DLO, DLO, NE);
auto u_hi = Reshape(localH.Write(), DHI, DHI, DHI, NE);
auto d_b = Reshape(b.Read(), DHI, DLO);
auto m_ = Reshape(mask.Read(), DHI, DHI, DHI, NE);
mfem::forall_2D(NE, DHI, DHI, [=] MFEM_HOST_DEVICE (int e)
{
// Load B into shared memory
MFEM_SHARED real_t s_B[DHI*DLO];
kernels::internal::LoadBt<DLO,DHI>(DLO,DHI,d_b,s_B);
const DeviceMatrix B(s_B, DHI, DLO);
MFEM_SHARED real_t s_u[DHI*DHI*DLO];
const DeviceCube u(s_u, DHI, DHI, DLO);
real_t v[DHI];
MFEM_FOREACH_THREAD(lx,x,DLO)
{
MFEM_FOREACH_THREAD(ly,y,DLO)
{
for (int hz = 0; hz < DHI; ++hz) { v[hz] = 0.0; }
for (int lz = 0; lz < DLO; ++lz)
{
const real_t XYZ = u_lo(lx,ly,lz,e);
for (int hz = 0; hz < DHI; ++hz) { v[hz] += XYZ * B(hz,lz); }
}
for (int hz = 0; hz < DHI; ++hz) { u(hz,ly,lx) = v[hz]; }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(hz,y,DHI)
{
MFEM_FOREACH_THREAD(lx,x,DLO)
{
for (int hy = 0; hy < DHI; ++hy) { v[hy] = 0.0; }
for (int ly = 0; ly < DLO; ++ly)
{
const real_t zYX = u(hz,ly,lx);
for (int hy = 0; hy < DHI; ++hy) { v[hy] += zYX * B(hy,ly); }
}
for (int hy = 0; hy < DHI; ++hy) { u(hz,hy,lx) = v[hy]; }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(hz,y,DHI)
{
MFEM_FOREACH_THREAD(hy,x,DHI)
{
for (int hx = 0; hx < DHI; ++hx) { v[hx] = 0.0; }
for (int lx = 0; lx < DLO; ++lx)
{
const real_t zyX = u(hz,hy,lx);
for (int hx = 0; hx < DHI; ++hx) { v[hx] += zyX * B(hx,lx); }
}
for (int hx = 0; hx < DHI; ++hx)
{
u_hi(hx,hy,hz,e) = m_(hx,hy,hz,e)*v[hx];
}
}
}
});
}
void Prolongation3D(const int NE, const int D1D, const int Q1D,
const Vector& localL, Vector& localH,
const Array<real_t>& B, const Vector& mask)
@@ -2474,9 +2403,9 @@ void Prolongation3D(const int NE, const int D1D, const int Q1D,
});
}
void ProlongationTranspose2D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<real_t>& Bt, const Vector& mask)
void Restriction2D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<real_t>& Bt, const Vector& mask)
{
auto x_ = Reshape(localH.Read(), Q1D, Q1D, NE);
auto y_ = Reshape(localL.Write(), D1D, D1D, NE);
@@ -2519,80 +2448,9 @@ void ProlongationTranspose2D(const int NE, const int D1D, const int Q1D,
}
});
}
template <int DLO, int DHI>
static void SmemProlongationTranspose3D(
const int NE, const Vector& localH, Vector& localL,
const Array<real_t>& bt, const Vector& mask)
{
MFEM_PERF_FUNCTION;
auto u_h = Reshape(localH.Read(), DHI, DHI, DHI, NE);
auto u_l = Reshape(localL.Write(), DLO, DLO, DLO, NE);
auto d_bt = Reshape(bt.Read(), DLO, DHI);
auto m_ = Reshape(mask.Read(), DHI, DHI, DHI, NE);
mfem::forall_2D(NE, DHI, DHI, [=] MFEM_HOST_DEVICE (int e)
{
// Load Bt into shared memory
MFEM_SHARED real_t s_Bt[DHI*DLO];
kernels::internal::LoadBt<DHI,DLO>(DHI,DLO,d_bt,s_Bt);
const DeviceMatrix Bt(s_Bt, DLO, DHI);
MFEM_SHARED real_t s_u[DLO*DHI*DHI];
const DeviceCube u(s_u, DLO, DHI, DHI);
real_t v[DLO];
MFEM_FOREACH_THREAD(hx,x,DHI)
{
MFEM_FOREACH_THREAD(hy,y,DHI)
{
for (int lz = 0; lz < DLO; ++lz) { v[lz] = 0.0; }
for (int hz = 0; hz < DHI; ++hz)
{
const real_t XYZ = m_(hx,hy,hz,e)*u_h(hx,hy,hz,e);
for (int lz = 0; lz < DLO; ++lz) { v[lz] += XYZ * Bt(lz,hz); }
}
for (int lz = 0; lz < DLO; ++lz) { u(lz,hy,hx) = v[lz]; }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(lz,y,DLO)
{
MFEM_FOREACH_THREAD(hx,x,DHI)
{
for (int ly = 0; ly < DLO; ++ly) { v[ly] = 0.0; }
for (int hy = 0; hy < DHI; ++hy)
{
const real_t zYX = u(lz,hy,hx);
for (int ly = 0; ly < DLO; ++ly) { v[ly] += zYX * Bt(ly,hy); }
}
for (int ly = 0; ly < DLO; ++ly) { u(lz,ly,hx) = v[ly]; }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(lz,y,DLO)
{
MFEM_FOREACH_THREAD(ly,x,DLO)
{
for (int lx = 0; lx < DLO; ++lx) { v[lx] = 0.0; }
for (int hx = 0; hx < DHI; ++hx)
{
const real_t zyX = u(lz,ly,hx);
for (int lx = 0; lx < DLO; ++lx) { v[lx] += zyX * Bt(lx,hx); }
}
for (int lx = 0; lx < DLO; ++lx)
{
u_l(lx,ly,lz,e) = v[lx];
}
}
}
});
}
void ProlongationTranspose3D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<real_t>& Bt, const Vector& mask)
void Restriction3D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<real_t>& Bt, const Vector& mask)
{
auto x_ = Reshape(localH.Read(), Q1D, Q1D, Q1D, NE);
auto y_ = Reshape(localL.Write(), D1D, D1D, D1D, NE);
@@ -2660,15 +2518,11 @@ void ProlongationTranspose3D(const int NE, const int D1D, const int Q1D,
}
});
}
} // namespace TransferKernels
void TensorProductPRefinementTransferOperator::Mult(const Vector& x,
Vector& y) const
{
MFEM_PERF_FUNCTION;
using namespace TransferKernels;
if (lFESpace.GetMesh()->GetNE() == 0)
{
return;
@@ -2677,25 +2531,11 @@ void TensorProductPRefinementTransferOperator::Mult(const Vector& x,
elem_restrict_lex_l->Mult(x, localL);
if (dim == 2)
{
Prolongation2D(NE, D1D, Q1D, localL, localH, B, mask);
TransferKernels::Prolongation2D(NE, D1D, Q1D, localL, localH, B, mask);
}
else if (dim == 3)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x23:
SmemProlongation3D<2,3>(NE, localL, localH, B, mask); break;
case 0x24:
SmemProlongation3D<2,4>(NE, localL, localH, B, mask); break;
case 0x35:
SmemProlongation3D<3,5>(NE, localL, localH, B, mask); break;
case 0x46:
SmemProlongation3D<4,6>(NE, localL, localH, B, mask); break;
case 0x47:
SmemProlongation3D<4,7>(NE, localL, localH, B, mask); break;
default:
Prolongation3D(NE, D1D, Q1D, localL, localH, B, mask); break;
}
TransferKernels::Prolongation3D(NE, D1D, Q1D, localL, localH, B, mask);
}
else
{
@@ -2709,9 +2549,6 @@ void TensorProductPRefinementTransferOperator::Mult(const Vector& x,
void TensorProductPRefinementTransferOperator::MultTranspose(const Vector& x,
Vector& y) const
{
MFEM_PERF_FUNCTION;
using namespace TransferKernels;
if (lFESpace.GetMesh()->GetNE() == 0)
{
return;
@@ -2720,25 +2557,11 @@ void TensorProductPRefinementTransferOperator::MultTranspose(const Vector& x,
elem_restrict_lex_h->Mult(x, localH);
if (dim == 2)
{
ProlongationTranspose2D(NE, D1D, Q1D, localH, localL, Bt, mask);
TransferKernels::Restriction2D(NE, D1D, Q1D, localH, localL, Bt, mask);
}
else if (dim == 3)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x23:
SmemProlongationTranspose3D<2,3>(NE, localH, localL, Bt, mask); break;
case 0x24:
SmemProlongationTranspose3D<2,4>(NE, localH, localL, Bt, mask); break;
case 0x35:
SmemProlongationTranspose3D<3,5>(NE, localH, localL, Bt, mask); break;
case 0x46:
SmemProlongationTranspose3D<4,6>(NE, localH, localL, Bt, mask); break;
case 0x47:
SmemProlongationTranspose3D<4,7>(NE, localH, localL, Bt, mask); break;
default:
ProlongationTranspose3D(NE, D1D, Q1D, localH, localL, Bt, mask); break;
}
TransferKernels::Restriction3D(NE, D1D, Q1D, localH, localL, Bt, mask);
}
else
{
@@ -2760,20 +2583,20 @@ TrueTransferOperator::TrueTransferOperator(const FiniteElementSpace& lFESpace_,
P = lFESpace.GetProlongationMatrix();
R = hFESpace.IsVariableOrder() ? hFESpace.GetHpRestrictionMatrix() :
hFESpace.GetRestrictionOperator();
hFESpace.GetRestrictionMatrix();
// P and R can be both null
// P can be null and R not null
// If P is not null it is assumed that R is not null as well
if (P) { MFEM_VERIFY(R, "Both P and R have to be not NULL") }
if (!IsIdentityProlongation(P))
if (P)
{
tmpL.SetSize(lFESpace_.GetVSize());
tmpH.SetSize(hFESpace_.GetVSize());
}
// P can be null and R not null
else if (!IsIdentityProlongation(R))
else if (R)
{
tmpH.SetSize(hFESpace_.GetVSize());
}
@@ -2786,14 +2609,13 @@ TrueTransferOperator::~TrueTransferOperator()
void TrueTransferOperator::Mult(const Vector& x, Vector& y) const
{
MFEM_PERF_FUNCTION;
if (!IsIdentityProlongation(P))
if (P)
{
P->Mult(x, tmpL);
localTransferOperator->Mult(tmpL, tmpH);
R->Mult(tmpH, y);
}
else if (!IsIdentityProlongation(R))
else if (R)
{
localTransferOperator->Mult(x, tmpH);
R->Mult(tmpH, y);
@@ -2806,14 +2628,13 @@ void TrueTransferOperator::Mult(const Vector& x, Vector& y) const
void TrueTransferOperator::MultTranspose(const Vector& x, Vector& y) const
{
MFEM_PERF_FUNCTION;
if (!IsIdentityProlongation(P))
if (P)
{
R->MultTranspose(x, tmpH);
localTransferOperator->MultTranspose(tmpH, tmpL);
P->MultTranspose(tmpL, y);
}
else if (!IsIdentityProlongation(R))
else if (R)
{
R->MultTranspose(x, tmpH);
localTransferOperator->MultTranspose(tmpH, y);
+4 -1
View File
@@ -621,6 +621,9 @@ public:
const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_);
/// Destructor
virtual ~TensorProductPRefinementTransferOperator() { }
/// @brief Interpolation or prolongation of a vector \p x corresponding to
/// the coarse space to the vector \p y corresponding to the fine space.
void Mult(const Vector& x, Vector& y) const override;
@@ -639,7 +642,7 @@ private:
const FiniteElementSpace& lFESpace;
const FiniteElementSpace& hFESpace;
const Operator * P = nullptr;
const Operator * R = nullptr;
const SparseMatrix * R = nullptr;
TransferOperator* localTransferOperator;
mutable Vector tmpL;
mutable Vector tmpH;
+1
View File
@@ -39,6 +39,7 @@ list(APPEND HDRS
arrays_by_name.hpp
backends.hpp
binaryio.hpp
complex_type.hpp
cuda.hpp
device.hpp
error.hpp
+9 -83
View File
@@ -14,98 +14,24 @@
#include "../config/config.hpp"
#define MFEM_CONCAT_(X,Y) X##Y
#define MFEM_CONCAT(X,Y) MFEM_CONCAT_(X,Y)
#ifdef MFEM_USE_CALIPER
#include "device.hpp"
#include "backends.hpp"
#ifdef MFEM_USE_MPI
#include "communication.hpp"
#endif
#include <caliper/cali.h>
#include <caliper/cali-manager.h>
#endif
namespace mfem
{
namespace internal
{
extern int annotation_sync_stream; // defined in globals.cpp
extern int annotation_sync_mpi; // defined in globals.cpp
#ifdef MFEM_USE_CALIPER
inline void AnnotationSync()
{
if (annotation_sync_stream && Device::Allows(Backend::DEVICE_MASK))
{
MFEM_STREAM_SYNC;
}
#ifdef MFEM_USE_MPI
if (annotation_sync_mpi && Mpi::IsInitialized() && !Mpi::IsFinalized())
{
MPI_Barrier(GetGlobalMPI_Comm());
}
#endif
}
struct FunctionAnnotation
{
::cali::Function cali_func;
FunctionAnnotation(const char *fname)
: cali_func((AnnotationSync(), fname)) { }
~FunctionAnnotation() { AnnotationSync(); }
};
struct ScopeAnnotation
{
::cali::ScopeAnnotation cali_scope;
ScopeAnnotation(const char *name)
: cali_scope((AnnotationSync(), name)) { }
~ScopeAnnotation() { AnnotationSync(); }
};
#endif // #ifdef MFEM_USE_CALIPER
} // namespace internal
} // namespace mfem
#ifdef MFEM_USE_CALIPER
#define MFEM_PERF_FUNCTION \
mfem::internal::FunctionAnnotation mfem_func_annotation_(_MFEM_FUNC_NAME)
#define MFEM_PERF_BEGIN(s) \
(mfem::internal::AnnotationSync(), CALI_MARK_BEGIN(s))
#define MFEM_PERF_END(s) \
(mfem::internal::AnnotationSync(), CALI_MARK_END(s))
#define MFEM_PERF_FUNCTION CALI_CXX_MARK_FUNCTION
#define MFEM_PERF_BEGIN(s) CALI_MARK_BEGIN(s)
#define MFEM_PERF_END(s) CALI_MARK_END(s)
#define MFEM_PERF_SCOPE(name) \
mfem::internal::ScopeAnnotation \
MFEM_CONCAT(mfem_scope_annotation_,__LINE__)(name)
cali::Annotation::Guard cali_autogenerated_guard_name(cali::Annotation("function").begin(std::string(name).c_str()))
#define MFEM_PERF_SYNC_STREAM(b) (mfem::internal::annotation_sync_stream = (b))
#define MFEM_PERF_SYNC_MPI(b) (mfem::internal::annotation_sync_mpi = (b))
#define MFEM_PERF_SYNC(b) (MFEM_PERF_SYNC_STREAM(b), MFEM_PERF_SYNC_MPI(b))
#else // #ifdef MFEM_USE_CALIPER
#else
#define MFEM_PERF_FUNCTION
#define MFEM_PERF_BEGIN(s) ((void)(0))
#define MFEM_PERF_BEGIN(s)
#define MFEM_PERF_END(s)
#define MFEM_PERF_SCOPE(name)
#define MFEM_PERF_SYNC_STREAM(b)
#define MFEM_PERF_SYNC_MPI(b)
#define MFEM_PERF_SYNC(b)
#endif
#endif // #ifdef MFEM_USE_CALIPER
#endif // MFEM_ANNOTATION_HPP
#endif
+6 -6
View File
@@ -14,7 +14,7 @@
#include "../config/config.hpp"
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#ifdef MFEM_USE_CUDA
#include <cusparse.h>
#include <library_types.h>
#include <cuda_runtime.h>
@@ -22,7 +22,7 @@
#endif
#include "cuda.hpp"
#if defined(MFEM_USE_HIP) && defined(__HIP__)
#ifdef MFEM_USE_HIP
#include <hip/hip_runtime.h>
#endif
#include "hip.hpp"
@@ -43,7 +43,7 @@
#endif
#endif
#if !defined(MFEM_USE_CUDA_OR_HIP)
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
#define MFEM_DEVICE
#define MFEM_HOST
#define MFEM_LAMBDA
@@ -55,7 +55,7 @@
#endif
#if !((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
#define MFEM_SHARED
#define MFEM_SYNC_THREAD
#define MFEM_BLOCK_ID(k) 0
@@ -66,7 +66,7 @@
#endif
// 'double' and 'float' atomicAdd implementation for previous versions of CUDA
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 600)
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 600
MFEM_DEVICE inline mfem::real_t atomicAdd(mfem::real_t *add, mfem::real_t val)
{
unsigned long long int *ptr = (unsigned long long int *) add;
@@ -94,7 +94,7 @@ template <typename T>
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
{
#if ((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
return atomicAdd(&add,val);
#else
T old = add;
+3 -13
View File
@@ -347,23 +347,13 @@ void GroupTopology::Swap(GroupTopology &other)
mfem::Swap(group_mgroup, other.group_mgroup);
}
/// \cond DO_NOT_DOCUMENT
// Initialize the static mpi_type for the specializations of MPITypeMap:
const MPI_Datatype MPITypeMap<bool>::mpi_type = MFEM_MPI_CXX_BOOL;
const MPI_Datatype MPITypeMap<char>::mpi_type = MPI_CHAR;
const MPI_Datatype MPITypeMap<unsigned char>::mpi_type = MPI_UNSIGNED_CHAR;
const MPI_Datatype MPITypeMap<short>::mpi_type = MPI_SHORT;
const MPI_Datatype MPITypeMap<unsigned short>::mpi_type = MPI_UNSIGNED_SHORT;
const MPI_Datatype MPITypeMap<int>::mpi_type = MPI_INT;
const MPI_Datatype MPITypeMap<unsigned int>::mpi_type = MPI_UNSIGNED;
const MPI_Datatype MPITypeMap<long>::mpi_type = MPI_LONG;
const MPI_Datatype MPITypeMap<unsigned long>::mpi_type = MPI_UNSIGNED_LONG;
const MPI_Datatype MPITypeMap<long long>::mpi_type = MPI_LONG_LONG;
const MPI_Datatype MPITypeMap<unsigned long long>::mpi_type =
MPI_UNSIGNED_LONG_LONG;
const MPI_Datatype MPITypeMap<float>::mpi_type = MPI_FLOAT;
const MPI_Datatype MPITypeMap<double>::mpi_type = MPI_DOUBLE;
/// \endcond DO_NOT_DOCUMENT
const MPI_Datatype MPITypeMap<int64_t>::mpi_type = MPI_INT64_T;
const MPI_Datatype MPITypeMap<uint64_t>::mpi_type = MPI_UINT64_T;
GroupCommunicator::GroupCommunicator(const GroupTopology &gt, Mode m)
: gtopo(gt), mode(m)
+24 -59
View File
@@ -23,9 +23,13 @@
#include <mpi.h>
#include <cstdint>
// Some MPI implementations do not have MPI_CXX_BOOL or do not handle it
// correctly, so we use MPI_UNSIGNED_CHAR as the MPI type for 'bool':
#define MFEM_MPI_CXX_BOOL MPI_UNSIGNED_CHAR
// can't directly use MPI_CXX_BOOL because Microsoft's MPI implementation
// doesn't include MPI_CXX_BOOL. Fallback to MPI_C_BOOL if unavailable.
#ifdef MPI_CXX_BOOL
#define MFEM_MPI_CXX_BOOL MPI_CXX_BOOL
#else
#define MFEM_MPI_CXX_BOOL MPI_C_BOOL
#endif
namespace mfem
{
@@ -421,24 +425,16 @@ public:
~GroupCommunicator();
};
/// General MPI message tags used by MFEM
enum MessageTag
{
DEREFINEMENT_MATRIX_CONSTRUCTION_DATA =
291, /// ParFiniteElementSpace ParallelDerefinementMatrix and
/// ParDerefineMatrixOp
};
enum VarMessageTag
{
NEIGHBOR_ELEMENT_RANK_VM, ///< NeighborElementRankMessage
NEIGHBOR_ORDER_VM, ///< NeighborOrderMessage
NEIGHBOR_DEREFINEMENT_VM, ///< NeighborDerefinementMessage
NEIGHBOR_REFINEMENT_VM, ///< NeighborRefinementMessage
NEIGHBOR_PREFINEMENT_VM, ///< NeighborPRefinementMessage
NEIGHBOR_ROW_VM, ///< NeighborRowMessage
REBALANCE_VM, ///< RebalanceMessage
REBALANCE_DOF_VM, ///< RebalanceDofMessage
NEIGHBOR_ELEMENT_RANK_VM, ///< NeighborElementRankMessage
NEIGHBOR_ORDER_VM, ///< NeighborOrderMessage
NEIGHBOR_DEREFINEMENT_VM, ///< NeighborDerefinementMessage
NEIGHBOR_REFINEMENT_VM, ///< NeighborRefinementMessage
NEIGHBOR_PREFINEMENT_VM, ///< NeighborPRefinementMessage
NEIGHBOR_ROW_VM, ///< NeighborRowMessage
REBALANCE_VM, ///< RebalanceMessage
REBALANCE_DOF_VM ///< RebalanceDofMessage
};
/// \brief Variable-length MPI message containing unspecific binary data.
@@ -603,50 +599,10 @@ protected:
template <typename Type> struct MPITypeMap;
// Specializations of MPITypeMap; mpi_type initialized in communication.cpp:
template<> struct MPITypeMap<bool>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<char>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<unsigned char>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<short>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<unsigned short>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<int>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<unsigned int>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<long>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<unsigned long>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<long long>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<unsigned long long>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<double>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
@@ -655,6 +611,15 @@ template<> struct MPITypeMap<float>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<int64_t>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
template<> struct MPITypeMap<uint64_t>
{
static MFEM_EXPORT const MPI_Datatype mpi_type;
};
/** Reorder MPI ranks to follow the Z-curve within the physical machine topology
(provided that functions to query physical node coordinates are available).
+125
View File
@@ -0,0 +1,125 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_COMPLEX_TYPE
#define MFEM_COMPLEX_TYPE
#include "../config/config.hpp"
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
#include <complex>
#include <utility>
#endif
#if defined(MFEM_USE_CUDA)
#include <cuComplex.h>
#endif
#if defined(MFEM_USE_HIP)
#include <hip/hip_complex.h>
#endif
namespace mfem
{
/// @brief Complex number type for device.
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
#define zAbs std::abs
#define zExp std::exp
#define zNorm std::norm
using complex_t = std::complex<real_t>;
#else // CUDA or HIP
#if defined(MFEM_USE_CUDA)
using DoubleComplex_t = cuDoubleComplex;
#endif
#if defined(MFEM_USE_HIP)
using DoubleComplex_t = hipDoubleComplex;
#endif
struct Complex : public DoubleComplex_t
{
MFEM_HOST_DEVICE Complex() = default;
MFEM_HOST_DEVICE Complex(real_t r) { x = r, y = 0.0; }
MFEM_HOST_DEVICE Complex(real_t r, real_t i) { x = r, y = i; }
MFEM_HOST_DEVICE real_t real() const { return x; }
MFEM_HOST_DEVICE void real(real_t r) { x = r; }
MFEM_HOST_DEVICE real_t imag() const { return y; }
MFEM_HOST_DEVICE void imag(real_t i) { y = i; }
template <typename U>
MFEM_HOST_DEVICE inline Complex &operator*=(const U &z)
{
return *this = *this * z, *this;
}
template <typename U>
MFEM_HOST_DEVICE inline Complex &operator/=(const U &z)
{
return *this = *this / z, *this;
}
};
MFEM_HOST_DEVICE inline Complex operator*(const Complex &x, const real_t &y)
{
return Complex(x.real() * y, x.imag() * y);
}
MFEM_HOST_DEVICE inline Complex operator+(const Complex &a, const Complex &b)
{
return Complex(a.real() + b.real(), a.imag() + b.imag());
}
MFEM_HOST_DEVICE inline Complex operator*(const real_t d, const Complex &z)
{
return Complex(z.real() * d, z.imag() * d);
}
MFEM_HOST_DEVICE inline Complex operator*(const Complex &a, const Complex &b)
{
return Complex(a.real() * b.real() - a.imag() * b.imag(),
a.real() * b.imag() + a.imag() * b.real());
}
MFEM_HOST_DEVICE inline Complex operator/(const Complex &z, const real_t &d)
{
return Complex(z.real() / d, z.imag() / d);
}
MFEM_HOST_DEVICE inline real_t zAbs(const Complex &z)
{
return std::hypot(z.real(), z.imag());
}
MFEM_HOST_DEVICE inline Complex zExp(const Complex &q)
{
Complex z;
real_t s, c, e = std::exp(q.real());
sincos(q.imag(), &s, &c);
z.real(c * e), z.imag(s * e);
return z;
}
MFEM_HOST_DEVICE inline real_t zNorm(const Complex &z)
{
return z.real() * z.real() + z.imag() * z.imag();
}
using complex_t = Complex;
#endif // MFEM_USE_CUDA || MFEM_USE_HIP
} // namespace mfem
#endif // MFEM_COMPLEX_TYPE
+1 -1
View File
@@ -24,7 +24,7 @@ void mfem_cuda_error(cudaError_t err, const char *expr, const char *func,
const char *file, int line)
{
mfem::err << "\n\nCUDA error: (" << expr << ") failed with error:\n --> "
<< cudaGetErrorString(err) << " [code: " << (int)err << ']'
<< cudaGetErrorString(err)
<< "\n ... in function: " << func
<< "\n ... in file: " << file << ':' << line << '\n';
mfem_error();
+5 -5
View File
@@ -18,7 +18,7 @@
// CUDA block size used by MFEM.
#define MFEM_CUDA_BLOCKS 256
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#ifdef MFEM_USE_CUDA
#define MFEM_USE_CUDA_OR_HIP
#define MFEM_DEVICE __device__
#define MFEM_HOST __host__
@@ -37,9 +37,10 @@
__FILE__, __LINE__); \
} \
} while (0)
#endif // MFEM_USE_CUDA
// Define the MFEM inner threading macros
#if defined(__CUDA_ARCH__)
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)
#define MFEM_SHARED __shared__
#define MFEM_SYNC_THREAD __syncthreads()
#define MFEM_BLOCK_ID(k) blockIdx.k
@@ -47,13 +48,12 @@
#define MFEM_THREAD_SIZE(k) blockDim.k
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=threadIdx.k; i<N; i+=blockDim.k)
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) if(const int i=threadIdx.k; i<N)
#endif // defined(__CUDA_ARCH__)
#endif // defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#endif
namespace mfem
{
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#ifdef MFEM_USE_CUDA
// Function used by the macro MFEM_GPU_CHECK.
void mfem_cuda_error(cudaError_t err, const char *expr, const char *func,
const char *file, int line);
+1 -17
View File
@@ -151,22 +151,6 @@ Device::Device()
{
SetGPUAwareMPI(true);
}
if (const char *mfem_perf_sync = GetEnv("MFEM_PERF_SYNC"))
{
MFEM_PERF_SYNC(std::atoi(mfem_perf_sync));
MFEM_CONTRACT_VAR(mfem_perf_sync);
}
if (const char *mfem_perf_sync_stream = GetEnv("MFEM_PERF_SYNC_STREAM"))
{
MFEM_PERF_SYNC_STREAM(std::atoi(mfem_perf_sync_stream));
MFEM_CONTRACT_VAR(mfem_perf_sync_stream);
}
if (const char *mfem_perf_sync_mpi = GetEnv("MFEM_PERF_SYNC_MPI"))
{
MFEM_PERF_SYNC_MPI(std::atoi(mfem_perf_sync_mpi));
MFEM_CONTRACT_VAR(mfem_perf_sync_mpi);
}
}
Device::~Device()
@@ -631,7 +615,7 @@ void Device::Setup(const std::string &device_option, const int device_id)
if (Allows(Backend::DEBUG_DEVICE)) { ngpu = 1; }
}
MemoryType Device::QueryMemoryType(const void* ptr)
MemoryType Device::QueryMemoryType(void *ptr)
{
// from HYPRE's hypre_GetPointerLocation
MemoryType res = MemoryType::HOST;
+3 -3
View File
@@ -297,9 +297,9 @@ public:
/// Get the status of GPU-aware MPI flag.
static bool GetGPUAwareMPI() { return Get().mpi_gpu_aware; }
/** Query the device driver for what memory type a given @a ptr is allocated
* with. */
static MemoryType QueryMemoryType(const void* ptr);
/** @brief Query the device driver for what memory type a given @a ptr is
allocated with. */
static MemoryType QueryMemoryType(void *ptr);
/** @brief The number of hardware compute units/streaming multiprocessors
available on a given compute device @a device_id. */
+1 -1
View File
@@ -193,4 +193,4 @@ void mfem_warning(const char *msg)
}
}
} // namespace mfem
}
+2 -2
View File
@@ -176,7 +176,7 @@ __device__ void abort_msg(T & msg)
printf(__VA_ARGS__); \
asm("trap;"); \
}
#elif defined(__HIP_DEVICE_COMPILE__)
#elif defined(MFEM_USE_HIP)
#define MFEM_ABORT_KERNEL(...) \
{ \
printf(__VA_ARGS__); \
@@ -208,4 +208,4 @@ __device__ void abort_msg(T & msg)
#define MFEM_ASSERT_KERNEL(x,...)
#endif
#endif // MFEM_ERROR_HPP
#endif
+12 -171
View File
@@ -158,8 +158,8 @@ private:
#define MFEM_PRAGMA(X) _Pragma(#X)
// MFEM_UNROLL pragma macro that can be used inside MFEM_FORALL macros.
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__) // Clang cuda or nvcc
#ifdef __NVCC__ // nvcc specifically
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)
#ifdef __NVCC__
#define MFEM_UNROLL(N) MFEM_PRAGMA(unroll(N))
#else // Assuming Clang CUDA
#define MFEM_UNROLL(N) MFEM_PRAGMA(unroll N)
@@ -169,12 +169,12 @@ private:
#endif
// MFEM_GPU_FORALL: "parallel for" executed with CUDA or HIP based on the MFEM
// build-time configuration (MFEM_USE_CUDA or MFEM_USE_HIP), and if compiling
// with CUDA/HIP language. Otherwise, this macro is a no-op.
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
// build-time configuration (MFEM_USE_CUDA or MFEM_USE_HIP). If neither CUDA nor
// HIP is enabled, this macro is a no-op.
#if defined(MFEM_USE_CUDA)
#define MFEM_GPU_FORALL(i, N,...) CuWrap1D(N, [=] MFEM_DEVICE \
(int i) {__VA_ARGS__})
#elif defined(MFEM_USE_HIP) && defined(__HIP__)
#elif defined(MFEM_USE_HIP)
#define MFEM_GPU_FORALL(i, N,...) HipWrap1D(N, [=] MFEM_DEVICE \
(int i) {__VA_ARGS__})
#else
@@ -225,52 +225,6 @@ void OmpWrap(const int N, HBODY &&h_body)
#endif
}
template <typename HBODY>
void OmpWrap2D(const int Nx, const int Ny, HBODY &&h_body)
{
#ifdef MFEM_USE_OPENMP
// requires OpenMP 3.1
#pragma omp parallel for collapse(2)
for (int j = 0; j < Ny; j++)
{
for (int i = 0; i < Nx; i++)
{
h_body(i, j);
}
}
#else
MFEM_CONTRACT_VAR(Nx);
MFEM_CONTRACT_VAR(Ny);
MFEM_CONTRACT_VAR(h_body);
MFEM_ABORT("OpenMP requested for MFEM but OpenMP is not enabled!");
#endif
}
template <typename HBODY>
void OmpWrap3D(const int Nx, const int Ny, const int Nz, HBODY &&h_body)
{
#ifdef MFEM_USE_OPENMP
// requires OpenMP 3.1
#pragma omp parallel for collapse(3)
for (int k = 0; k < Nz; k++)
{
for (int j = 0; j < Ny; j++)
{
for (int i = 0; i < Nx; i++)
{
h_body(i, j, k);
}
}
}
#else
MFEM_CONTRACT_VAR(Nx);
MFEM_CONTRACT_VAR(Ny);
MFEM_CONTRACT_VAR(Nz);
MFEM_CONTRACT_VAR(h_body);
MFEM_ABORT("OpenMP requested for MFEM but OpenMP is not enabled!");
#endif
}
/// RAJA Cuda and Hip backends
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
@@ -500,42 +454,6 @@ void RajaOmpWrap(const int N, HBODY &&h_body)
RAJA::forall<RAJA::omp_parallel_for_exec>(RAJA::RangeSegment(0,N), h_body);
}
template <typename HBODY>
void RajaOmpWrap2D(const int Nx, const int Ny, HBODY &&h_body)
{
using omp_launch_policy = RAJA::LaunchPolicy<RAJA::omp_launch_t>;
using global_thread_xy = RAJA::LoopPolicy<RAJA::omp_for_exec>;
RAJA::RangeSegment xrange(0, Nx);
RAJA::RangeSegment yrange(0, Ny);
RAJA::launch<omp_launch_policy>(RAJA::ExecPlace::HOST, RAJA::LaunchParams(),
[=](RAJA::LaunchContext ctx)
{
// contiguous in x
RAJA::expt::loop<global_thread_xy>(ctx, xrange, yrange, [&](int i, int j)
{
h_body(i, j);
});
});
}
template <typename HBODY>
void RajaOmpWrap3D(const int Nx, const int Ny, const int Nz, HBODY &&h_body)
{
using omp_launch_policy = RAJA::LaunchPolicy<RAJA::omp_launch_t>;
using global_thread_xyz = RAJA::LoopPolicy<RAJA::omp_for_exec>;
RAJA::RangeSegment xrange(0, Nx);
RAJA::RangeSegment yrange(0, Ny);
RAJA::RangeSegment zrange(0, Nz);
RAJA::launch<omp_launch_policy>(RAJA::ExecPlace::HOST, RAJA::LaunchParams(),
[=](RAJA::LaunchContext ctx)
{
// contiguous in x
RAJA::expt::loop<global_thread_xyz>(ctx, xrange, yrange, zrange,
[&](int i, int j, int k)
{ h_body(i, j, k); });
});
}
#endif
@@ -563,7 +481,7 @@ void RajaSeqWrap(const int N, HBODY &&h_body)
/// CUDA backend
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#ifdef MFEM_USE_CUDA
template <typename BODY> __global__ static
void CuKernel1D(const int N, BODY body)
@@ -655,11 +573,11 @@ struct CuWrap<3>
}
};
#endif // defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#endif // MFEM_USE_CUDA
/// HIP backend
#if defined(MFEM_USE_HIP) && defined(__HIP__)
#ifdef MFEM_USE_HIP
template <typename BODY> __global__ static
void HipKernel1D(const int N, BODY body)
@@ -750,7 +668,7 @@ struct HipWrap<3>
}
};
#endif // defined(MFEM_USE_HIP) && defined(__HIP__)
#endif // MFEM_USE_HIP
/// The forall kernel body wrapper
@@ -783,7 +701,7 @@ inline void ForallWrap(const bool use_dev, const int N,
}
#endif
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#ifdef MFEM_USE_CUDA
// If Backend::CUDA is allowed, use it
if (Device::Allows(Backend::CUDA))
{
@@ -791,7 +709,7 @@ inline void ForallWrap(const bool use_dev, const int N,
}
#endif
#if defined(MFEM_USE_HIP) && defined(__HIP__)
#ifdef MFEM_USE_HIP
// If Backend::HIP is allowed, use it
if (Device::Allows(Backend::HIP))
{
@@ -835,83 +753,6 @@ inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
template<typename lambda>
inline void forall(int N, lambda &&body) { ForallWrap<1>(true, N, body); }
template<typename lambda>
inline void forall(int Nx, int Ny, lambda &&body)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
{
int j = idx / Nx;
int i = idx % Nx;
body(i, j);
});
}
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
else if (Device::Allows(Backend::RAJA_OMP))
{
return RajaOmpWrap2D(Nx, Ny, body);
}
#endif
#ifdef MFEM_USE_OPENMP
else if (Device::Allows(Backend::OMP))
{
return OmpWrap2D(Nx, Ny, body);
}
#endif
else
{
for (int j = 0; j < Ny; ++j)
{
for (int i = 0; i < Nx; ++i)
{
body(i, j);
}
}
}
}
template<typename lambda>
inline void forall(int Nx, int Ny, int Nz, lambda &&body)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
{
int i = idx % Nx;
int j = idx / Nx;
int k = j / Ny;
j = j % Ny;
body(i, j, k);
});
}
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
else if (Device::Allows(Backend::RAJA_OMP))
{
return RajaOmpWrap3D(Nx, Ny, Nz, body);
}
#endif
#ifdef MFEM_USE_OPENMP
else if (Device::Allows(Backend::OMP))
{
return OmpWrap3D(Nx, Ny, Nz, body);
}
#endif
else
{
for (int k = 0; k < Nz; ++k)
{
for (int j = 0; j < Ny; ++j)
{
for (int i = 0; i < Nx; ++i)
{
body(i, j, k);
}
}
}
}
}
template<typename lambda>
inline void forall_switch(bool use_dev, int N, lambda &&body)
{
-3
View File
@@ -31,9 +31,6 @@ namespace internal
{
bool mfem_out_initialized = false;
bool mfem_err_initialized = false;
int annotation_sync_stream = 0; // declared in annotation.hpp
int annotation_sync_mpi = 0; // declared in annotation.hpp
}
void OutStream::Init()
+1 -1
View File
@@ -24,7 +24,7 @@ void mfem_hip_error(hipError_t err, const char *expr, const char *func,
const char *file, int line)
{
mfem::err << "\n\nHIP error: (" << expr << ") failed with error:\n --> "
<< hipGetErrorString(err) << " [code: " << (int)err << ']'
<< hipGetErrorString(err)
<< "\n ... in function: " << func
<< "\n ... in file: " << file << ':' << line << '\n';
mfem_error();
+4 -4
View File
@@ -18,7 +18,7 @@
// HIP block size used by MFEM.
#define MFEM_HIP_BLOCKS 256
#if defined(MFEM_USE_HIP) && defined(__HIP__)
#ifdef MFEM_USE_HIP
#define MFEM_USE_CUDA_OR_HIP
#define MFEM_DEVICE __device__
#define MFEM_HOST __host__
@@ -37,9 +37,10 @@
__FILE__, __LINE__); \
} \
} while (0)
#endif // MFEM_USE_HIP
// Define the MFEM inner threading macros
#if defined(__HIP_DEVICE_COMPILE__)
#if defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)
#define MFEM_SHARED __shared__
#define MFEM_SYNC_THREAD __syncthreads()
#define MFEM_BLOCK_ID(k) hipBlockIdx_ ##k
@@ -49,8 +50,7 @@
for(int i=hipThreadIdx_ ##k; i<N; i+=hipBlockDim_ ##k)
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) \
if(const int i=hipThreadIdx_ ##k; i<N)
#endif // defined(__HIP_DEVICE_COMPILE__)
#endif // defined(MFEM_USE_HIP) && defined(__HIP__)
#endif
namespace mfem
{
+8 -2
View File
@@ -513,7 +513,10 @@ public:
void *HtoD(void *dst, const void *src, size_t bytes) override
{ return HipMemcpyHtoD(dst, src, bytes); }
void *DtoD(void* dst, const void* src, size_t bytes) override
{ return HipMemcpyDtoD(dst, src, bytes); }
// Unlike cudaMemcpy(DtoD), hipMemcpy(DtoD) causes a host-side synchronization so
// instead we use hipMemcpyAsync to get similar behavior.
// for more info see: https://github.com/mfem/mfem/pull/2780
{ return HipMemcpyDtoDAsync(dst, src, bytes); }
void *DtoH(void *dst, const void *src, size_t bytes) override
{ return HipMemcpyDtoH(dst, src, bytes); }
};
@@ -655,7 +658,10 @@ public:
return CuMemcpyDtoD(dst, src, bytes);
#endif
#ifdef MFEM_USE_HIP
return HipMemcpyDtoD(dst, src, bytes);
// Unlike cudaMemcpy(DtoD), hipMemcpy(DtoD) causes a host-side synchronization so
// instead we use hipMemcpyAsync to get similar behavior.
// for more info see: https://github.com/mfem/mfem/pull/2780
return HipMemcpyDtoDAsync(dst, src, bytes);
#endif
// rm.copy(dst, const_cast<void*>(src), bytes); return dst;
}
+5 -9
View File
@@ -657,8 +657,7 @@ private: // Static methods used by the Memory<T> class
/// Return the host pointer.
MFEM_ENZYME_INACTIVE static void *Register_(void *ptr, void *h_ptr,
size_t bytes, MemoryType mt,
bool own, bool alias,
unsigned &flags);
bool own, bool alias, unsigned &flags);
/// Register a pair of external host and device pointers
static void Register2_(void *h_ptr, void *d_ptr, size_t bytes,
@@ -742,7 +741,7 @@ private:
/// Insert a host address @a h_ptr and size *a bytes in the memory map to be
/// managed.
void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
void Insert(void *h_ptr, size_t bytes, MemoryType h_mt, MemoryType d_mt);
/// Insert a device and the host addresses in the memory map
void InsertDevice(void *d_ptr, void *h_ptr, size_t bytes,
@@ -897,7 +896,6 @@ inline HYPRE_MemoryLocation GetHypreMemoryLocation()
#elif MFEM_HYPRE_VERSION < 23100
return HYPRE_MEMORY_DEVICE;
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
if (!HYPRE_Initialized()) { return HYPRE_MEMORY_HOST; }
HYPRE_MemoryLocation loc;
HYPRE_GetMemoryLocation(&loc);
return loc;
@@ -982,7 +980,7 @@ inline void Memory<T>::Wrap(T *ptr, int size, bool own)
#ifdef MFEM_DEBUG
if (own && MemoryManager::Exists())
{
MemoryType h_ptr_mt = MemoryManager::GetHostMemoryType_((void*)h_ptr);
MemoryType h_ptr_mt = MemoryManager::GetHostMemoryType_(h_ptr);
MFEM_VERIFY(h_mt == h_ptr_mt,
"h_mt = " << (int)h_mt << ", h_ptr_mt = " << (int)h_ptr_mt);
}
@@ -990,8 +988,7 @@ inline void Memory<T>::Wrap(T *ptr, int size, bool own)
if (own && h_mt != MemoryType::HOST)
{
const size_t bytes = size*sizeof(T);
MemoryManager::Register_((void*)ptr, (void*)ptr, bytes, h_mt, own, false,
flags);
MemoryManager::Register_(ptr, ptr, bytes, h_mt, own, false, flags);
}
}
@@ -1060,8 +1057,7 @@ inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
// register the 'base' if the MemoryManager::Exists():
MemoryManager::Exists()
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
IsDeviceMemory(MemoryManager::GetDeviceMemoryType()) ||
(MemoryManager::Exists() && HypreUsingGPU())
MemoryManager::Exists() && HypreUsingGPU()
#endif
)
{
+1 -1
View File
@@ -537,7 +537,7 @@ void reduce(int N, T &res, B &&body, const R &reducer, bool use_dev,
return;
}
#if defined(MFEM_USE_CUDA_OR_HIP)
#if defined(MFEM_USE_HIP) || defined(MFEM_USE_CUDA)
if (use_dev &&
mfem::Device::Allows(Backend::CUDA | Backend::HIP | Backend::RAJA_CUDA |
Backend::RAJA_HIP))
+2
View File
@@ -21,6 +21,7 @@ list(APPEND SRCS
blockvector.cpp
complex_densemat.cpp
complex_operator.cpp
complex_vector.cpp
constraints.cpp
densemat.cpp
symmat.cpp
@@ -47,6 +48,7 @@ list(APPEND HDRS
blockvector.hpp
complex_densemat.hpp
complex_operator.hpp
complex_vector.hpp
constraints.hpp
densemat.hpp
dinvariants.hpp
+4 -4
View File
@@ -20,13 +20,13 @@
#define MFEM_CU_or_HIP(stub) HIP##stub
#endif
#define MFEM_CONCAT3(x, y, z) MFEM_CONCAT3_(x, y, z)
#define MFEM_CONCAT3_(x, y, z) x ## y ## z
#define MFEM_CONCAT(x, y, z) MFEM_CONCAT_(x, y, z)
#define MFEM_CONCAT_(x, y, z) x ## y ## z
#ifdef MFEM_USE_SINGLE
#define MFEM_GPUBLAS_PREFIX(stub) MFEM_CONCAT3(MFEM_cu_or_hip(blas), S, stub)
#define MFEM_GPUBLAS_PREFIX(stub) MFEM_CONCAT(MFEM_cu_or_hip(blas), S, stub)
#elif defined(MFEM_USE_DOUBLE)
#define MFEM_GPUBLAS_PREFIX(stub) MFEM_CONCAT3(MFEM_cu_or_hip(blas), D, stub)
#define MFEM_GPUBLAS_PREFIX(stub) MFEM_CONCAT(MFEM_cu_or_hip(blas), D, stub)
#endif
#define MFEM_BLAS_SUCCESS MFEM_CU_or_HIP(BLAS_STATUS_SUCCESS)
+302
View File
@@ -9,6 +9,7 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "complex_densemat.hpp"
#include "lapack.hpp"
#include <complex>
@@ -16,6 +17,8 @@
namespace mfem
{
using namespace std;
DenseMatrix & ComplexDenseMatrix::real()
{
MFEM_ASSERT(Op_Real_, "ComplexDenseMatrix has no real part!");
@@ -1017,4 +1020,303 @@ void ComplexCholeskyFactors::GetInverseMatrix(int m, real_t * X_r,
delete [] X;
}
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix()
: height(0), width(0)
{}
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(const ComplexTypeDenseMatrix &m)
: height(m.Height()), width(m.Width())
{
const int hw = height * width;
if (hw > 0)
{
MFEM_ASSERT(m.data, "invalid source matrix");
data.New(hw);
std::memcpy(data, m.data, sizeof(complex_t)*hw);
}
}
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(const DenseMatrix &m)
: height(m.Height()), width(m.Width())
{
const int hw = height * width;
if (hw > 0)
{
MFEM_ASSERT(m.data, "invalid source matrix");
data.New(hw);
for (int i = 0; i < hw; i++)
{
data[i] = m.data[i];
}
}
}
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(int s)
: height(s), width(s)
{
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
if (s > 0)
{
data.New(s*s);
*this = 0.0; // init with zeroes
}
}
ComplexTypeDenseMatrix::ComplexTypeDenseMatrix(int m, int n)
: height(m), width(n)
{
MFEM_ASSERT(m >= 0 && n >= 0,
"invalid DenseMatrix size: " << m << " x " << n);
const int capacity = m*n;
if (capacity > 0)
{
data.New(capacity);
*this = 0.0; // init with zeroes
}
}
void ComplexTypeDenseMatrix::SetSize(int h, int w)
{
MFEM_ASSERT(h >= 0 && w >= 0,
"invalid ComplexTypeDenseMatrix size: " << h << " x " << w);
if (Height() == h && Width() == w)
{
return;
}
height = h;
width = w;
const int hw = h*w;
if (hw > data.Capacity())
{
data.Delete();
data.New(hw);
*this = 0.0; // init with zeroes
}
}
/// Returns reference to a_{ij}.
complex_t &ComplexTypeDenseMatrix::Elem(int i, int j)
{
return (*this)(i,j);
}
/// Returns constant reference to a_{ij}.
const complex_t &ComplexTypeDenseMatrix::Elem(int i, int j) const
{
return (*this)(i,j);
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(real_t c)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(complex_t c)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
/// Copy the matrix entries from the given array
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(const real_t *d)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = d[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=
(const complex_t *d)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = d[i];
}
return *this;
}
/// Sets the matrix size and elements equal to those of m
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=(const DenseMatrix &m)
{
SetSize(m.height, m.width);
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] = m.data[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator=
(const ComplexTypeDenseMatrix &m)
{
SetSize(m.height, m.width);
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] = m.data[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=(const real_t *m)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] += m[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=
(const complex_t *m)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] += m[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=(const DenseMatrix &m)
{
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] += m.data[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator+=
(const ComplexTypeDenseMatrix &m)
{
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] += m.data[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator-=(const DenseMatrix &m)
{
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] -= m.data[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator-=
(const ComplexTypeDenseMatrix &m)
{
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] -= m.data[i];
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator*=(real_t c)
{
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] *= c;
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::operator*=(complex_t c)
{
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] *= c;
}
return *this;
}
ComplexTypeDenseMatrix &ComplexTypeDenseMatrix::Set(const DenseMatrix &Mr,
const DenseMatrix &Mi)
{
MFEM_ASSERT(height == Mr.Height() && height == Mi.Height() &&
width == Mr.Width() && width == Mi.Width(),
"incompatible Matrices!");
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] = complex_t(Mr.data[i], Mi.data[i]);
}
return *this;
}
void ComplexTypeDenseMatrix::Swap(ComplexTypeDenseMatrix &other)
{
mfem::Swap(width, other.width);
mfem::Swap(height, other.height);
mfem::Swap(data, other.data);
}
ComplexTypeDenseMatrix::~ComplexTypeDenseMatrix()
{
data.Delete();
}
const DenseMatrix &ComplexTypeDenseMatrix::real() const
{
re_part.SetSize(height, width);
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
re_part.data[i] = data[i].real();
}
return re_part;
}
const DenseMatrix &ComplexTypeDenseMatrix::imag() const
{
im_part.SetSize(height, width);
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
im_part.data[i] = data[i].imag();
}
return im_part;
}
} // mfem namespace
+215
View File
@@ -13,6 +13,7 @@
#define MFEM_COMPLEX_DENSEMAT
#include "complex_operator.hpp"
#include "../general/complex_type.hpp"
#include <complex>
namespace mfem
@@ -241,6 +242,220 @@ public:
};
class ComplexTypeDenseMatrix
{
protected:
int height; ///< Dimension of the output / number of rows in the matrix.
int width; ///< Dimension of the input / number of columns in the matrix.
private:
Memory<complex_t > data;
mutable DenseMatrix re_part;
mutable DenseMatrix im_part;
public:
/** Default constructor for DenseMatrix.
Sets data = NULL and height = width = 0. */
ComplexTypeDenseMatrix();
/// Copy constructor
ComplexTypeDenseMatrix(const ComplexTypeDenseMatrix &);
ComplexTypeDenseMatrix(const DenseMatrix &);
/// Creates square matrix of size s.
explicit ComplexTypeDenseMatrix(int s);
/// Creates rectangular matrix of size m x n.
ComplexTypeDenseMatrix(int m, int n);
/// Construct a ComplexTypeDenseMatrix using an existing data array.
/** The ComplexTypeDenseMatrix does not assume ownership of the data array,
i.e. it will not delete the array. */
ComplexTypeDenseMatrix(complex_t *d, int h, int w)
: height(h), width(w) { UseExternalData(d, h, w); }
/// Create a dense matrix using a braced initializer list
/// The inner lists correspond to rows of the matrix
template <int M, int N, typename T = real_t>
explicit ComplexTypeDenseMatrix(const T (&values)[M][N]) :
ComplexTypeDenseMatrix(
M, N)
{
// DenseMatrix is column-major so copies have to be element-wise
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
{
(*this)(i,j) = values[i][j];
}
}
}
/// Change the data array and the size of the DenseMatrix.
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
not delete the data array @a d. This method should not be used with
DenseMatrix that owns its current data array. */
void UseExternalData(complex_t *d, int h, int w)
{
data.Wrap(d, h*w, false);
height = h; width = w;
}
/// Change the data array and the size of the DenseMatrix.
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
not delete the new array @a d. This method will delete the current data
array, if owned. */
void Reset(complex_t *d, int h, int w)
{ if (OwnsData()) { data.Delete(); } UseExternalData(d, h, w); }
/** Clear the data array and the dimensions of the DenseMatrix. This method
should not be used with DenseMatrix that owns its current data array. */
void ClearExternalData() { data.Reset(); height = width = 0; }
/// Delete the matrix data array (if owned) and reset the matrix state.
void Clear()
{ if (OwnsData()) { data.Delete(); } ClearExternalData(); }
/// Get the height (size of output) of the Operator. Synonym with NumRows().
inline int Height() const { return height; }
/** @brief Get the number of rows (size of output) of the Operator. Synonym
with Height(). */
inline int NumRows() const { return height; }
/// Get the width (size of input) of the Operator. Synonym with NumCols().
inline int Width() const { return width; }
/** @brief Get the number of columns (size of input) of the Operator. Synonym
with Width(). */
inline int NumCols() const { return width; }
/// For backward compatibility define Size to be synonym of Width()
int Size() const { return Width(); }
// Total size = width*height
int TotalSize() const { return width*height; }
/// Change the size of the DenseMatrix to s x s.
void SetSize(int s) { SetSize(s, s); }
/// Change the size of the DenseMatrix to h x w.
void SetSize(int h, int w);
/// Returns the matrix data array.
inline complex_t *Data() const
{
return const_cast<complex_t*>
((const complex_t*)data);
}
/// Returns the matrix data array.
inline complex_t *GetData() const { return Data(); }
Memory<complex_t > &GetMemory() { return data; }
const Memory<complex_t > &GetMemory() const { return data; }
/// Return the DenseMatrix data (host pointer) ownership flag.
inline bool OwnsData() const { return data.OwnsHostPtr(); }
/// Returns reference to a_{ij}.
inline complex_t &operator()(int i, int j);
/// Returns constant reference to a_{ij}.
inline const complex_t &operator()(int i, int j) const;
/// Returns reference to a_{ij}.
complex_t &Elem(int i, int j);
/// Returns constant reference to a_{ij}.
const complex_t &Elem(int i, int j) const;
/// Sets the matrix elements equal to constant c
ComplexTypeDenseMatrix &operator=(real_t c);
ComplexTypeDenseMatrix &operator=(complex_t c);
/// Copy the matrix entries from the given array
ComplexTypeDenseMatrix &operator=(const real_t *d);
ComplexTypeDenseMatrix &operator=(const complex_t *d);
/// Sets the matrix size and elements equal to those of m
ComplexTypeDenseMatrix &operator=(const DenseMatrix &m);
ComplexTypeDenseMatrix &operator=(const ComplexTypeDenseMatrix &m);
ComplexTypeDenseMatrix &operator+=(const real_t *m);
ComplexTypeDenseMatrix &operator+=(const complex_t *m);
ComplexTypeDenseMatrix &operator+=(const DenseMatrix &m);
ComplexTypeDenseMatrix &operator+=(const ComplexTypeDenseMatrix &m);
ComplexTypeDenseMatrix &operator-=(const DenseMatrix &m);
ComplexTypeDenseMatrix &operator-=(const ComplexTypeDenseMatrix &m);
ComplexTypeDenseMatrix &operator*=(real_t c);
ComplexTypeDenseMatrix &operator*=(complex_t c);
/// (*this) = x + i * y
ComplexTypeDenseMatrix &Set(const DenseMatrix &x, const DenseMatrix &y);
std::size_t MemoryUsage() const
{ return data.Capacity() * sizeof(complex_t); }
/// Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
const complex_t *Read(bool on_dev = true) const
{ return mfem::Read(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::Read(GetMemory(), TotalSize(), false).
const complex_t *HostRead() const
{ return mfem::Read(data, Height()*Width(), false); }
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), on_dev).
complex_t *Write(bool on_dev = true)
{ return mfem::Write(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), false).
complex_t *HostWrite()
{ return mfem::Write(data, Height()*Width(), false); }
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), on_dev).
complex_t *ReadWrite(bool on_dev = true)
{ return mfem::ReadWrite(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), false).
complex_t *HostReadWrite()
{ return mfem::ReadWrite(data, Height()*Width(), false); }
void Swap(ComplexTypeDenseMatrix &other);
/// Return a reference to the real part of this matrix
const DenseMatrix &real() const;
/// Return a reference to the imaginary part of this matrix
const DenseMatrix &imag() const;
/// Destroys dense matrix.
virtual ~ComplexTypeDenseMatrix();
};
/// Specialization of the template function Swap<> for class ComplexTypeDenseMatrix
template<> inline void Swap<ComplexTypeDenseMatrix>(ComplexTypeDenseMatrix &a,
ComplexTypeDenseMatrix &b)
{
a.Swap(b);
}
// Inline methods
inline complex_t &ComplexTypeDenseMatrix::operator()(int i, int j)
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
return data[i+j*height];
}
inline const complex_t &ComplexTypeDenseMatrix::operator()
(int i, int j) const
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
return data[i+j*height];
}
} // namespace mfem
#endif // MFEM_COMPLEX_DENSEMAT
+424
View File
@@ -0,0 +1,424 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "../general/reducers.hpp"
#include "complex_vector.hpp"
using namespace std;
namespace mfem
{
ComplexVector::ComplexVector(const ComplexVector &v)
{
const int s = v.Size();
size = s;
if (s > 0)
{
MFEM_ASSERT(!v.data.Empty(), "invalid source vector");
data.New(s, v.data.GetMemoryType());
data.CopyFrom(v.data, s);
}
UseDevice(v.UseDevice());
}
ComplexVector::ComplexVector(const Vector &v)
{
const int s = v.Size();
size = s;
if (s > 0)
{
MFEM_ASSERT(!v.data.Empty(), "invalid source vector");
data.New(s, v.data.GetMemoryType());
MFEM_FORALL(i, size, data[i] = v.data[i]; );
}
UseDevice(v.UseDevice());
}
ComplexVector::ComplexVector(ComplexVector &&v)
{
*this = std::move(v);
}
complex_t &ComplexVector::Elem(int i)
{
return operator()(i);
}
const complex_t &ComplexVector::Elem(int i) const
{
return operator()(i);
}
complex_t ComplexVector::operator*(const complex_t *v) const
{
HostRead();
complex_t dot = 0.0;
#ifdef MFEM_USE_LEGACY_OPENMP
#pragma omp parallel for reduction(+:dot)
#endif
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
complex_t ComplexVector::operator*(const real_t *v) const
{
HostRead();
complex_t dot = 0.0;
#ifdef MFEM_USE_LEGACY_OPENMP
#pragma omp parallel for reduction(+:dot)
#endif
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
complex_t ComplexVector::operator*(const ComplexVector &v) const
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
if (size == 0) { return 0.0; }
const bool use_dev = UseDevice() || v.UseDevice();
const auto m_data = Read(use_dev), v_data = v.Read(use_dev);
// The standard way of computing the dot product is non-deterministic
complex_t prod = 0.0;
for (int i = 0; i < size; i++)
{
prod += m_data[i] * v_data[i];
}
return prod;
}
complex_t ComplexVector::operator*(const Vector &v) const
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
if (size == 0) { return 0.0; }
const bool use_dev = UseDevice() || v.UseDevice();
const auto m_data = Read(use_dev);
const auto v_data = v.Read(use_dev);
// The standard way of computing the dot product is non-deterministic
complex_t prod = 0.0;
for (int i = 0; i < size; i++)
{
prod += m_data[i] * v_data[i];
}
return prod;
}
ComplexVector &ComplexVector::operator=(const complex_t *v)
{
HostRead();
MFEM_FORALL(i, size, data[i] = v[i]; );
return *this;
}
ComplexVector &ComplexVector::operator=(const real_t *v)
{
HostRead();
MFEM_FORALL(i, size, data[i] = v[i]; );
return *this;
}
ComplexVector &ComplexVector::operator=(const ComplexVector &v)
{
#if 0
SetSize(v.Size(), v.data.GetMemoryType());
data.CopyFrom(v.data, v.Size());
UseDevice(v.UseDevice());
#else
SetSize(v.Size());
const bool vuse = v.UseDevice();
const bool use_dev = UseDevice() || vuse;
v.UseDevice(use_dev);
// keep 'data' where it is, unless 'use_dev' is true
if (use_dev) { Write(); }
data.CopyFrom(v.data, v.Size());
v.UseDevice(vuse);
#endif
return *this;
}
ComplexVector &ComplexVector::operator=(const Vector &v)
{
SetSize(v.Size());
const bool vuse = v.UseDevice();
const bool use_dev = UseDevice() || vuse;
v.UseDevice(use_dev);
// keep 'data' where it is, unless 'use_dev' is true
if (use_dev) { Write(); }
MFEM_FORALL(i, size, data[i] = v[i]; );
v.UseDevice(vuse);
return *this;
}
ComplexVector &ComplexVector::operator=(ComplexVector &&v)
{
v.Swap(*this);
if (this != &v) { v.Destroy(); }
return *this;
}
ComplexVector &ComplexVector::operator=(complex_t value)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = Write(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] = value; });
return *this;
}
ComplexVector &ComplexVector::operator=(real_t value)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = Write(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] = value; });
return *this;
}
ComplexVector &ComplexVector::operator*=(complex_t c)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] *= c; });
return *this;
}
ComplexVector &ComplexVector::operator*=(real_t c)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] *= c; });
return *this;
}
ComplexVector &ComplexVector::operator*=(const ComplexVector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] *= x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator*=(const Vector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] *= x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator/=(complex_t c)
{
const bool use_dev = UseDevice();
const int N = size;
const complex_t m = conj(c) / norm(c);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] *= m; });
return *this;
}
ComplexVector &ComplexVector::operator/=(real_t c)
{
const bool use_dev = UseDevice();
const int N = size;
const real_t m = 1.0/c;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] *= m; });
return *this;
}
ComplexVector &ComplexVector::operator/=(const ComplexVector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] /= x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator/=(const Vector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] /= x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator-=(complex_t c)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] -= c; });
return *this;
}
ComplexVector &ComplexVector::operator-=(real_t c)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] -= c; });
return *this;
}
ComplexVector &ComplexVector::operator-=(const ComplexVector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] -= x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator-=(const Vector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] -= x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator+=(complex_t c)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] += c; });
return *this;
}
ComplexVector &ComplexVector::operator+=(real_t c)
{
const bool use_dev = UseDevice();
const int N = size;
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] += c; });
return *this;
}
ComplexVector &ComplexVector::operator+=(const ComplexVector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] += x[i]; });
return *this;
}
ComplexVector &ComplexVector::operator+=(const Vector &v)
{
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] += x[i]; });
return *this;
}
ComplexVector &ComplexVector::Set(const Vector &Vr, const Vector &Vi)
{
MFEM_ASSERT(size == Vr.size && size == Vi.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || Vr.UseDevice() || Vi.UseDevice();
const int N = size;
const auto x = Vr.Read(use_dev);
const auto y = Vi.Read(use_dev);
auto z = Write(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ z[i] = complex_t(x[i], y[i]); });
return *this;
}
const Vector &ComplexVector::real() const
{
re_part.SetSize(size);
const bool use_dev = UseDevice();
const int N = size;
const auto z = Read(use_dev);
auto x = re_part.Write(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ x[i] = z[i].real(); });
return re_part;
}
const Vector &ComplexVector::imag() const
{
im_part.SetSize(size);
const bool use_dev = UseDevice();
const int N = size;
const auto z = Read(use_dev);
auto y = im_part.Write(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i)
{ y[i] = z[i].imag(); });
return im_part;
}
}
+479
View File
@@ -0,0 +1,479 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_COMPLEX_VECTOR
#define MFEM_COMPLEX_VECTOR
#include "vector.hpp"
#include "../general/complex_type.hpp"
namespace mfem
{
class ComplexVector
{
private:
Memory<complex_t > data;
int size;
mutable Vector re_part;
mutable Vector im_part;
public:
/// Default constructor for ComplexVector. Sets size = 0
ComplexVector() : size(0) { }
/// Copy constructor. Allocates a new data array and copies the data.
ComplexVector(const ComplexVector &);
/// Copy constructor. Allocates a new data array and copies the
/// data into real part of this vector.
ComplexVector(const Vector &);
/// Move constructor. "Steals" data from its argument.
ComplexVector(ComplexVector&& v);
/// @brief Creates vector of size s.
/// @warning Entries are not initialized to zero!
explicit ComplexVector(int s);
/// Creates a vector referencing an array of complex<doubles>,
/// owned by someone else.
/// The pointer @a data_ can be NULL. The data array can be replaced later
/// with SetData().
ComplexVector(complex_t *data_, int size_)
{ data.Wrap(data_, size_, false); size = size_; }
/// @brief Create a ComplexVector referencing a sub-vector of the
// ComplexVector @a base starting at the given offset, @a
// base_offset, and size @a size_.
ComplexVector(ComplexVector &base, int base_offset, int size_)
: data(base.data, base_offset, size_), size(size_) { }
/// Create a ComplexVector of size @a size_ using MemoryType @a mt.
ComplexVector(int size_, MemoryType mt)
: data(size_, mt), size(size_) { }
/// @brief Create a ComplexVector of size @a size_ using host
/// MemoryType @a h_mt and device MemoryType @a d_mt.
ComplexVector(int size_, MemoryType h_mt, MemoryType d_mt)
: data(size_, h_mt, d_mt), size(size_) { }
/// Create a vector from a statically sized C-style array of convertible type
template <typename CT, int N>
explicit ComplexVector(const CT (&values)[N]) : ComplexVector(N)
{ std::copy(values, values + N, begin()); }
/// Create a vector using a braced initializer list
template <typename CT, typename std::enable_if<
std::is_convertible<CT,complex_t >::value,bool>::type = true>
explicit ComplexVector(std::initializer_list<CT> values) : ComplexVector(
values.size())
{ std::copy(values.begin(), values.end(), begin()); }
/// Enable execution of Vector operations using the mfem::Device.
/// The default is to use Backend::CPU (serial execution on each MPI rank),
/// regardless of the mfem::Device configuration.
///
/// When appropriate, MFEM functions and class methods will enable the use
/// of the mfem::Device for their Vector parameters.
///
/// Some derived classes, e.g. GridFunction, enable the use of the
/// mfem::Device by default.
virtual void UseDevice(bool use_dev) const { data.UseDevice(use_dev); }
/// Return the device flag of the Memory object used by the Vector
virtual bool UseDevice() const { return data.UseDevice(); }
/// @brief Resize the vector to size @a s.
/// If the new size is less than or equal to Capacity() then the internal
/// data array remains the same. Otherwise, the old array is deleted, if
/// owned, and a new array of size @a s is allocated without copying the
/// previous content of the ComplexVector.
/// @warning In the second case above (new size greater than current one),
/// the vector will allocate new data array, even if it did not own the
/// original data! Also, new entries are not initialized!
void SetSize(int s);
/// Resize the vector to size @a s using MemoryType @a mt.
void SetSize(int s, MemoryType mt);
/// Resize the vector to size @a s using the MemoryType of @a v.
void SetSize(int s, const ComplexVector &v)
{ SetSize(s, v.GetMemory().GetMemoryType()); }
/// Resize the vector to size @a s using the MemoryType of @a v.
void SetSize(int s, const Vector &v)
{ SetSize(s, v.GetMemory().GetMemoryType()); }
/// Set the Vector data.
/// @warning This method should be called only when OwnsData() is false.
void SetData(complex_t *d)
{ data.Wrap(d, data.Capacity(), false); }
/// Set the Vector data and size.
/// The Vector does not assume ownership of the new data. The new size is
/// also used as the new Capacity().
/// @warning This method should be called only when OwnsData() is false.
/// @sa NewDataAndSize().
void SetDataAndSize(complex_t *d, int s)
{ data.Wrap(d, s, false); size = s; }
/// Set the Vector data and size, deleting the old data, if owned.
/// The Vector does not assume ownership of the new data. The new size is
/// also used as the new Capacity().
/// @sa SetDataAndSize().
void NewDataAndSize(complex_t *d, int s)
{
data.Delete();
SetDataAndSize(d, s);
}
/// Reset the Vector to use the given external Memory @a mem and size @a s.
/// If @a own_mem is false, the Vector will not own any of the pointers of
/// @a mem.
///
/// Note that when @a own_mem is true, the @a mem object can be destroyed
/// immediately by the caller but `mem.Delete()` should NOT be called since
/// the Vector object takes ownership of all pointers owned by @a mem.
///
/// @sa NewDataAndSize().
inline void NewMemoryAndSize(const Memory<complex_t > &mem,
int s, bool own_mem);
/// Reset the Vector to be a reference to a sub-vector of @a base.
inline void MakeRef(ComplexVector &base, int offset, int size);
/// @brief Reset the Vector to be a reference to a sub-vector of @a base
/// without changing its current size.
inline void MakeRef(ComplexVector &base, int offset);
/// Set the Vector data (host pointer) ownership flag.
void MakeDataOwner() const { data.SetHostPtrOwner(true); }
/// Destroy a vector
void Destroy();
/// @brief Delete the device pointer, if owned. If @a copy_to_host is true
/// and the data is valid only on device, move it to host before deleting.
/// Invalidates the device memory.
void DeleteDevice(bool copy_to_host = true)
{ data.DeleteDevice(copy_to_host); }
/// Returns the size of the vector.
inline int Size() const { return size; }
/// Return the size of the currently allocated data array.
/// It is always true that Capacity() >= Size().
inline int Capacity() const { return data.Capacity(); }
/// Return a pointer to the beginning of the ComplexVector data.
/// @warning This method should be used with caution as it gives write access
/// to the data of const-qualified ComplexVector%s.
inline complex_t *GetData() const
{ return const_cast<complex_t*>((const complex_t*)data); }
/// STL-like begin.
inline complex_t *begin() { return data; }
/// STL-like end.
inline complex_t *end() { return data + size; }
/// STL-like begin (const version).
inline const complex_t *begin() const { return data; }
/// STL-like end (const version).
inline const complex_t *end() const { return data + size; }
/// Return a reference to the Memory object used by the Vector.
Memory<complex_t > &GetMemory() { return data; }
/// @brief Return a reference to the Memory object used by the
/// ComplexVector, const version.
const Memory<complex_t > &GetMemory() const { return data; }
/// Update the memory location of the vector to match @a v.
void SyncMemory(const ComplexVector &v) const
{ GetMemory().Sync(v.GetMemory()); }
/// Update the alias memory location of the vector to match @a v.
void SyncAliasMemory(const ComplexVector &v) const
{ GetMemory().SyncAlias(v.GetMemory(),Size()); }
/// Read the Vector data (host pointer) ownership flag.
inline bool OwnsData() const { return data.OwnsHostPtr(); }
/// Changes the ownership of the data; after the call the Vector is empty
inline void StealData(complex_t **p)
{ *p = data; data.Reset(); size = 0; }
/// Changes the ownership of the data; after the call the Vector is empty
inline complex_t *StealData()
{ complex_t *p; StealData(&p); return p; }
/// Access Vector entries. Index i = 0 .. size-1.
complex_t &Elem(int i);
/// Read only access to Vector entries. Index i = 0 .. size-1.
const complex_t &Elem(int i) const;
/// Access Vector entries using () for 0-based indexing.
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
inline complex_t &operator()(int i);
/// Read only access to Vector entries using () for 0-based indexing.
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
inline const complex_t &operator()(int i) const;
/// Access Vector entries using [] for 0-based indexing.
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
inline complex_t &operator[](int i) { return (*this)(i); }
/// Read only access to Vector entries using [] for 0-based indexing.
/// @note If MFEM_DEBUG is enabled, bounds checking is performed.
inline const complex_t &operator[](int i) const
{ return (*this)(i); }
/// Dot product with a `complex<double> *` array.
/// @note No complex conjugate is performed
complex_t operator*(const complex_t *v) const;
complex_t operator*(const real_t *v) const;
/// Return the inner-product.
/// @note No complex conjugate is performed
complex_t operator*(const ComplexVector &v) const;
complex_t operator*(const Vector &v) const;
/// Copy Size() entries from @a v.
ComplexVector &operator=(const complex_t *v);
ComplexVector &operator=(const real_t *v);
/// Copy assignment.
/// @note Defining this method overwrites the implicitly defined copy
/// assignment operator.
ComplexVector &operator=(const ComplexVector &v);
ComplexVector &operator=(const Vector &v);
/// Move assignment
ComplexVector &operator=(ComplexVector&& v);
/// Redefine '=' for vector = constant.
ComplexVector &operator=(complex_t value);
ComplexVector &operator=(real_t value);
/// Scale vector by a constant
ComplexVector &operator*=(complex_t c);
ComplexVector &operator*=(real_t c);
/// Component-wise scaling: (*this)(i) *= v(i)
ComplexVector &operator*=(const ComplexVector &v);
ComplexVector &operator*=(const Vector &v);
/// Divide vector by a consant
ComplexVector &operator/=(complex_t c);
ComplexVector &operator/=(real_t c);
/// Component-wise division: (*this)(i) /= v(i)
ComplexVector &operator/=(const ComplexVector &v);
ComplexVector &operator/=(const Vector &v);
/// Subtract a constant from this vector
ComplexVector &operator-=(complex_t c);
ComplexVector &operator-=(real_t c);
/// Subtract a vector from this vector
ComplexVector &operator-=(const ComplexVector &v);
ComplexVector &operator-=(const Vector &v);
/// Add a constant to this vector
ComplexVector &operator+=(complex_t c);
ComplexVector &operator+=(real_t c);
/// Add a vector to this vector
ComplexVector &operator+=(const ComplexVector &v);
ComplexVector &operator+=(const Vector &v);
/// (*this) = x + i * y
ComplexVector &Set(const Vector &x, const Vector &y);
/// Swap the contents of two Vectors
inline void Swap(ComplexVector &other);
/// Return a reference to the real part of this vector
const Vector &real() const;
/// Return a reference to the imaginary part of this vector
const Vector &imag() const;
/// Destroys vector.
virtual ~ComplexVector();
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
virtual const complex_t *Read(bool on_dev = true) const
{ return mfem::Read(data, size, on_dev); }
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
virtual const complex_t *HostRead() const
{ return mfem::Read(data, size, false); }
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
virtual complex_t *Write(bool on_dev = true)
{ return mfem::Write(data, size, on_dev); }
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
virtual complex_t *HostWrite()
{ return mfem::Write(data, size, false); }
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
virtual complex_t *ReadWrite(bool on_dev = true)
{ return mfem::ReadWrite(data, size, on_dev); }
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
virtual complex_t *HostReadWrite()
{ return mfem::ReadWrite(data, size, false); }
};
inline ComplexVector::ComplexVector(int s)
{
MFEM_ASSERT(s>=0,"Unexpected negative size.");
size = s;
if (s > 0)
{
data.New(s);
}
}
inline void ComplexVector::SetSize(int s)
{
if (s == size)
{
return;
}
if (s <= data.Capacity())
{
size = s;
return;
}
// preserve a valid MemoryType and device flag
const MemoryType mt = data.GetMemoryType();
const bool use_dev = data.UseDevice();
data.Delete();
size = s;
data.New(s, mt);
data.UseDevice(use_dev);
}
inline void ComplexVector::SetSize(int s, MemoryType mt)
{
if (mt == data.GetMemoryType())
{
if (s == size)
{
return;
}
if (s <= data.Capacity())
{
size = s;
return;
}
}
const bool use_dev = data.UseDevice();
data.Delete();
if (s > 0)
{
data.New(s, mt);
size = s;
}
else
{
data.Reset();
size = 0;
}
data.UseDevice(use_dev);
}
inline void ComplexVector::NewMemoryAndSize(
const Memory<complex_t > &mem,
int s,
bool own_mem)
{
data.Delete();
size = s;
if (own_mem)
{
data = mem;
}
else
{
data.MakeAlias(mem, 0, s);
}
}
inline void ComplexVector::MakeRef(ComplexVector &base, int offset, int s)
{
data.Delete();
size = s;
data.MakeAlias(base.GetMemory(), offset, s);
}
inline void ComplexVector::MakeRef(ComplexVector &base, int offset)
{
data.Delete();
data.MakeAlias(base.GetMemory(), offset, size);
}
inline void ComplexVector::Destroy()
{
const bool use_dev = data.UseDevice();
data.Delete();
size = 0;
data.Reset();
data.UseDevice(use_dev);
}
inline complex_t &ComplexVector::operator()(int i)
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
inline const complex_t &ComplexVector::operator()(int i) const
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
inline void ComplexVector::Swap(ComplexVector &other)
{
mfem::Swap(data, other.data);
mfem::Swap(size, other.size);
}
/// Specialization of the template function Swap<> for class ComplexVector
template<> inline void Swap<ComplexVector>(ComplexVector &a, ComplexVector &b)
{
a.Swap(b);
}
inline ComplexVector::~ComplexVector()
{
data.Delete();
}
} // namespace mfem
#endif
+1
View File
@@ -24,6 +24,7 @@ class DenseMatrix : public Matrix
{
friend class DenseTensor;
friend class DenseMatrixInverse;
friend class ComplexTypeDenseMatrix;
private:
Memory<real_t> data;
-8
View File
@@ -1868,8 +1868,6 @@ HYPRE_Int HypreParMatrix::Mult(HypreParVector &x, HypreParVector &y,
void HypreParMatrix::Mult(real_t a, const Vector &x, real_t b, Vector &y) const
{
MFEM_PERF_FUNCTION;
MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
<< ", expected size = " << Width());
MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
@@ -1928,8 +1926,6 @@ void HypreParMatrix::Mult(real_t a, const Vector &x, real_t b, Vector &y) const
void HypreParMatrix::MultTranspose(real_t a, const Vector &x,
real_t b, Vector &y) const
{
MFEM_PERF_FUNCTION;
MFEM_ASSERT(x.Size() == Height(), "invalid x.Size() = " << x.Size()
<< ", expected size = " << Height());
MFEM_ASSERT(y.Size() == Width(), "invalid y.Size() = " << y.Size()
@@ -4095,8 +4091,6 @@ void HypreSolver::Setup(const HypreParVector &b, HypreParVector &x) const
{
if (setup_called) { return; }
MFEM_PERF_FUNCTION;
MFEM_VERIFY(A != NULL, "HypreParMatrix A is missing");
HYPRE_Int err_flag = SetupFcn()(*this, *A, b, x);
@@ -4122,8 +4116,6 @@ void HypreSolver::Setup(const Vector &b, Vector &x) const
void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
{
MFEM_PERF_FUNCTION;
HYPRE_Int err_flag;
if (A == NULL)
{
+6 -14
View File
@@ -50,19 +50,17 @@ void Operator::InitTVectors(const Operator *Po, const Operator *Ri,
void Operator::AddMult(const Vector &x, Vector &y, const real_t a) const
{
z_am.SetSize(y.Size());
z_am.UseDevice(true);
Mult(x, z_am);
y.Add(a, z_am);
mfem::Vector z(y.Size());
Mult(x, z);
y.Add(a, z);
}
void Operator::AddMultTranspose(const Vector &x, Vector &y,
const real_t a) const
{
z_am.SetSize(y.Size());
z_am.UseDevice(true);
MultTranspose(x, z_am);
y.Add(a, z_am);
mfem::Vector z(y.Size());
MultTranspose(x, z);
y.Add(a, z);
}
void Operator::ArrayMult(const Array<const Vector *> &X,
@@ -588,8 +586,6 @@ void ConstrainedOperator::EliminateRHS(const Vector &x, Vector &b) const
void ConstrainedOperator::ConstrainedMult(const Vector &x, Vector &y,
const bool transpose) const
{
MFEM_PERF_FUNCTION;
const int csz = constraint_list.Size();
if (csz == 0)
{
@@ -789,8 +785,6 @@ void RectangularConstrainedOperator::EliminateRHS(const Vector &x,
void RectangularConstrainedOperator::Mult(const Vector &x, Vector &y) const
{
MFEM_PERF_FUNCTION;
const int trial_csz = trial_constraints.Size();
const int test_csz = test_constraints.Size();
if (trial_csz == 0)
@@ -826,8 +820,6 @@ void RectangularConstrainedOperator::Mult(const Vector &x, Vector &y) const
void RectangularConstrainedOperator::MultTranspose(const Vector &x,
Vector &y) const
{
MFEM_PERF_FUNCTION;
const int trial_csz = trial_constraints.Size();
const int test_csz = test_constraints.Size();
if (test_csz == 0)
+2 -12
View File
@@ -13,7 +13,6 @@
#define MFEM_OPERATOR
#include "vector.hpp"
#include "../general/annotation.hpp"
namespace mfem
{
@@ -24,13 +23,6 @@ class RectangularConstrainedOperator;
/// Abstract operator
class Operator
{
private:
/// Auxiliary Vector used by the methods AddMult() and AddMultTranspose().
/** @note This Vector is private to prevent derived classes from accidentaly
using it in their implementation of Mult() or MultTranspose() which may
lead to hard-to-find bugs. */
mutable Vector z_am;
protected:
int height; ///< Dimension of the output / number of rows in the matrix.
int width; ///< Dimension of the input / number of columns in the matrix.
@@ -826,12 +818,10 @@ public:
explicit IdentityOperator(int n) : Operator(n) { }
/// Operator application
void Mult(const Vector &x, Vector &y) const override
{ MFEM_PERF_FUNCTION; y = x; }
void Mult(const Vector &x, Vector &y) const override { y = x; }
/// Application of the transpose
void MultTranspose(const Vector &x, Vector &y) const override
{ MFEM_PERF_FUNCTION; y = x; }
void MultTranspose(const Vector &x, Vector &y) const override { y = x; }
};
/// Returns true if P is the identity prolongation, i.e. if it is either NULL or
+39 -87
View File
@@ -55,8 +55,6 @@ IterativeSolver::IterativeSolver(MPI_Comm comm_)
real_t IterativeSolver::Dot(const Vector &x, const Vector &y) const
{
MFEM_PERF_FUNCTION;
#ifndef MFEM_USE_MPI
return (x * y);
#else
@@ -316,29 +314,25 @@ void OperatorJacobiSmoother::Mult(const Vector &x, Vector &y) const
MFEM_VERIFY(x.Size() == Width(), "invalid input vector");
MFEM_VERIFY(y.Size() == Height(), "invalid output vector");
auto DI = dinv.Read();
auto X = x.Read();
if (iterative_mode)
{
MFEM_VERIFY(oper, "iterative_mode == true requires the forward operator");
oper->Mult(y, residual); // r = A y
auto R = residual.Read();
auto Y = y.ReadWrite();
// y += D^{-1} (x - A y)
mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
{
Y[i] += DI[i] * (X[i] - R[i]);
});
subtract(x, residual, residual); // r = x - A y
}
else
{
auto Y = y.Write();
// y = D^{-1} x
mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
{
Y[i] = DI[i] * X[i];
});
residual = x;
y.UseDevice(true);
y = 0.0;
}
auto DI = dinv.Read();
auto R = residual.Read();
auto Y = y.ReadWrite();
mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
{
Y[i] += DI[i] * R[i];
});
}
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
@@ -354,8 +348,7 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
diag(d),
coeffs(order),
ess_tdof_list(ess_tdofs),
residual(order > 1 ? N : 0),
z(order > 1 ? N : 0),
residual(N),
oper(&oper_) { Setup(); }
#ifdef MFEM_USE_MPI
@@ -375,15 +368,14 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
real_t power_tolerance,
int power_seed)
#endif
: Solver((MFEM_PERF_BEGIN(_MFEM_FUNC_NAME), d.Size())),
: Solver(d.Size()),
order(order_),
N(d.Size()),
dinv(N),
diag(d),
coeffs(order),
ess_tdof_list(ess_tdofs),
residual(order > 1 ? N : 0),
z(order > 1 ? N : 0),
residual(N),
oper(&oper_)
{
OperatorJacobiSmoother invDiagOperator(diag, ess_tdofs, 1.0);
@@ -402,7 +394,6 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
power_seed);
Setup();
MFEM_PERF_END(_MFEM_FUNC_NAME);
}
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
@@ -431,7 +422,7 @@ void OperatorChebyshevSmoother::Setup()
{
// Invert diagonal
residual.UseDevice(true);
z.UseDevice(true);
helperVector.UseDevice(true);
auto D = diag.Read();
auto X = dinv.Write();
mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { X[i] = 1.0 / D[i]; });
@@ -441,20 +432,6 @@ void OperatorChebyshevSmoother::Setup()
X[I[i]] = 1.0;
});
const int order_save = order;
order = -1; // avoid early exit in SetOrder() when 'new_order' == 'order'
SetOrder(order_save);
}
void OperatorChebyshevSmoother::SetOrder(int new_order)
{
if (new_order == order) { return; }
order = new_order;
coeffs.SetSize(order);
residual.SetSize(order > 1 ? N : 0);
z.SetSize(order > 1 ? N : 0);
// Set up Chebyshev coefficients
// For reference, see e.g., Parallel multigrid smoothing: polynomial versus
// Gauss-Seidel by Adams et al.
@@ -524,8 +501,6 @@ void OperatorChebyshevSmoother::SetOrder(int new_order)
void OperatorChebyshevSmoother::Mult(const Vector& x, Vector &y) const
{
MFEM_PERF_FUNCTION;
if (iterative_mode)
{
MFEM_ABORT("Chebyshev smoother not implemented for iterative mode");
@@ -536,55 +511,32 @@ void OperatorChebyshevSmoother::Mult(const Vector& x, Vector &y) const
MFEM_ABORT("Chebyshev smoother requires operator");
}
// for k = 0, perform:
// r = D^{-1} x
// y = C_0 r
const real_t C_0 = coeffs[0];
auto Dinv = dinv.Read();
auto X = x.Read();
auto Y0 = y.Write();
if (order == 1)
{
mfem::forall(N, [=] MFEM_HOST_DEVICE (int i)
{
Y0[i] = C_0 * Dinv[i] * X[i];
});
}
else
{
auto R0 = residual.Write();
mfem::forall(N, [=] MFEM_HOST_DEVICE (int i)
{
Y0[i] = C_0 * (R0[i] = Dinv[i] * X[i]);
});
}
residual = x;
helperVector.SetSize(x.Size());
helperVector.UseDevice(true);
for (int k = 1; k < order; ++k)
{
// Apply: z = A r
oper->Mult(residual, z);
y.UseDevice(true);
y = 0.0;
// Scale residual by inverse diagonal and add weighted contribution to y:
// r = D^{-1} z
// y += C_k r
const real_t C_k = coeffs[k];
auto Z = z.Read();
for (int k = 0; k < order; ++k)
{
// Apply
if (k > 0)
{
oper->Mult(residual, helperVector);
residual = helperVector;
}
// Scale residual by inverse diagonal
const int n = N;
auto Dinv = dinv.Read();
auto R = residual.ReadWrite();
mfem::forall(n, [=] MFEM_HOST_DEVICE (int i) { R[i] *= Dinv[i]; });
// Add weighted contribution to y
auto Y = y.ReadWrite();
if (k < order-1)
{
auto R = residual.Write();
mfem::forall(N, [=] MFEM_HOST_DEVICE (int i)
{
Y[i] += C_k * (R[i] = Dinv[i] * Z[i]);
});
}
else
{
mfem::forall(N, [=] MFEM_HOST_DEVICE (int i)
{
Y[i] += C_k * Dinv[i] * Z[i];
});
}
auto C = coeffs.Read();
mfem::forall(n, [=] MFEM_HOST_DEVICE (int i) { Y[i] += C[k] * R[i]; });
}
}
@@ -3261,7 +3213,7 @@ void ResidualBCMonitor::MonitorResidual(
MPI_Comm comm = iter_solver->GetComm();
if (comm != MPI_COMM_NULL)
{
real_t glob_bc_norm_squared = 0.0;
double glob_bc_norm_squared = 0.0;
MPI_Reduce(&bc_norm_squared, &glob_bc_norm_squared, 1,
MPITypeMap<real_t>::mpi_type,
MPI_SUM, 0, comm);
+8 -9
View File
@@ -380,11 +380,11 @@ public:
void SetPositiveDiagonal(bool pos_diag = true) { use_abs_diag = pos_diag; }
/// Approach the solution of the linear system by applying Jacobi smoothing.
void Mult(const Vector &x, Vector &y) const override;
void Mult(const Vector &x, Vector &y) const;
/** @brief Approach the solution of the transposed linear system by applying
Jacobi smoothing. */
void MultTranspose(const Vector &x, Vector &y) const override { Mult(x, y); }
void MultTranspose(const Vector &x, Vector &y) const { Mult(x, y); }
/** @brief Recompute the diagonal using the method AssembleDiagonal of the
given new Operator, @a op. */
@@ -397,7 +397,7 @@ public:
When the new Operator, @a op, is not a (Par)BilinearForm, any previously
set array of essential true-dofs will be thrown away because in this case
any essential b.c. will be handled by the AssembleDiagonal method. */
void SetOperator(const Operator &op) override;
void SetOperator(const Operator &op);
private:
Vector dinv;
@@ -481,22 +481,21 @@ public:
/** @brief Approach the solution of the linear system by applying Chebyshev
smoothing. */
void Mult(const Vector &x, Vector &y) const override;
void Mult(const Vector &x, Vector &y) const;
/** @brief Approach the solution of the transposed linear system by applying
Chebyshev smoothing. */
void MultTranspose(const Vector &x, Vector &y) const override { Mult(x, y); }
void MultTranspose(const Vector &x, Vector &y) const { Mult(x, y); }
void SetOperator(const Operator &op_) override
void SetOperator(const Operator &op_)
{
oper = &op_;
}
void Setup();
void SetOrder(int new_order);
private:
int order;
const int order;
real_t max_eig_estimate;
const int N;
Vector dinv;
@@ -504,7 +503,7 @@ private:
Array<real_t> coeffs;
const Array<int>& ess_tdof_list;
mutable Vector residual;
mutable Vector z;
mutable Vector helperVector;
const Operator* oper;
};
-4
View File
@@ -764,8 +764,6 @@ void SparseMatrix::Mult(const Vector &x, Vector &y) const
void SparseMatrix::AddMult(const Vector &x, Vector &y, const real_t a) const
{
MFEM_PERF_FUNCTION;
MFEM_ASSERT(width == x.Size(), "Input vector size (" << x.Size()
<< ") must match matrix width (" << width << ")");
MFEM_ASSERT(height == y.Size(), "Output vector size (" << y.Size()
@@ -966,8 +964,6 @@ void SparseMatrix::MultTranspose(const Vector &x, Vector &y) const
void SparseMatrix::AddMultTranspose(const Vector &x, Vector &y,
const real_t a) const
{
MFEM_PERF_FUNCTION;
MFEM_ASSERT(height == x.Size(), "Input vector size (" << x.Size()
<< ") must match matrix height (" << height << ")");
MFEM_ASSERT(width == y.Size(), "Output vector size (" << y.Size()
+2 -16
View File
@@ -117,7 +117,7 @@ Vector::Vector(const Vector &v)
UseDevice(v.UseDevice());
}
Vector::Vector(Vector &&v) : Vector()
Vector::Vector(Vector &&v)
{
*this = std::move(v);
}
@@ -205,16 +205,14 @@ Vector &Vector::operator=(const Vector &v)
data.CopyFrom(v.data, v.Size());
UseDevice(v.UseDevice());
#else
SetSize(v.Size());
const bool vuse = v.UseDevice();
const bool use_dev = UseDevice() || vuse;
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
SetSize(v.Size());
v.UseDevice(use_dev);
// keep 'data' where it is, unless 'use_dev' is true
if (use_dev) { Write(); }
data.CopyFrom(v.data, v.Size());
v.UseDevice(vuse);
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
#endif
return *this;
}
@@ -229,11 +227,9 @@ Vector &Vector::operator=(Vector &&v)
Vector &Vector::operator=(real_t value)
{
const bool use_dev = UseDevice();
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
const int N = size;
auto y = Write(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i) { y[i] = value; });
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
return *this;
}
@@ -294,12 +290,10 @@ Vector &Vector::operator-=(const Vector &v)
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i) { y[i] -= x[i]; });
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
return *this;
}
@@ -317,12 +311,10 @@ Vector &Vector::operator+=(const Vector &v)
MFEM_ASSERT(size == v.size, "incompatible Vectors!");
const bool use_dev = UseDevice() || v.UseDevice();
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
const int N = size;
const auto x = v.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i) { y[i] += x[i]; });
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
return *this;
}
@@ -334,11 +326,9 @@ Vector &Vector::Add(const real_t a, const Vector &Va)
{
const int N = size;
const bool use_dev = UseDevice() || Va.UseDevice();
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
const auto x = Va.Read(use_dev);
auto y = ReadWrite(use_dev);
mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE (int i) { y[i] += a * x[i]; });
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
}
return *this;
}
@@ -455,7 +445,6 @@ void add(const Vector &v1, real_t alpha, const Vector &v2, Vector &v)
{
#if !defined(MFEM_USE_LEGACY_OPENMP)
const bool use_dev = v1.UseDevice() || v2.UseDevice() || v.UseDevice();
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
const int N = v.size;
// Note: get read access first, in case v is the same as v1/v2.
const auto d_x = v1.Read(use_dev);
@@ -465,7 +454,6 @@ void add(const Vector &v1, real_t alpha, const Vector &v2, Vector &v)
{
d_z[i] = d_x[i] + alpha * d_y[i];
});
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
#else
const real_t *v1p = v1.data, *v2p = v2.data;
real_t *vp = v.data;
@@ -581,7 +569,6 @@ void subtract(const Vector &x, const Vector &y, Vector &z)
#if !defined(MFEM_USE_LEGACY_OPENMP)
const bool use_dev = x.UseDevice() || y.UseDevice() || z.UseDevice();
if (use_dev) { MFEM_PERF_BEGIN(_MFEM_FUNC_NAME); }
const int N = x.size;
// Note: get read access first, in case z is the same as x/y.
const auto xd = x.Read(use_dev);
@@ -591,7 +578,6 @@ void subtract(const Vector &x, const Vector &y, Vector &z)
{
zd[i] = xd[i] - yd[i];
});
if (use_dev) { MFEM_PERF_END(_MFEM_FUNC_NAME); }
#else
const real_t *xp = x.data;
const real_t *yp = y.data;
+2
View File
@@ -80,6 +80,8 @@ inline real_t rand_real()
/// Vector data type.
class Vector
{
friend class ComplexVector;
protected:
Memory<real_t> data;

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