Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c968516f36 | ||
|
|
9a9d1ea967 | ||
|
|
46ee2ab5dc | ||
|
|
fb672667cc | ||
|
|
1d35fafd21 | ||
|
|
a0ed1bfbca | ||
|
|
0c08279225 | ||
|
|
bc84ce3b47 | ||
|
|
36abe386e0 | ||
|
|
a4868f2a98 | ||
|
|
21321b3abc | ||
|
|
bb4f39c3d7 | ||
|
|
b605a29988 | ||
|
|
7967e13f1d | ||
|
|
b615f22b66 | ||
|
|
e0fe515f21 | ||
|
|
269ee766db | ||
|
|
da8a221097 | ||
|
|
20d6e63df0 | ||
|
|
2288cdcb7f | ||
|
|
233337c9d1 | ||
|
|
4d9cd853b7 | ||
|
|
27352658c3 | ||
|
|
b8a303a07a | ||
|
|
cee9bf3bb2 |
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -18,7 +18,7 @@
|
||||
setup_baseline:
|
||||
tags:
|
||||
- shell
|
||||
- dane
|
||||
- ruby
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
==================================
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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; }
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 >, Mode m)
|
||||
: gtopo(gt), mode(m)
|
||||
|
||||
+24
-59
@@ -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).
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -193,4 +193,4 @@ void mfem_warning(const char *msg)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
{
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -24,6 +24,7 @@ class DenseMatrix : public Matrix
|
||||
{
|
||||
friend class DenseTensor;
|
||||
friend class DenseMatrixInverse;
|
||||
friend class ComplexTypeDenseMatrix;
|
||||
|
||||
private:
|
||||
Memory<real_t> data;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user