Compare commits
126
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
368e36a060 | ||
|
|
e8ea798077 | ||
|
|
69498877af | ||
|
|
e9782cf437 | ||
|
|
9a9f64d8bb | ||
|
|
0eb3001197 | ||
|
|
71f25f5e16 | ||
|
|
43c50376eb | ||
|
|
e4a066edf4 | ||
|
|
886a4c89e4 | ||
|
|
b36ed0e9e9 | ||
|
|
e72ed572c4 | ||
|
|
bfa68b0b18 | ||
|
|
1181253c61 | ||
|
|
d379628b54 | ||
|
|
ec45731607 | ||
|
|
19b57a28b7 | ||
|
|
46627b8655 | ||
|
|
0fe2887b1c | ||
|
|
03ad5fbae5 | ||
|
|
a018c66a9e | ||
|
|
14be9236bc | ||
|
|
45d38fd1ba | ||
|
|
734ae021f0 | ||
|
|
2c23f5b0bd | ||
|
|
a17fb5b587 | ||
|
|
bc404c0f85 | ||
|
|
8e59709ab8 | ||
|
|
f4d0cef65c | ||
|
|
1bef281b45 | ||
|
|
95a86d2bd8 | ||
|
|
ccfee841cb | ||
|
|
7f9cba0daa | ||
|
|
1ae0d6bef6 | ||
|
|
6031b7b2a3 | ||
|
|
3efda06ca8 | ||
|
|
55c7f5821e | ||
|
|
4ffa0434f7 | ||
|
|
2a30ba157f | ||
|
|
1d1a33b365 | ||
|
|
6c8349dbbe | ||
|
|
e4b760a30b | ||
|
|
18ca7ab2c9 | ||
|
|
3f097ae869 | ||
|
|
f912b347a2 | ||
|
|
90a6dc7c99 | ||
|
|
17455e7b08 | ||
|
|
81f209953e | ||
|
|
b3d4579618 | ||
|
|
47855ff8cf | ||
|
|
115a11bf79 | ||
|
|
b81cce6f45 | ||
|
|
550ca6b702 | ||
|
|
5be6c032db | ||
|
|
1eafad0af9 | ||
|
|
e61ac08b8d | ||
|
|
37b22e4e48 | ||
|
|
767f4bde18 | ||
|
|
e09d02ff2f | ||
|
|
da9a797b46 | ||
|
|
92d8e7f46b | ||
|
|
a147b61e98 | ||
|
|
14323a6da5 | ||
|
|
b30809a663 | ||
|
|
e68e96cd00 | ||
|
|
d9e7737354 | ||
|
|
01479824f5 | ||
|
|
389f1ef5ce | ||
|
|
346f5b28c9 | ||
|
|
3867b49d3b | ||
|
|
e4a32db1dc | ||
|
|
84cb244bc2 | ||
|
|
11b878612b | ||
|
|
dc45953e0d | ||
|
|
b4ebf12ff8 | ||
|
|
f9cc9c6c15 | ||
|
|
28094ba952 | ||
|
|
72c7405b08 | ||
|
|
2ce3063b2c | ||
|
|
dbd90b50cb | ||
|
|
b640cbe587 | ||
|
|
5a4ba178bb | ||
|
|
f4c44a86bc | ||
|
|
a2e780d18d | ||
|
|
2855a6daa3 | ||
|
|
490026763d | ||
|
|
087a87bf40 | ||
|
|
43c5a04aae | ||
|
|
cda6ee0580 | ||
|
|
a3639fe70c | ||
|
|
78c5f51309 | ||
|
|
3880a48af5 | ||
|
|
9f7f84defb | ||
|
|
ce5fd63d0a | ||
|
|
e3c45310b0 | ||
|
|
fdec992469 | ||
|
|
3e23785a13 | ||
|
|
52a0337f5e | ||
|
|
f021091e9e | ||
|
|
21b592778a | ||
|
|
c3233944ad | ||
|
|
dd097f2cf5 | ||
|
|
a8eba4053f | ||
|
|
2df1b13811 | ||
|
|
1d28cfe699 | ||
|
|
5957053d85 | ||
|
|
4ba8a2e148 | ||
|
|
e494dc8255 | ||
|
|
9fec24c86c | ||
|
|
664d0a543a | ||
|
|
20c3c30c87 | ||
|
|
083c4845db | ||
|
|
a23c634e37 | ||
|
|
556f6d72f5 | ||
|
|
3ebb7a7b65 | ||
|
|
02136f8f5b | ||
|
|
37adfbc405 | ||
|
|
0a142864e9 | ||
|
|
f975c36e49 | ||
|
|
471bc1f10d | ||
|
|
8f52418e5c | ||
|
|
41a095e972 | ||
|
|
effc07cdbf | ||
|
|
46ac9d937f | ||
|
|
2af3577482 | ||
|
|
fb5e610ca5 |
@@ -58,6 +58,7 @@ jobs:
|
||||
build-system: [make, cmake]
|
||||
hypre-target: [int32]
|
||||
precision: [fp64]
|
||||
enzyme: [false]
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
build-system: cmake
|
||||
@@ -80,15 +81,17 @@ jobs:
|
||||
codecov: YES
|
||||
- os: ubuntu-latest
|
||||
target: dbg
|
||||
config-opts: 'CPPFLAGS+=-Og'
|
||||
config-opts: "CPPFLAGS+=-Og"
|
||||
- os: macos-latest
|
||||
codecov: NO
|
||||
- os: windows-latest
|
||||
codecov: NO
|
||||
# config-opts: '-G "Ninja Multi-Config"'
|
||||
- os: windows-latest
|
||||
target: opt
|
||||
mpi: par
|
||||
config-opts: '-DBUILD_SHARED_LIBS=ON'
|
||||
config-opts: "-DBUILD_SHARED_LIBS=ON"
|
||||
# config-opts: '-DBUILD_SHARED_LIBS=ON -G "Ninja Multi-Config"'
|
||||
- os: ubuntu-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
@@ -96,7 +99,7 @@ jobs:
|
||||
build-system: cmake
|
||||
hypre-target: int32
|
||||
precision: fp64
|
||||
config-opts: '-DCMAKE_INSTALL_PREFIX=../cmake-install'
|
||||
config-opts: "-DCMAKE_INSTALL_PREFIX=../cmake-install"
|
||||
# This option can be set to pass additional configuration options to
|
||||
# the MFEM configuration command.
|
||||
# config-opts: '-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||
@@ -121,7 +124,17 @@ jobs:
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp32
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
- os: macos-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp64
|
||||
enzyme: true
|
||||
config-opts: MFEM_USE_ENZYME=YES ENZYME_DIR=$(brew --prefix enzyme)
|
||||
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}${{ matrix.enzyme && '-enzyme' || '' }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -131,8 +144,8 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: easimon/maximize-build-space@v8
|
||||
with:
|
||||
overprovision-lvm: 'true'
|
||||
remove-android: 'true'
|
||||
overprovision-lvm: "true"
|
||||
remove-android: "true"
|
||||
|
||||
# Checkout MFEM in "mfem" subdirectory. Final path:
|
||||
# /home/runner/work/mfem/mfem/mfem
|
||||
@@ -144,6 +157,17 @@ jobs:
|
||||
# Fetch the complete history for codecov to access commits ID
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Windows environment - PowerShell [debug]
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
ls env: | fl
|
||||
|
||||
- name: Windows environment - Bash [debug]
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
env
|
||||
shell: bash
|
||||
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
@@ -258,6 +282,18 @@ jobs:
|
||||
run: |
|
||||
vcpkg install metis-mfem --triplet=x64-windows-static --overlay-ports=${{ env.MFEM_TOP_DIR }}/config/vcpkg/ports
|
||||
|
||||
# It's usually fine to build the above TPLs with a different compiler.
|
||||
#
|
||||
- name: install Enzyme (macOS w/ Enzyme)
|
||||
if: matrix.enzyme && matrix.os == 'macos-latest'
|
||||
run: |
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew update
|
||||
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
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
library-only: false
|
||||
config-options:
|
||||
CXX="clang++-18"
|
||||
CXXFLAGS="-g -O1 -std=c++11
|
||||
CXXFLAGS="-g -O1 -std=c++17
|
||||
-fsanitize=address
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize-address-use-after-scope"
|
||||
|
||||
@@ -201,6 +201,9 @@ examples/superlu/sol.*
|
||||
miniapps/adjoint/cvsRoberts_ASAi_dns
|
||||
miniapps/adjoint/adjoint_advection_diffusion
|
||||
|
||||
miniapps/dfem/dfem-minimal-surface
|
||||
miniapps/dfem/dfem-minimal-surface-output
|
||||
|
||||
miniapps/electromagnetics/volta
|
||||
miniapps/electromagnetics/tesla
|
||||
miniapps/electromagnetics/maxwell
|
||||
|
||||
@@ -10,15 +10,42 @@
|
||||
|
||||
Version 4.8.1 (development)
|
||||
===========================
|
||||
- Added support for variational resampling of H1 vector fields to ParMoonolith
|
||||
integration.
|
||||
|
||||
Starting with this version, MFEM requires a C++17 compiler.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Introduced dFEM: a new MFEM capability for Automatic Differentiation (AD) of
|
||||
nonlinear finite element operators, based on Enzyme or dual numbers AD at
|
||||
quadrature points. These features are part of the new mfem::future namespace
|
||||
and some of the API can change in the future. See the new dFEM minimal surface
|
||||
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use.
|
||||
|
||||
- Using Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM
|
||||
built with plugin support. See INSTALL for more details.
|
||||
|
||||
- In the ParMoonolith integration, added support for variational resampling of
|
||||
H1 vector fields.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added support for higher order meshes in Mesh::MakeSimplicial and
|
||||
ParMesh::MakeSimplicial.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
|
||||
executes on device if either the vector or the array have the device flag
|
||||
set. This is most often used for setting constant essential boundary
|
||||
conditions. A new function Vector::SetSubVectorHost has been added in cases
|
||||
where host execution is always needed (e.g. when the DOFs array is small).
|
||||
|
||||
API changes:
|
||||
-----------
|
||||
- mfem::internal::tensor and mfem::internal::dual have been moved to
|
||||
mfem::future::tensor and mfem::future::dual.
|
||||
|
||||
|
||||
Version 4.8, released on Apr 9, 2025
|
||||
====================================
|
||||
|
||||
|
||||
+27
-6
@@ -18,8 +18,8 @@ message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
|
||||
"Path to optional user configuration file.")
|
||||
|
||||
# Require C++11 and disable compiler-specific extensions
|
||||
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to use.")
|
||||
# Require C++17 and disable compiler-specific extensions
|
||||
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to use.")
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL
|
||||
"Force the use of the chosen C++ standard.")
|
||||
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Enable C++ standard extensions.")
|
||||
@@ -133,7 +133,6 @@ if (MFEM_USE_CUDA)
|
||||
if (NOT CMAKE_CUDA_HOST_COMPILER)
|
||||
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
set(CUDA_FLAGS "--expt-extended-lambda")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
|
||||
elseif (NOT CMAKE_CUDA_ARCHITECTURES)
|
||||
@@ -148,6 +147,20 @@ if (MFEM_USE_CUDA)
|
||||
endif()
|
||||
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
|
||||
enable_language(CUDA)
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
# backup try to detect if this is clang or nvcc
|
||||
if(CMAKE_CUDA_COMPILER MATCHES "nvcc$")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
else()
|
||||
if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
|
||||
# nvcc
|
||||
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
|
||||
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
|
||||
"CUDA standard to use.")
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
|
||||
@@ -256,7 +269,11 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
|
||||
if (OPENMP_FOUND)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
if (MFEM_USE_CUDA)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
|
||||
if(MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -532,9 +549,10 @@ if (MFEM_USE_TRIBOL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enzyme
|
||||
if (MFEM_USE_ENZYME)
|
||||
find_package(ENZYME REQUIRED)
|
||||
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
|
||||
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
|
||||
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
|
||||
endif()
|
||||
|
||||
# MFEM_TIMER_TYPE
|
||||
@@ -686,6 +704,9 @@ if (MFEM_USE_MPI)
|
||||
target_link_libraries(mfem PUBLIC ${MPI_CXX_LINK_FLAGS})
|
||||
endif()
|
||||
endif()
|
||||
if (MFEM_USE_ENZYME)
|
||||
target_link_libraries(mfem PUBLIC ClangEnzymeFlags)
|
||||
endif()
|
||||
|
||||
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
|
||||
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
|
||||
|
||||
+5
-1
@@ -120,6 +120,7 @@ The MFEM source code has the following structure:
|
||||
| └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── dfem
|
||||
│ ├── eltrans
|
||||
│ ├── fe
|
||||
│ ├── gslib
|
||||
@@ -138,6 +139,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── adjoint
|
||||
│ ├── autodiff
|
||||
│ ├── common
|
||||
│ ├── dfem
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── gslib
|
||||
@@ -549,6 +551,8 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Add a short description of the example in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New miniapps:
|
||||
- [ ] All sample runs at the top of the miniapp source file work.
|
||||
- [ ] Add to internal testing repo, if sample runs should be included in nightly tests [internally](#tests-at-llnl).
|
||||
- [ ] Exclude long sample runs from automated testing, with `* ` (one space) before the command.
|
||||
- [ ] Update top-level `makefile` and `makefile` in corresponding miniapp directory.
|
||||
- [ ] Add the miniapp binary and any files generated by it to the top-level `.gitignore` file.
|
||||
- [ ] Update CMake build system:
|
||||
@@ -743,7 +747,7 @@ and debug build is performed with a simple run of `ex1` to verify the executable
|
||||
- We mirror the `master` and `next` branches internally (to `gh-master` and
|
||||
`gh-next`) and run longer nightly tests via cron. On the weekends, a more
|
||||
extensive test is run which extracts and executes all the different sample
|
||||
runs from each example.
|
||||
runs from each example and most miniapps.
|
||||
|
||||
- We also mirror PRs on the LLNL GitLab instance. PR mirroring can only be
|
||||
triggered by _LLNL developers_, but test status is publicly available. Only
|
||||
|
||||
@@ -263,7 +263,7 @@ See the configuration file config/defaults.mk for the default settings.
|
||||
Compilers:
|
||||
CXX - C++ compiler, serial build
|
||||
MPICXX - MPI C++ compiler, parallel build
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc'
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
|
||||
|
||||
Compiler options:
|
||||
OPTIM_FLAGS - Options for optimized build
|
||||
@@ -608,11 +608,12 @@ MFEM_USE_TRIBOL = YES/NO
|
||||
|
||||
MFEM_USE_ENZYME = YES/NO
|
||||
Enables automatic differentiation support through the LLVM plugin Enzyme.
|
||||
This requires the compiler to be set to clang (>=14.0.0). We also advise to
|
||||
use the link time optimization (LTO) plugin, to enable functions that you
|
||||
define over multiple files (compilation units) and want to be differentiated
|
||||
automatically, to work. This requires to also use LLVM/LLD for linking.
|
||||
Recommended options are in config/defaults.mk.
|
||||
This requires the compiler to be set to clang (>=14.0.0). We also advise the
|
||||
use of the link time optimization (LTO) plugin, so functions defined over
|
||||
multiple files (compilation units) can be differentiated automatically. This
|
||||
requires to also use LLVM/LLD for linking. The recommended options are in
|
||||
config/defaults.mk. For more detailed instructions, see the section "Specific
|
||||
options for Enzyme" below.
|
||||
|
||||
MFEM_BUILD_TAG = (any value)
|
||||
An optional tag to characterize the build. Exported to config/config.mk.
|
||||
@@ -835,7 +836,7 @@ The specific libraries and their options are:
|
||||
|
||||
- CUDA (optional), used when MFEM_USE_CUDA = YES.
|
||||
URL: https://developer.nvidia.com/cuda-toolkit
|
||||
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
|
||||
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB, CUDA_DIR (when CUDA_CXX=clang++).
|
||||
Versions: CUDA >= 10.1.168.
|
||||
|
||||
- HIP (optional), used when MFEM_USE_HIP = YES.
|
||||
@@ -911,7 +912,7 @@ The specific libraries and their options are:
|
||||
- Enzyme, used when MFEM_USE_ENZYME = YES. Requires LLVM/Clang >= 14.0.0.
|
||||
URL: https://github.com/EnzymeAD/Enzyme
|
||||
Options: ENZYME_DIR, ENZYME_OPT, ENZYME_LIB.
|
||||
Versions: Enzyme >= v0.0.33.
|
||||
Versions: Enzyme >= v0.0.176.
|
||||
|
||||
|
||||
Building with CMake
|
||||
@@ -1190,3 +1191,73 @@ the older HIP C++ library build/linkage. To ensure proper build and linkage
|
||||
check that `CMAKE_CXX_COMPILER` and `CMAKE_HIP_COMPILER` are set to the same
|
||||
compiler. This is especially important when using an MPI compiler (for example
|
||||
crayCC) where some linker flags may get dropped if these two are not identical.
|
||||
|
||||
Specific options for Enzyme
|
||||
===========================
|
||||
To work properly, MFEM and Enzyme need to use the same LLVM/Clang configuration.
|
||||
For example, on macOS this can be done by using Homebrew: first install Enzyme,
|
||||
which in turn installs LLVM as a dependency (as of May 2025, this is LLVM 19):
|
||||
|
||||
brew install enzyme
|
||||
|
||||
In order to ensure the correct compiler choice for the MFEM makefile build, set
|
||||
|
||||
CXX = $(shell brew --prefix llvm@19)/bin/clang++
|
||||
|
||||
in the user.mk file (adapted from config/defaults.mk, see the section "Building
|
||||
with GNU make" above). With MPI, it is convenient to set
|
||||
|
||||
MPICXX = OMPI_CXX=$(CXX) mpicxx
|
||||
|
||||
for OpenMPI and
|
||||
|
||||
MPICXX = MPICH_CXX=$(CXX) mpicxx
|
||||
|
||||
for MPICH.
|
||||
|
||||
Additionally, the Enzyme directory needs to be set in user.mk as follows:
|
||||
|
||||
ENZYME_DIR = $(shell brew --prefix enzyme)
|
||||
|
||||
Specifically, a full build on a Mac can be tested by adding the following
|
||||
user.mk file in the config/ directory
|
||||
|
||||
MFEM_USE_ENZYME = YES
|
||||
ENZYME_DIR = $(shell brew --prefix enzyme)
|
||||
LLVM_DIR = $(shell brew --prefix llvm@19)
|
||||
CXX = $(LLVM_DIR)/bin/clang++
|
||||
MFEM_USE_MPI = YES
|
||||
MPICXX = OMPI_CXX=$(CXX) mpicxx
|
||||
|
||||
and running
|
||||
|
||||
make config
|
||||
make -j
|
||||
cd miniapps/dfem
|
||||
make
|
||||
./dfem-minimal-surface
|
||||
|
||||
On Linux systems, for example Ubuntu 24.04, use the package manager to install
|
||||
the Enzyme dependencies
|
||||
|
||||
sudo apt install libclang-dev libzstd-dev llvm-dev clang
|
||||
|
||||
and then clone and build Enzyme
|
||||
|
||||
cd $HOME
|
||||
git clone https://github.com/EnzymeAD/Enzyme.git
|
||||
cd Enzyme/enzyme && mkdir build && cd build
|
||||
CC=clang CXX=clang++ cmake .. -DLLVM_DIR=/usr/lib/llvm-18/lib/cmake -DCMAKE_INSTALL_PREFIX=$HOME/Enzyme/enzyme/build
|
||||
make -j
|
||||
make install
|
||||
|
||||
From here, one can proceed in the same way using the following user.mk settings
|
||||
|
||||
MFEM_USE_ENZYME = YES
|
||||
ENZYME_DIR = $(HOME)/Enzyme/enzyme/build
|
||||
CXX = clang++
|
||||
MFEM_USE_MPI = YES
|
||||
MPICXX = OMPI_CXX=$(CXX) mpicxx
|
||||
|
||||
On other Linux systems the LLVM packages may have different names, for example
|
||||
on RHEL9, one needs to "sudo yum install llvm-devel libzstd clang-devel".
|
||||
|
||||
@@ -1,27 +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.
|
||||
|
||||
message(STATUS "Looking for ENZYME ...")
|
||||
message(STATUS " in ENZYME_DIR = ${ENZYME_DIR}")
|
||||
|
||||
# Make sure the directory and version combination works. Do nothing otherwise.
|
||||
if(EXISTS "${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
|
||||
message(STATUS "Found ENZYME: ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
|
||||
|
||||
# Set ENZYME_FOUND
|
||||
set(ENZYME_FOUND TRUE CACHE BOOL "ENZYME was found." FORCE)
|
||||
|
||||
# Set CXX flags to accommodate the Enzyme Clang plugin
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -load -Xclang ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so -mllvm -enzyme-loose-types=1")
|
||||
set(MFEM_USE_ENZYME YES)
|
||||
else()
|
||||
|
||||
endif()
|
||||
@@ -86,8 +86,9 @@ if (HYPRE_FOUND AND HYPRE_USING_CUDA)
|
||||
mfem_culib_set_libraries(CUSPARSE cusparse)
|
||||
mfem_culib_set_libraries(CURAND curand)
|
||||
mfem_culib_set_libraries(CUBLAS cublas)
|
||||
mfem_culib_set_libraries(CUSOLVER cusolver)
|
||||
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
|
||||
${CUBLAS_LIBRARIES})
|
||||
${CUBLAS_LIBRARIES} ${CUSOLVER_LIBRARIES})
|
||||
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
|
||||
"HYPRE libraries + dependencies." FORCE)
|
||||
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
|
||||
|
||||
@@ -125,7 +125,9 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
|
||||
if (MFEM_USE_CUDA)
|
||||
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST}
|
||||
PROPERTIES LANGUAGE CUDA)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Actually add the executable
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ MFEM_MPIEXEC_NP = @MFEM_MPIEXEC_NP@
|
||||
MFEM_MPI_NP = @MFEM_MPI_NP@
|
||||
|
||||
# The NVCC compiler cannot link with -x=cu
|
||||
MFEM_LINK_FLAGS := $(filter-out -x=cu -xhip, $(MFEM_FLAGS))
|
||||
MFEM_LINK_FLAGS := $(filter-out -x=cu -xcuda -xhip, $(MFEM_FLAGS))
|
||||
|
||||
# Optional extra configuration
|
||||
@MFEM_CONFIG_EXTRA@
|
||||
|
||||
@@ -268,6 +268,8 @@ set(TRIBOL_DIR "${MFEM_DIR}/../tribol" CACHE PATH "Path to Tribol")
|
||||
set(Tribol_REQUIRED_PACKAGES "Axom/core/mint/slam/slic" CACHE STRING
|
||||
"Additional packages required by Tribol")
|
||||
|
||||
set(ENZYME_DIR "${MFEM_DIR}/../enzyme" CACHE PATH "Path to Enzyme")
|
||||
|
||||
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
|
||||
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
|
||||
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
|
||||
|
||||
+34
-29
@@ -24,7 +24,7 @@ EGREP_BIN = $(shell command -v egrep 2> /dev/null)
|
||||
CXX = g++
|
||||
MPICXX = mpicxx
|
||||
|
||||
BASE_FLAGS = -std=c++11
|
||||
BASE_FLAGS = -std=c++17
|
||||
OPTIM_FLAGS = -O3 $(BASE_FLAGS)
|
||||
DEBUG_FLAGS = -g $(XCOMPILER)-Wall $(BASE_FLAGS)
|
||||
|
||||
@@ -43,12 +43,23 @@ SHARED = NO
|
||||
|
||||
# CUDA configuration options
|
||||
#
|
||||
# If you set MFEM_USE_ENZYME=YES, CUDA_CXX has to be configured to use cuda with
|
||||
# clang as its host compiler.
|
||||
# If you set MFEM_USE_ENZYME=YES, must use CUDA_CXX=clang++
|
||||
CUDA_CXX = nvcc
|
||||
CUDA_ARCH = sm_60
|
||||
CUDA_FLAGS = -x=cu --expt-extended-lambda -arch=$(CUDA_ARCH)
|
||||
# Prefixes for passing flags to the host compiler and linker when using CUDA_CXX
|
||||
# Base CUDA install directory, only needed if building with clang+cuda:
|
||||
# The default setting is:
|
||||
# 1. If CUDA_HOME is defined and non-empty, use that.
|
||||
# 2. If nvcc is in the path, use the directory two levels up from that.
|
||||
# 3. Use /usr/local/cuda
|
||||
CUDA_DIR = $(or $(CUDA_HOME),$(patsubst %/,%,$(dir \
|
||||
$(patsubst %/,%,$(dir $(shell command -v nvcc))))),/usr/local/cuda)
|
||||
# flags for clang+cuda
|
||||
CLANG_CUDA_FLAGS = -xcuda --cuda-path=$(CUDA_DIR) --cuda-gpu-arch=$(CUDA_ARCH)
|
||||
# flags for nvcc
|
||||
NVCC_FLAGS = -x=cu --expt-extended-lambda --expt-relaxed-constexpr \
|
||||
-arch=$(CUDA_ARCH)
|
||||
# Prefixes for passing flags to the host compiler and linker when using
|
||||
# CUDA_CXX=nvcc
|
||||
CUDA_XCOMPILER = -Xcompiler=
|
||||
CUDA_XLINKER = -Xlinker=
|
||||
|
||||
@@ -227,7 +238,7 @@ HYPRE_OPT = -I$(HYPRE_DIR)/include
|
||||
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
|
||||
ifeq (YES,$(MFEM_USE_CUDA))
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
HYPRE_LIB += -lcusparse -lcurand -lcublas
|
||||
HYPRE_LIB += -lcusolver -lcusparse -lcurand -lcublas
|
||||
endif
|
||||
ifeq (YES,$(MFEM_USE_HIP))
|
||||
# This is only necessary when hypre is built with hip:
|
||||
@@ -242,7 +253,7 @@ ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK)$(MFEM_USE_MUMPS),NONONO)
|
||||
METIS_OPT =
|
||||
METIS_LIB = -L$(METIS_DIR) -lmetis
|
||||
else
|
||||
METIS_DIR = @MFEM_DIR@/../metis-5.0
|
||||
METIS_DIR = @MFEM_DIR@/../metis-5.1.0
|
||||
METIS_OPT = -I$(METIS_DIR)/include
|
||||
METIS_LIB = -L$(METIS_DIR)/lib -lmetis
|
||||
endif
|
||||
@@ -511,6 +522,9 @@ GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
|
||||
# CUDA library configuration
|
||||
CUDA_OPT =
|
||||
CUDA_LIB = -lcusparse -lcublas
|
||||
CLANG_CUDA_LIB = -L$(CUDA_DIR)/lib64 -L$(CUDA_DIR)/lib \
|
||||
$(XLINKER)-rpath,$(CUDA_DIR)/lib64,-rpath,$(CUDA_DIR)/lib \
|
||||
-lcudart -ldl -lrt -pthread
|
||||
|
||||
# HIP library configuration
|
||||
HIP_OPT =
|
||||
@@ -610,29 +624,20 @@ TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mi
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
# If you want to enable automatic differentiation at compile time, use the
|
||||
# options below, adapted to your configuration. To be more flexible, we
|
||||
# recommend using the Enzyme plugin during link time optimization. One option is
|
||||
# to add your options to the global compiler/linker flags like
|
||||
#
|
||||
# BASE_FLAGS += -flto
|
||||
# CXX_XLINKER += -fuse-ld=lld -Wl,--lto-legacy-pass-manager\
|
||||
# -Wl,-mllvm=-load=$(ENZYME_DIR)/LLDEnzyme-$(ENZYME_VERSION).so -Wl,
|
||||
#
|
||||
ENZYME_DIR ?= @MFEM_DIR@/../enzyme
|
||||
ENZYME_VERSION ?= 14
|
||||
ENZYME_OPT = -fno-experimental-new-pass-manager -Xclang -load -Xclang $(ENZYME_DIR)/ClangEnzyme-$(ENZYME_VERSION).so
|
||||
ENZYME_LIB = ""
|
||||
|
||||
# Google Benchmark, SUNDIALS >= 6.4.0, STRUMPACK, RAJA, UMPIRE, and Tribol require C++14:
|
||||
ifneq ($(filter YES,$(MFEM_USE_BENCHMARK) $(MFEM_USE_SUNDIALS) $(MFEM_USE_STRUMPACK) $(MFEM_USE_RAJA) $(MFEM_USE_UMPIRE) $(MFEM_USE_TRIBOL)),)
|
||||
BASE_FLAGS = -std=c++14
|
||||
endif
|
||||
# Ginkgo requires C++17:
|
||||
ifeq ($(MFEM_USE_GINKGO),YES)
|
||||
BASE_FLAGS = -std=c++17
|
||||
ENZYME_DIR = @MFEM_DIR@/../enzyme
|
||||
ENZYME_PLUGIN = $(abspath $(wildcard $(subst \
|
||||
@MFEM_DIR@,$(MFEM_DIR),$(ENZYME_DIR))/lib/ClangEnzyme-*.$(SO_EXT)))
|
||||
ifeq ($(MAKECMDGOALS)-$(MFEM_USE_ENZYME),config-YES)
|
||||
ifeq ($(ENZYME_PLUGIN),)
|
||||
$(error Unable to find the Enzyme pluging! Please set ENZYME_DIR)
|
||||
endif
|
||||
ifneq ($(words $(ENZYME_PLUGIN)),1)
|
||||
$(error Multiple versions of the Enzyme pluging found! \
|
||||
Please set ENZYME_PLUGIN directly)
|
||||
endif
|
||||
endif
|
||||
ENZYME_OPT = -fplugin=$(ENZYME_PLUGIN)
|
||||
ENZYME_LIB =
|
||||
|
||||
# If YES, enable some informational messages
|
||||
VERBOSE = NO
|
||||
|
||||
@@ -951,6 +951,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/fem/ceed/integrators/nlconvection \
|
||||
@MFEM_SOURCE_DIR@/fem/ceed/interface \
|
||||
@MFEM_SOURCE_DIR@/fem/ceed/solvers \
|
||||
@MFEM_SOURCE_DIR@/fem/dfem \
|
||||
@MFEM_SOURCE_DIR@/fem/eltrans \
|
||||
@MFEM_SOURCE_DIR@/fem/fe \
|
||||
@MFEM_SOURCE_DIR@/fem/gslib \
|
||||
@@ -972,6 +973,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/adjoint \
|
||||
@MFEM_SOURCE_DIR@/miniapps/autodiff \
|
||||
@MFEM_SOURCE_DIR@/miniapps/common \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dfem \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
|
||||
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
|
||||
|
||||
@@ -64,6 +64,7 @@ set(SRCS
|
||||
datacollection.cpp
|
||||
dgmassinv.cpp
|
||||
doftrans.cpp
|
||||
dfem/doperator.cpp
|
||||
eltrans.cpp
|
||||
batchitrans.cpp
|
||||
estimators.cpp
|
||||
@@ -180,6 +181,14 @@ set(HDRS
|
||||
dgmassinv.hpp
|
||||
dgmassinv_kernels.hpp
|
||||
doftrans.hpp
|
||||
dfem/doperator.hpp
|
||||
dfem/fieldoperator.hpp
|
||||
dfem/integrate.hpp
|
||||
dfem/parameterspace.hpp
|
||||
dfem/qfunction_apply.hpp
|
||||
dfem/qfunction_transform.hpp
|
||||
dfem/tuple.hpp
|
||||
dfem/util.hpp
|
||||
eltrans.hpp
|
||||
estimators.hpp
|
||||
fe.hpp
|
||||
|
||||
+19
-33
@@ -466,7 +466,6 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * doftrans;
|
||||
Mesh *mesh = fes -> GetMesh();
|
||||
DenseMatrix elmat, *elmat_p;
|
||||
|
||||
@@ -503,13 +502,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
// Element-wise integration
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
// Set both doftrans (potentially needed to assemble the element
|
||||
// matrix) and vdofs, which is also needed when the element matrices
|
||||
// are pre-assembled.
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat_p = &(*element_matrices)(i);
|
||||
@@ -547,10 +547,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
}
|
||||
doftrans.TransformDual(elmat);
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
if (static_cond)
|
||||
@@ -628,13 +625,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
|
||||
fes -> GetBdrElementVDofs (i, vdofs, doftrans);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
int k = 0;
|
||||
for (; k < boundary_integs.Size(); k++)
|
||||
@@ -654,10 +652,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
}
|
||||
doftrans.TransformDual(elmat);
|
||||
elmat_p = &elmat;
|
||||
if (!static_cond)
|
||||
{
|
||||
@@ -1530,8 +1525,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
DenseMatrix elmat;
|
||||
|
||||
Mesh *mesh = test_fes -> GetMesh();
|
||||
@@ -1554,11 +1547,12 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < test_fes -> GetNE(); i++)
|
||||
{
|
||||
const int elem_attr = mesh->GetAttribute(i);
|
||||
dom_dof_trans = trial_fes -> GetElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetElementVDofs (i, test_vdofs);
|
||||
trial_fes->GetElementVDofs (i, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs (i, test_vdofs, ran_dof_trans);
|
||||
eltrans = test_fes -> GetElementTransformation (i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
@@ -1574,10 +1568,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
}
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -1605,13 +1596,14 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans, ran_dof_trans;
|
||||
for (int i = 0; i < test_fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
dom_dof_trans = trial_fes -> GetBdrElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetBdrElementVDofs (i, test_vdofs);
|
||||
trial_fes->GetBdrElementVDofs (i, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetBdrElementVDofs (i, test_vdofs, ran_dof_trans);
|
||||
eltrans = test_fes -> GetBdrElementTransformation (i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
@@ -1626,10 +1618,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
*eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
}
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -2407,8 +2396,6 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
DenseMatrix elmat;
|
||||
|
||||
Mesh *mesh = test_fes->GetMesh();
|
||||
@@ -2431,11 +2418,13 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation dom_dof_trans;
|
||||
DofTransformation ran_dof_trans;
|
||||
for (int i = 0; i < test_fes->GetNE(); i++)
|
||||
{
|
||||
const int elem_attr = mesh->GetAttribute(i);
|
||||
dom_dof_trans = trial_fes->GetElementVDofs(i, trial_vdofs);
|
||||
ran_dof_trans = test_fes->GetElementVDofs(i, test_vdofs);
|
||||
trial_fes->GetElementVDofs(i, trial_vdofs, dom_dof_trans);
|
||||
test_fes->GetElementVDofs(i, test_vdofs, ran_dof_trans);
|
||||
eltrans = test_fes->GetElementTransformation(i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
@@ -2451,10 +2440,7 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
}
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
mat->SetSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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 "doperator.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
|
||||
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
|
||||
{
|
||||
MFEM_ASSERT(parameters.size() == p.size(),
|
||||
"number of parameters doesn't match descriptors");
|
||||
for (size_t i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
p[i]->Read();
|
||||
parameters_l[i] = *p[i];
|
||||
}
|
||||
}
|
||||
|
||||
DifferentiableOperator::DifferentiableOperator(
|
||||
const std::vector<FieldDescriptor> &solutions,
|
||||
const std::vector<FieldDescriptor> ¶meters,
|
||||
const ParMesh &mesh) :
|
||||
mesh(mesh),
|
||||
solutions(solutions),
|
||||
parameters(parameters)
|
||||
{
|
||||
fields.resize(solutions.size() + parameters.size());
|
||||
fields_e.resize(fields.size());
|
||||
solutions_l.resize(solutions.size());
|
||||
parameters_l.resize(parameters.size());
|
||||
|
||||
for (size_t i = 0; i < solutions.size(); i++)
|
||||
{
|
||||
fields[i] = solutions[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < parameters.size(); i++)
|
||||
{
|
||||
fields[i + solutions.size()] = parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,819 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "../fespace.hpp"
|
||||
|
||||
#include "util.hpp"
|
||||
#include "interpolate.hpp"
|
||||
#include "integrate.hpp"
|
||||
#include "qfunction_apply.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Type alias for a function that computes the action of an operator
|
||||
using action_t =
|
||||
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that computes the action of a derivative
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that assembles the sparse matrix of a
|
||||
/// derivative operator
|
||||
using assemble_derivative_hypreparmatrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
|
||||
|
||||
/// @brief Type alias for a function that applies the appropriate restriction to
|
||||
/// the solution and parameters
|
||||
using restriction_callback_t =
|
||||
std::function<void(std::vector<Vector> &,
|
||||
const std::vector<Vector> &,
|
||||
std::vector<Vector> &)>;
|
||||
|
||||
/// Class representing the derivative (Jacobian) operator of a
|
||||
/// DifferentiableOperator.
|
||||
///
|
||||
/// This class implements a derivative operator that computes directional
|
||||
/// derivatives for a given set of solution and parameter fields. It supports
|
||||
/// both forward and transpose operations, as well as assembly into sparse
|
||||
/// matrices.
|
||||
///
|
||||
/// @note The derivative operator uses only forward mode differentiation in Mult
|
||||
/// and MultTranspose. It does not support reverse mode differentiation. The
|
||||
/// MultTranspose operation is achieved by using the transpose of the derivative
|
||||
/// actions on each quadrature point.
|
||||
///
|
||||
/// @see DifferentiableOperator
|
||||
class DerivativeOperator : public Operator
|
||||
{
|
||||
public:
|
||||
/// Constructor for the DerivativeOperator class.
|
||||
///
|
||||
/// This is usually not called directly from a user. A DifferentiableOperator
|
||||
/// calls this constructor when using
|
||||
/// DifferentiableOperator::GetDerivative().
|
||||
DerivativeOperator(
|
||||
const int &height,
|
||||
const int &width,
|
||||
const std::vector<derivative_action_t> &derivative_actions,
|
||||
const FieldDescriptor &direction,
|
||||
const int &daction_l_size,
|
||||
const std::vector<derivative_action_t> &derivative_actions_transpose,
|
||||
const FieldDescriptor &transpose_direction,
|
||||
const int &daction_transpose_l_size,
|
||||
const std::vector<Vector *> &solutions_l,
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
Operator(height, width),
|
||||
derivative_actions(derivative_actions),
|
||||
direction(direction),
|
||||
daction_l(daction_l_size),
|
||||
daction_l_size(daction_l_size),
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
transpose_direction(transpose_direction),
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
{
|
||||
s_l[i] = *solutions_l[i];
|
||||
}
|
||||
|
||||
std::vector<Vector> p_l(parameters_l.size());
|
||||
for (size_t i = 0; i < p_l.size(); i++)
|
||||
{
|
||||
p_l[i] = *parameters_l[i];
|
||||
}
|
||||
|
||||
fields_e.resize(solutions_l.size() + parameters_l.size());
|
||||
restriction_callback(s_l, p_l, fields_e);
|
||||
}
|
||||
|
||||
/// @brief Compute the action of the derivative operator on a given vector.
|
||||
///
|
||||
/// @param direction_t The direction vector in which to compute the
|
||||
/// derivative. This has to be a T-dof vector.
|
||||
/// @param result_t Result vector of the action of the derivative on
|
||||
/// direction_t on T-dofs.
|
||||
void Mult(const Vector &direction_t, Vector &result_t) const override
|
||||
{
|
||||
daction_l.SetSize(daction_l_size);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(direction, direction_t, direction_l);
|
||||
for (const auto &f : derivative_actions)
|
||||
{
|
||||
f(fields_e, direction_l, daction_l);
|
||||
}
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Compute the transpose of the derivative operator on a given
|
||||
/// vector.
|
||||
///
|
||||
/// This function computes the transpose of the derivative operator on a
|
||||
/// given vector by transposing the quadrature point local forward derivative
|
||||
/// action. It does not use reverse mode automatic differentiation.
|
||||
///
|
||||
/// @param direction_t The direction vector in which to compute the
|
||||
/// derivative. This has to be a T-dof vector.
|
||||
/// @param result_t Result vector of the transpose action of the derivative on
|
||||
/// direction_t on T-dofs.
|
||||
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
|
||||
{
|
||||
MFEM_ASSERT(!derivative_actions_transpose.empty(),
|
||||
"derivative can't be used to be multiplied in transpose mode");
|
||||
|
||||
daction_l.SetSize(width);
|
||||
daction_l = 0.0;
|
||||
|
||||
prolongation(transpose_direction, direction_t, direction_l);
|
||||
for (const auto &f : derivative_actions_transpose)
|
||||
{
|
||||
f(fields_e, direction_l, daction_l);
|
||||
}
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Assemble the derivative operator into a HypreParMatrix.
|
||||
///
|
||||
/// @param A The HypreParMatrix to assemble the derivative operator into. Can
|
||||
/// be an uninitialized object.
|
||||
void Assemble(HypreParMatrix &A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a matrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// Derivative action callbacks. Depending on the requested derivatives in
|
||||
/// DifferentiableOperator the callbacks represent certain combinations of
|
||||
/// actions of derivatives of the forward operator.
|
||||
std::vector<derivative_action_t> derivative_actions;
|
||||
|
||||
FieldDescriptor direction;
|
||||
|
||||
mutable Vector daction_l;
|
||||
|
||||
const int daction_l_size;
|
||||
|
||||
/// Transpose Derivative action callbacks. Depending on the requested
|
||||
/// derivatives in DifferentiableOperator the callbacks represent certain
|
||||
/// combinations of actions of derivatives of the forward operator.
|
||||
std::vector<derivative_action_t> derivative_actions_transpose;
|
||||
|
||||
FieldDescriptor transpose_direction;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
|
||||
mutable Vector direction_l;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
/// Callbacks that assemble derivatives into a HypreParMatrix.
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
};
|
||||
|
||||
/// Class representing a differentiable operator which acts on solution and
|
||||
/// parameter fields to compute residuals.
|
||||
///
|
||||
/// This class provides functionality to define differentiable operators by
|
||||
/// composing functions that compute values at quadrature points. It supports
|
||||
/// automatic differentiation to compute derivatives with respect to solutions
|
||||
/// (Jacobians) and parameter fields (general derivative operators).
|
||||
///
|
||||
/// The operator is constructed with solution fields that it will act on and
|
||||
/// parameter fields that define coefficients. Quadrature functions are added by
|
||||
/// e.g. using AddDomainIntegrator() which specify how the operator evaluates f
|
||||
/// those functionas and parameters at quadrature points.
|
||||
///
|
||||
/// Derivatives can be computed by obtaining a DerivativeOperator using
|
||||
/// GetDerivative().
|
||||
///
|
||||
/// @see DerivativeOperator
|
||||
class DifferentiableOperator : public Operator
|
||||
{
|
||||
public:
|
||||
/// Constructor for the DifferentiableOperator class.
|
||||
///
|
||||
/// @param solutions The solution fields that the operator will act on.
|
||||
/// @param parameters The parameter fields that define coefficients.
|
||||
/// @param mesh The mesh on which the operator is defined.
|
||||
DifferentiableOperator(
|
||||
const std::vector<FieldDescriptor> &solutions,
|
||||
const std::vector<FieldDescriptor> ¶meters,
|
||||
const ParMesh &mesh);
|
||||
|
||||
/// @brief Compute the action of the operator on a given vector.
|
||||
///
|
||||
/// @param solutions_t The solution vector in which to compute the action.
|
||||
/// This has to be a T-dof vector.
|
||||
/// @param result_t Result vector of the action of the operator on
|
||||
/// solutions_t. The result is a T-dof vector.
|
||||
void Mult(const Vector &solutions_t, Vector &result_t) const override
|
||||
{
|
||||
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
|
||||
prolongation(solutions, solutions_t, solutions_l);
|
||||
for (auto &action : action_callbacks)
|
||||
{
|
||||
action(solutions_l, parameters_l, residual_l);
|
||||
}
|
||||
prolongation_transpose(residual_l, result_t);
|
||||
}
|
||||
|
||||
/// @brief Add a domain integrator to the operator.
|
||||
///
|
||||
/// @param qfunc The quadrature function to be added.
|
||||
/// @param inputs Tuple of FieldOperators for the inputs of the quadrature
|
||||
/// function.
|
||||
/// @param outputs Tuple of FieldOperators for the outputs of the quadrature
|
||||
/// function.
|
||||
/// @param integration_rule IntegrationRule to use with this integrator.
|
||||
/// @param domain_attributes Domain attributes marker array indicating over
|
||||
/// which attributes this integrator will integrate over.
|
||||
/// @param derivative_ids Derivatives to be made available for this
|
||||
/// integrator.
|
||||
template <
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t = decltype(std::make_index_sequence<0> {})>
|
||||
void AddDomainIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &domain_attributes,
|
||||
derivative_ids_t derivative_ids = std::make_index_sequence<0> {});
|
||||
|
||||
/// @brief Set the parameters for the operator.
|
||||
///
|
||||
/// This has to be called before using Mult() or MultTranspose().
|
||||
///
|
||||
/// @param p The parameters to be set. This should be a vector of pointers to
|
||||
/// the parameter vectors. The vectors have to be L-vectors (e.g.
|
||||
/// GridFunctions).
|
||||
void SetParameters(std::vector<Vector *> p) const;
|
||||
|
||||
/// @brief Disable the use of tensor product structure.
|
||||
///
|
||||
/// This function disables the use of tensor product structure for the
|
||||
/// operator. Usually, DifferentiableOperator creates callbacks based on
|
||||
/// heuristics that achieve good performance for each element type. Some
|
||||
/// functionality is not implemented for these performant algorithms but only
|
||||
/// for generic assembly. Therefore the user can decide to use fallback
|
||||
/// methods.
|
||||
void DisableTensorProductStructure(bool disable = true)
|
||||
{
|
||||
use_tensor_product_structure = !disable;
|
||||
}
|
||||
|
||||
/// @brief Get the derivative operator for a given derivative ID.
|
||||
///
|
||||
/// This function returns a shared pointer to a DerivativeOperator that
|
||||
/// computes the derivative of the operator with respect to the given
|
||||
/// derivative ID. The derivative ID is used to identify the specific
|
||||
/// derivative action to be performed.
|
||||
///
|
||||
/// @param derivative_id The ID of the derivative to be computed.
|
||||
/// @param sol_l The solution vectors to be used for the derivative
|
||||
/// computation. This should be a vector of pointers to the solution
|
||||
/// vectors. The vectors have to be L-vectors (e.g. GridFunctions).
|
||||
/// @param par_l The parameter vectors to be used for the derivative
|
||||
/// computation. This should be a vector of pointers to the parameter
|
||||
/// vectors. The vectors have to be L-vectors (e.g. GridFunctions).
|
||||
/// @return A shared pointer to the DerivativeOperator.
|
||||
std::shared_ptr<DerivativeOperator> GetDerivative(
|
||||
size_t derivative_id, std::vector<Vector *> sol_l, std::vector<Vector *> par_l)
|
||||
{
|
||||
MFEM_ASSERT(derivative_action_callbacks.find(derivative_id) !=
|
||||
derivative_action_callbacks.end(),
|
||||
"no derivative action has been found for ID " << derivative_id);
|
||||
|
||||
MFEM_ASSERT(sol_l.size() == solutions.size(),
|
||||
"wrong number of solutions");
|
||||
|
||||
MFEM_ASSERT(par_l.size() == parameters.size(),
|
||||
"wrong number of parameters");
|
||||
|
||||
const size_t derivative_idx = FindIdx(derivative_id, fields);
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
height,
|
||||
GetTrueVSize(fields[derivative_idx]),
|
||||
derivative_action_callbacks[derivative_id],
|
||||
fields[derivative_idx],
|
||||
residual_l.Size(),
|
||||
daction_transpose_callbacks[derivative_id],
|
||||
fields[test_space_field_idx],
|
||||
GetVSize(fields[test_space_field_idx]),
|
||||
sol_l,
|
||||
par_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose,
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
}
|
||||
|
||||
private:
|
||||
const ParMesh &mesh;
|
||||
|
||||
std::vector<action_t> action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
|
||||
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
// solutions and parameters
|
||||
std::vector<FieldDescriptor> fields;
|
||||
|
||||
mutable std::vector<Vector> solutions_l;
|
||||
mutable std::vector<Vector> parameters_l;
|
||||
mutable Vector residual_l;
|
||||
|
||||
mutable std::vector<Vector> fields_e;
|
||||
mutable Vector residual_e;
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
// TODO: This can probably be removed, as it is only used in
|
||||
// the callbacks so they can copy it during the capture.
|
||||
std::function<void(Vector &, Vector &)> output_restriction_transpose;
|
||||
|
||||
restriction_callback_t restriction_callback;
|
||||
|
||||
std::map<size_t, std::function<void(const Vector &, Vector &)>>
|
||||
daction_prolongation_transpose;
|
||||
|
||||
std::map<size_t, size_t> assembled_vector_sizes;
|
||||
|
||||
bool use_tensor_product_structure = true;
|
||||
|
||||
size_t test_space_field_idx = SIZE_MAX;
|
||||
};
|
||||
|
||||
template <
|
||||
typename qfunc_t,
|
||||
typename input_t,
|
||||
typename output_t,
|
||||
typename derivative_ids_t>
|
||||
void DifferentiableOperator::AddDomainIntegrator(
|
||||
qfunc_t &qfunc,
|
||||
input_t inputs,
|
||||
output_t outputs,
|
||||
const IntegrationRule &integration_rule,
|
||||
const Array<int> &domain_attributes,
|
||||
derivative_ids_t derivative_ids)
|
||||
{
|
||||
using entity_t = Entity::Element;
|
||||
|
||||
static constexpr size_t num_inputs =
|
||||
tuple_size<decltype(inputs)>::value;
|
||||
|
||||
static constexpr size_t num_outputs =
|
||||
tuple_size<decltype(outputs)>::value;
|
||||
|
||||
using qf_signature =
|
||||
typename create_function_signature<decltype(&qfunc_t::operator())>::type;
|
||||
using qf_param_ts = typename qf_signature::parameter_ts;
|
||||
using qf_output_t = typename qf_signature::return_t;
|
||||
|
||||
// Consistency checks
|
||||
if constexpr (num_outputs > 1)
|
||||
{
|
||||
static_assert(dfem::always_false<qfunc_t>,
|
||||
"more than one output per quadrature functions is not supported right now");
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<qf_output_t, void>)
|
||||
{
|
||||
static_assert(dfem::always_false<qfunc_t>,
|
||||
"quadrature function has no return value");
|
||||
}
|
||||
|
||||
constexpr size_t num_qfinputs = tuple_size<qf_param_ts>::value;
|
||||
static_assert(num_qfinputs == num_inputs,
|
||||
"quadrature function inputs and descriptor inputs have to match");
|
||||
|
||||
constexpr size_t num_qf_outputs = tuple_size<qf_output_t>::value;
|
||||
static_assert(num_qf_outputs == num_outputs,
|
||||
"quadrature function outputs and descriptor outputs have to match");
|
||||
|
||||
constexpr auto inout_tuple =
|
||||
merge_mfem_tuples_as_empty_std_tuple(inputs, outputs);
|
||||
constexpr auto filtered_inout_tuple = filter_fields(inout_tuple);
|
||||
static constexpr size_t num_fields =
|
||||
count_unique_field_ids(filtered_inout_tuple);
|
||||
|
||||
MFEM_ASSERT(num_fields == solutions.size() + parameters.size(),
|
||||
"Total number of fields doesn't match sum of solutions and parameters."
|
||||
" This indicates that some fields are not used in the integrator,"
|
||||
" which currently is not supported.");
|
||||
|
||||
auto dependency_map = make_dependency_map(inputs);
|
||||
|
||||
// pretty_print(dependency_map);
|
||||
|
||||
auto input_to_field =
|
||||
create_descriptors_to_fields_map<entity_t>(fields, inputs);
|
||||
auto output_to_field =
|
||||
create_descriptors_to_fields_map<entity_t>(fields, outputs);
|
||||
|
||||
// TODO: factor out
|
||||
std::vector<int> inputs_vdim(num_inputs);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
inputs_vdim[i] = get<i>(inputs).vdim;
|
||||
});
|
||||
|
||||
|
||||
Array<int> elem_attributes;
|
||||
elem_attributes.SetSize(mesh.GetNE());
|
||||
for (int i = 0; i < mesh.GetNE(); ++i)
|
||||
{
|
||||
elem_attributes[i] = mesh.GetAttribute(i);
|
||||
}
|
||||
|
||||
const auto output_fop = get<0>(outputs);
|
||||
test_space_field_idx = FindIdx(output_fop.GetFieldId(), fields);
|
||||
|
||||
bool use_sum_factorization = false;
|
||||
auto entity_element_type =
|
||||
Element::TypeFromGeometry(mesh.GetTypicalElementGeometry());
|
||||
if ((entity_element_type == Element::QUADRILATERAL ||
|
||||
entity_element_type == Element::HEXAHEDRON) &&
|
||||
use_tensor_product_structure == true)
|
||||
{
|
||||
use_sum_factorization = true;
|
||||
}
|
||||
|
||||
ElementDofOrdering element_dof_ordering = ElementDofOrdering::NATIVE;
|
||||
DofToQuad::Mode doftoquad_mode = DofToQuad::Mode::FULL;
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
element_dof_ordering = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
doftoquad_mode = DofToQuad::Mode::TENSOR;
|
||||
}
|
||||
|
||||
auto [output_rt,
|
||||
output_e_sz] = get_restriction_transpose<entity_t>
|
||||
(fields[test_space_field_idx],
|
||||
element_dof_ordering, output_fop);
|
||||
auto &output_e_size = output_e_sz;
|
||||
|
||||
output_restriction_transpose = output_rt;
|
||||
residual_e.SetSize(output_e_size);
|
||||
|
||||
// The explicit captures are necessary to avoid dependency on
|
||||
// the specific instance of this class (this pointer).
|
||||
restriction_callback =
|
||||
[=, solutions = this->solutions, parameters = this->parameters]
|
||||
(std::vector<Vector> &sol,
|
||||
const std::vector<Vector> &par,
|
||||
std::vector<Vector> &f)
|
||||
{
|
||||
restriction<entity_t>(solutions, sol, f,
|
||||
element_dof_ordering);
|
||||
restriction<entity_t>(parameters, par, f,
|
||||
element_dof_ordering,
|
||||
solutions.size());
|
||||
};
|
||||
|
||||
prolongation_transpose = get_prolongation_transpose(
|
||||
fields[test_space_field_idx], output_fop, mesh.GetComm());
|
||||
|
||||
const int dimension = mesh.Dimension();
|
||||
[[maybe_unused]] const int num_elements = GetNumEntities<Entity::Element>(mesh);
|
||||
const int num_entities = GetNumEntities<entity_t>(mesh);
|
||||
const int num_qp = integration_rule.GetNPoints();
|
||||
|
||||
if constexpr (is_sum_fop<std::remove_cv_t<decltype(output_fop)>>::value)
|
||||
{
|
||||
residual_l.SetSize(1);
|
||||
height = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int residual_lsize = GetVSize(fields[test_space_field_idx]);
|
||||
residual_l.SetSize(residual_lsize);
|
||||
height = GetTrueVSize(fields[test_space_field_idx]);
|
||||
}
|
||||
|
||||
// TODO: Is this a hack?
|
||||
width = GetTrueVSize(fields[0]);
|
||||
|
||||
std::vector<const DofToQuad*> dtq;
|
||||
for (const auto &field : fields)
|
||||
{
|
||||
dtq.emplace_back(GetDofToQuad<entity_t>(
|
||||
field,
|
||||
integration_rule,
|
||||
doftoquad_mode));
|
||||
}
|
||||
const int q1d = (int)floor(std::pow(num_qp, 1.0/dimension) + 0.5);
|
||||
|
||||
const int residual_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop,
|
||||
fields[test_space_field_idx]);
|
||||
|
||||
auto input_dtq_maps = create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
|
||||
auto output_dtq_maps = create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
|
||||
|
||||
const int test_vdim = output_fop.vdim;
|
||||
const int test_op_dim = output_fop.size_on_qp / output_fop.vdim;
|
||||
|
||||
int num_test_dof = 0;
|
||||
if (num_entities)
|
||||
{
|
||||
if constexpr (is_sum_fop<std::decay_t<decltype(output_fop)>>::value)
|
||||
{
|
||||
num_test_dof = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
num_test_dof = output_e_size / output_fop.vdim / num_entities;
|
||||
}
|
||||
}
|
||||
|
||||
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
|
||||
|
||||
auto input_size_on_qp =
|
||||
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
|
||||
|
||||
auto action_shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
|
||||
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
|
||||
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
|
||||
|
||||
Vector shmem_cache(action_shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(action_shmem_info);
|
||||
|
||||
ThreadBlocks thread_blocks;
|
||||
if (dimension == 3)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
thread_blocks.x = q1d;
|
||||
thread_blocks.y = q1d;
|
||||
thread_blocks.z = q1d;
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
thread_blocks.x = q1d;
|
||||
thread_blocks.y = q1d;
|
||||
thread_blocks.z = 1;
|
||||
}
|
||||
}
|
||||
|
||||
action_callbacks.push_back(
|
||||
// Explicitly capture everything we need, so we can make explicit choice
|
||||
// how to capture every variable, by copy or by ref.
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
residual_size_on_qp, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
domain_attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
action_shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
// capture by ref:
|
||||
&restriction_cb = this->restriction_callback,
|
||||
&fields_e = this->fields_e,
|
||||
&residual_e = this->residual_e,
|
||||
&output_restriction_transpose = this->output_restriction_transpose
|
||||
]
|
||||
(std::vector<Vector> &sol, const std::vector<Vector> &par, Vector &res)
|
||||
mutable // mutable: needed to modify 'shmem_cache'
|
||||
{
|
||||
restriction_cb(sol, par, fields_e);
|
||||
|
||||
residual_e = 0.0;
|
||||
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
|
||||
|
||||
auto wrapped_fields_e = wrap_fields(fields_e,
|
||||
action_shmem_info.field_sizes,
|
||||
num_entities);
|
||||
|
||||
const bool has_attr = domain_attributes.Size() > 0;
|
||||
const auto d_domain_attr = domain_attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes.Read();
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
|
||||
residual_shmem, scratch_shmem] =
|
||||
unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, num_qp, e);
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
|
||||
call_qfunction<qf_param_ts>(
|
||||
qfunc, input_shmem, residual_shmem,
|
||||
residual_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
y, fhat, output_fop, output_dtq_shmem[0],
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, action_shmem_info.total_size, shmem_cache.ReadWrite());
|
||||
output_restriction_transpose(residual_e, res);
|
||||
});
|
||||
|
||||
// Without this compile-time check, some valid instantiations of this method
|
||||
// will fail.
|
||||
if constexpr (derivative_ids_t::size() != 0)
|
||||
{
|
||||
// Create the action of the derivatives
|
||||
for_constexpr([&,
|
||||
&or_transpose = this->output_restriction_transpose,
|
||||
&dapr_transpose = this->daction_prolongation_transpose]
|
||||
(const std::size_t derivative_id)
|
||||
{
|
||||
const size_t d_field_idx = FindIdx(derivative_id, fields);
|
||||
const auto direction = fields[d_field_idx];
|
||||
|
||||
dapr_transpose[derivative_id] = get_generic_prolongation_transpose(direction);
|
||||
|
||||
const int da_size_on_qp =
|
||||
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
|
||||
|
||||
auto shmem_info =
|
||||
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>(
|
||||
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
|
||||
num_qp, input_size_on_qp, residual_size_on_qp,
|
||||
element_dof_ordering, d_field_idx);
|
||||
|
||||
Vector shmem_cache(shmem_info.total_size);
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
Vector direction_e;
|
||||
Vector derivative_action_e(output_e_size);
|
||||
derivative_action_e = 0.0;
|
||||
|
||||
// Lookup the derivative_id key in the dependency map
|
||||
auto it = dependency_map.find(derivative_id);
|
||||
if (it == dependency_map.end())
|
||||
{
|
||||
MFEM_ABORT("Derivative ID not found in dependency map");
|
||||
}
|
||||
const auto input_is_dependent = it->second;
|
||||
|
||||
derivative_action_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
domain_attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent, // std::array<bool, num_inputs>
|
||||
direction, // FieldDescriptor
|
||||
direction_e, // Vector
|
||||
derivative_action_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
da_size_on_qp, // int
|
||||
|
||||
// capture by ref:
|
||||
&or_transpose
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_l,
|
||||
Vector &der_action_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, dir_l, direction_e,
|
||||
element_dof_ordering);
|
||||
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof,
|
||||
test_vdim, num_entities);
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
const auto d_elem_attr = elem_attributes.Read();
|
||||
const bool has_attr = domain_attributes.Size() > 0;
|
||||
const auto d_domain_attr = domain_attributes.Read();
|
||||
|
||||
derivative_action_e = 0.0;
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
|
||||
direction_shmem, input_shmem,
|
||||
shadow_shmem_, residual_shmem,
|
||||
scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field,
|
||||
inputs, ir_weights, scratch_shmem, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
// TODO: Probably redundant
|
||||
set_zero(shadow_shmem);
|
||||
|
||||
map_direction_to_quadrature_data_conditional(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, inputs,
|
||||
ir_weights, scratch_shmem, input_is_dependent, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
call_qfunction_derivative_action<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem,
|
||||
da_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim,
|
||||
test_op_dim, num_qp);
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
y, fhat, output_fop, output_dtq_shmem[0],
|
||||
scratch_shmem, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_info.total_size,
|
||||
shmem_cache.ReadWrite());
|
||||
or_transpose(derivative_action_e, der_action_l);
|
||||
});
|
||||
}, derivative_ids);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Base class for FieldOperators.
|
||||
///
|
||||
/// This class serves as a base for different FieldOperator types which can be
|
||||
/// applied to fields that are used with inputs to a quadrature point function.
|
||||
/// See DifferentialOperator.
|
||||
template <int FIELD_ID = -1>
|
||||
class FieldOperator
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for the FieldOperator.
|
||||
///
|
||||
/// This constructor initializes the FieldOperator with it's size on
|
||||
/// quadrature points. The size on quadrature points has to be determined by
|
||||
/// the FieldOperator type, the dimension and the vector dimension (number
|
||||
/// of components). See the following examples
|
||||
///
|
||||
/// Scalar FiniteElementSpace with Value FieldOperator:
|
||||
/// size = vdim x dim x 1 = 1 x dim x 1 = dim
|
||||
///
|
||||
/// Vector FiniteElementSpace with Gradient FieldOperator:
|
||||
/// size = vdim x dim x dim = vdim x dim x dim = vdim * dim^2
|
||||
///
|
||||
/// ParameterSpace with Identity FieldOperator:
|
||||
/// size = vdim = vdim
|
||||
constexpr FieldOperator(int size_on_qp = 0) :
|
||||
size_on_qp(size_on_qp) {};
|
||||
|
||||
/// @brief Get the field id this FieldOperator is attached to.
|
||||
static constexpr int GetFieldId() { return FIELD_ID; }
|
||||
|
||||
/// @brief Get the size on quadrature point for this FieldOperator.
|
||||
int size_on_qp = -1;
|
||||
|
||||
/// @brief Get the dimension of the FieldOperator.
|
||||
int dim = -1;
|
||||
|
||||
/// @brief Get the vector dimension (number of components)
|
||||
/// of the FieldOperator.
|
||||
int vdim = -1;
|
||||
};
|
||||
|
||||
/// @brief Identity FieldOperator.
|
||||
///
|
||||
/// This FieldOperator does nothing to the field. The field (usually a
|
||||
/// ParametricFunction) transfers the values to the quadrature point data and
|
||||
/// Identity can be viewed as an identity operation.
|
||||
template <int FIELD_ID = -1>
|
||||
class Identity : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Identity() : FieldOperator<FIELD_ID>() {}
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_identity_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_identity_fop<Identity<FIELD_ID>> : std::true_type {};
|
||||
|
||||
/// @brief Weight FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that this field contains the quadrature
|
||||
/// point weights.
|
||||
class Weight : public FieldOperator<-1>
|
||||
{
|
||||
public:
|
||||
constexpr Weight() : FieldOperator<-1>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_weight_fop : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_weight_fop<Weight> : std::true_type {};
|
||||
|
||||
/// @brief Value FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that the field contains the
|
||||
/// interpolated values of the degrees of freedom at the quadrature points.
|
||||
template <int FIELD_ID = -1>
|
||||
class Value : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Value() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_value_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_value_fop<Value<FIELD_ID>> : std::true_type {};
|
||||
|
||||
/// @brief Gradient FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is used to signal that the field contains the
|
||||
/// interpolated gradients of the degrees of freedom at the quadrature points.
|
||||
template <int FIELD_ID = -1>
|
||||
class Gradient : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Gradient() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_gradient_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_gradient_fop<Gradient<FIELD_ID>> : std::true_type {};
|
||||
|
||||
/// @brief Sum FieldOperator.
|
||||
///
|
||||
/// This FieldOperator is commonly used to signal that an output of a quadrature
|
||||
/// function should be summed.
|
||||
template <int FIELD_ID = -1>
|
||||
class Sum : public FieldOperator<FIELD_ID>
|
||||
{
|
||||
public:
|
||||
constexpr Sum() : FieldOperator<FIELD_ID>() {};
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_sum_fop : std::false_type {};
|
||||
|
||||
template <int FIELD_ID>
|
||||
struct is_sum_fop<Sum<FIELD_ID>> : std::true_type {};
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,472 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_impl(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
// assuming the quadrature point residual has to "play nice with
|
||||
// the test function"
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, cdim, num_dof] = B.GetShape();
|
||||
const int vdim = output.vdim > 0 ? output.vdim : cdim ;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * f(vd, 0, qp);
|
||||
}
|
||||
y(dof, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, dim, num_dof] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
acc += G(qp, d, dof) * f(vd, d, qp);
|
||||
}
|
||||
}
|
||||
y(dof, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sum_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
// This is the "integral over all quadrature points type" applying
|
||||
// B = 1 s.t. B^T * C \in R^1.
|
||||
const auto [num_qp, unused, unused1] = B.GetShape();
|
||||
auto cc = Reshape(&f(0, 0, 0), num_qp);
|
||||
for (int i = 0; i < num_qp; i++)
|
||||
{
|
||||
y(0, 0) += cc(i);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const auto vdim = output.vdim;
|
||||
auto cc = Reshape(&f(0, 0, 0), num_qp * vdim);
|
||||
auto yy = Reshape(&y(0, 0), num_qp * vdim);
|
||||
for (int i = 0; i < num_qp * vdim; i++)
|
||||
{
|
||||
yy(i) = cc(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qy, dx) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
yd(dx, dy, vd) += acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
uv[0] += fqp(vd, 0, qx, qy) * G(qx, 0, dx);
|
||||
uv[1] += fqp(vd, 1, qx, qy) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qy, dx) = uv[0];
|
||||
s1(qy, dx) = uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
uv[0] += s0(qy, dx) * B(qy, 0, dy);
|
||||
uv[1] += s1(qy, dx) * G(qy, 0, dy);
|
||||
}
|
||||
yd(dx, dy, vd) += uv[0] + uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
|
||||
// // TODO: Check if this is the right fix for all cases
|
||||
// auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d);
|
||||
// auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d);
|
||||
// for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
// {
|
||||
// MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
// {
|
||||
// yqp(sq, qx) = fqp(sq, qx);
|
||||
// }
|
||||
// MFEM_SYNC_THREAD;
|
||||
// }
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d);
|
||||
|
||||
for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
yqp(sq, qx, qy) = fqp(sq, qx, qy);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sum_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
auto fqp = Reshape(&f(0, 0, 0), q1d * q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp);
|
||||
for (int i = 0; i < q1d * q1d; i++)
|
||||
{
|
||||
yqp(0) += fqp(i);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qz, qy, dx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
acc += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
s1(qz, dy, dx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
acc += s1(qz, dy, dx) * B(qz, 0, dz);
|
||||
}
|
||||
yd(dx, dy, dz, vd) += acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = G.GetShape();
|
||||
const int vdim = output.vdim;
|
||||
const int test_dim = output.size_on_qp / vdim;
|
||||
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
|
||||
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), q1d, q1d, d1d);
|
||||
auto s2 = Reshape(&scratch_mem[2](0), q1d, q1d, d1d);
|
||||
auto s3 = Reshape(&scratch_mem[3](0), q1d, d1d, d1d);
|
||||
auto s4 = Reshape(&scratch_mem[4](0), q1d, d1d, d1d);
|
||||
auto s5 = Reshape(&scratch_mem[5](0), q1d, d1d, d1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qx = 0; qx < q1d; qx++)
|
||||
{
|
||||
uvw[0] += fqp(vd, 0, qx, qy, qz) * G(qx, 0, dx);
|
||||
uvw[1] += fqp(vd, 1, qx, qy, qz) * B(qx, 0, dx);
|
||||
uvw[2] += fqp(vd, 2, qx, qy, qz) * B(qx, 0, dx);
|
||||
}
|
||||
s0(qz, qy, dx) = uvw[0];
|
||||
s1(qz, qy, dx) = uvw[1];
|
||||
s2(qz, qy, dx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qy = 0; qy < q1d; qy++)
|
||||
{
|
||||
uvw[0] += s0(qz, qy, dx) * B(qy, 0, dy);
|
||||
uvw[1] += s1(qz, qy, dx) * G(qy, 0, dy);
|
||||
uvw[2] += s2(qz, qy, dx) * B(qy, 0, dy);
|
||||
}
|
||||
s3(qz, dy, dx) = uvw[0];
|
||||
s4(qz, dy, dx) = uvw[1];
|
||||
s5(qz, dy, dx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx, x, d1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int qz = 0; qz < q1d; qz++)
|
||||
{
|
||||
uvw[0] += s3(qz, dy, dx) * B(qz, 0, dz);
|
||||
uvw[1] += s4(qz, dy, dx) * B(qz, 0, dz);
|
||||
uvw[2] += s5(qz, dy, dx) * G(qz, 0, dz);
|
||||
}
|
||||
yd(dx, dy, dz, vd) += uvw[0] + uvw[1] + uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d, q1d);
|
||||
|
||||
for (int sq = 0; sq < output.size_on_qp; sq++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
yqp(sq, qx, qy, qz) = fqp(sq, qx, qy, qz);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (is_sum_fop<std::decay_t<output_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
auto fqp = Reshape(&f(0, 0, 0), q1d * q1d * q1d);
|
||||
auto yqp = Reshape(&y(0, 0), output.size_on_qp);
|
||||
for (int i = 0; i < q1d * q1d * q1d; i++)
|
||||
{
|
||||
yqp(0) += fqp(i);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename output_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_quadrature_data_to_fields(
|
||||
DeviceTensor<2, real_t> &y,
|
||||
const DeviceTensor<3, real_t> &f,
|
||||
const output_t &output,
|
||||
const DofToQuadMap &dtq,
|
||||
std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_2d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_quadrature_data_to_fields_tensor_impl_3d(y, f, output, dtq, scratch_mem);
|
||||
}
|
||||
else { MFEM_ABORT_KERNEL("dimension not supported"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
map_quadrature_data_to_fields_impl(y, f, output, dtq);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,573 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_field_to_quadrature_data_tensor_product_3d(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d, q1d);
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
|
||||
}
|
||||
s0(dz, dy, qx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dz, dy, qx) * B(qy, 0, dy);
|
||||
}
|
||||
s1(dz, qy, qx) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
acc += s1(dz, qy, qx) * B(qz, 0, dz);
|
||||
}
|
||||
fqp(vd, qx, qy, qz) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const int dim = input.dim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, d1d, q1d);
|
||||
auto s2 = Reshape(&scratch_mem[2](0), d1d, q1d, q1d);
|
||||
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
|
||||
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
const real_t f = field(dx, dy, dz, vd);
|
||||
uv[0] += f * B(qx, 0, dx);
|
||||
uv[1] += f * G(qx, 0, dx);
|
||||
}
|
||||
s0(dz, dy, qx) = uv[0];
|
||||
s1(dz, dy, qx) = uv[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(dz, z, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
const real_t s0i = s0(dz, dy, qx);
|
||||
uvw[0] += s1(dz, dy, qx) * B(qy, 0, dy);
|
||||
uvw[1] += s0i * G(qy, 0, dy);
|
||||
uvw[2] += s0i * B(qy, 0, dy);
|
||||
}
|
||||
s2(dz, qy, qx) = uvw[0];
|
||||
s3(dz, qy, qx) = uvw[1];
|
||||
s4(dz, qy, qx) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uvw[3] = {0.0, 0.0, 0.0};
|
||||
for (int dz = 0; dz < d1d; dz++)
|
||||
{
|
||||
uvw[0] += s2(dz, qy, qx) * B(qz, 0, dz);
|
||||
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
|
||||
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
|
||||
}
|
||||
fqp(vd, 0, qx, qy, qz) = uvw[0];
|
||||
fqp(vd, 1, qx, qy, qz) = uvw[1];
|
||||
fqp(vd, 2, qx, qy, qz) = uvw[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
// TODO: eeek
|
||||
const int q1d = (int)floor(std::pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
f(qx, qy, qz) = w(qx, qy, qz);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d * q1d);
|
||||
field_qp = field;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_field_to_quadrature_data_tensor_product_2d(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
|
||||
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d);
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
acc += B(qx, 0, dx) * field(dx, dy, vd);
|
||||
}
|
||||
s0(dy, qx) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
acc += s0(dy, qx) * B(qy, 0, dy);
|
||||
}
|
||||
fqp(vd, qx, qy) = acc;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
else if constexpr (
|
||||
is_gradient_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const auto [q1d, unused, d1d] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const int dim = input.dim;
|
||||
const auto field = Reshape(&field_e[0], d1d, d1d, vdim);
|
||||
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d);
|
||||
|
||||
auto s0 = Reshape(&scratch_mem[0](0), d1d, q1d);
|
||||
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, d1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dx = 0; dx < d1d; dx++)
|
||||
{
|
||||
const real_t f = field(dx, dy, vd);
|
||||
uv[0] += f * B(qx, 0, dx);
|
||||
uv[1] += f * G(qx, 0, dx);
|
||||
}
|
||||
s0(dy, qx) = uv[0];
|
||||
s1(dy, qx) = uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
real_t uv[2] = {0.0, 0.0};
|
||||
for (int dy = 0; dy < d1d; dy++)
|
||||
{
|
||||
const real_t s0i = s0(dy, qx);
|
||||
uv[0] += s1(dy, qx) * B(qy, 0, dy);
|
||||
uv[1] += s0i * G(qy, 0, dy);
|
||||
}
|
||||
fqp(vd, 0, qx, qy) = uv[0];
|
||||
fqp(vd, 1, qx, qy) = uv[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
// TODO: Create separate function for clarity
|
||||
else if constexpr (
|
||||
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
// TODO: eeek
|
||||
const int q1d = (int)floor(std::pow(num_qp, 1.0/input.dim) + 0.5);
|
||||
auto w = Reshape(&integration_weights[0], q1d, q1d);
|
||||
auto f = Reshape(&field_qp[0], q1d, q1d);
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
f(qx, qy) = w(qx, qy);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
|
||||
{
|
||||
const int q1d = B.GetShape()[0];
|
||||
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d);
|
||||
field_qp = field;
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
|
||||
"can't map field to quadrature data");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_field_to_quadrature_data(
|
||||
DeviceTensor<2> field_qp,
|
||||
const DofToQuadMap &dtq,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const field_operator_t &input,
|
||||
const DeviceTensor<1, const real_t> &integration_weights)
|
||||
{
|
||||
[[maybe_unused]] auto B = dtq.B;
|
||||
[[maybe_unused]] auto G = dtq.G;
|
||||
if constexpr (is_value_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, dim, num_dof] = B.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e(0), num_dof, vdim);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += B(qp, 0, dof) * field(dof, vd);
|
||||
}
|
||||
field_qp(vd, qp) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<field_operator_t>::value)
|
||||
{
|
||||
const auto [num_qp, dim, num_dof] = G.GetShape();
|
||||
const int vdim = input.vdim;
|
||||
const auto field = Reshape(&field_e(0), num_dof, vdim);
|
||||
|
||||
auto f = Reshape(&field_qp[0], vdim, dim, num_qp);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
real_t acc = 0.0;
|
||||
for (int dof = 0; dof < num_dof; dof++)
|
||||
{
|
||||
acc += G(qp, d, dof) * field(dof, vd);
|
||||
}
|
||||
f(vd, d, qp) = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<field_operator_t, Weight>)
|
||||
{
|
||||
const int num_qp = integration_weights.GetShape()[0];
|
||||
auto f = Reshape(&field_qp[0], num_qp);
|
||||
for (int qp = 0; qp < num_qp; qp++)
|
||||
{
|
||||
f(qp) = integration_weights(qp);
|
||||
}
|
||||
}
|
||||
else if constexpr (is_identity_fop<field_operator_t>::value)
|
||||
{
|
||||
auto [num_qp, unused, num_dof] = B.GetShape();
|
||||
const int size_on_qp = input.size_on_qp;
|
||||
const auto field = Reshape(&field_e[0], size_on_qp * num_qp);
|
||||
auto f = Reshape(&field_qp[0], size_on_qp * num_qp);
|
||||
for (int i = 0; i < size_on_qp * num_qp; i++)
|
||||
{
|
||||
f(i) = field(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(dfem::always_false<field_operator_t>,
|
||||
"can't map field to quadrature data");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void map_fields_to_quadrature_data(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<int, num_inputs> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
// When the input_to_field map returns -1, this means the requested input
|
||||
// is the integration weight. Weights don't have a user defined field
|
||||
// attached to them and we create a dummy field which is not accessed
|
||||
// inside the functions it is passed to.
|
||||
const auto dummy_field_weight = DeviceTensor<1>(nullptr, 0);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
const DeviceTensor<1> &field_e =
|
||||
(input_to_field[i] == -1) ? dummy_field_weight : fields_e[input_to_field[i]];
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("unsupported dimension");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename field_operator_t>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_field_to_quadrature_data_conditional(
|
||||
DeviceTensor<2> &field_qp,
|
||||
const DeviceTensor<1> &field_e,
|
||||
const DofToQuadMap &dtqmap,
|
||||
field_operator_t &fop,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const bool &condition,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
field_qp, dtqmap, field_e, fop, integration_weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t num_fields, size_t num_inputs, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_fields_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1, const real_t>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
map_field_to_quadrature_data_conditional(
|
||||
fields_qp[i], fields_e[i], dtqmaps[i], get<i>(fops), integration_weights,
|
||||
scratch_mem, conditions[i], use_sum_factorization);
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t num_inputs, typename field_operator_ts>
|
||||
MFEM_HOST_DEVICE
|
||||
void map_direction_to_quadrature_data_conditional(
|
||||
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
|
||||
const DeviceTensor<1> &direction_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
field_operator_ts fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
if (conditions[i])
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_2d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
map_field_to_quadrature_data_tensor_product_3d(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights, scratch_mem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
map_field_to_quadrature_data(
|
||||
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
|
||||
integration_weights);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "../fe/fe_base.hpp"
|
||||
#include "../../fem/fespace.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// Base class for parametric spaces
|
||||
class ParameterSpace
|
||||
{
|
||||
public:
|
||||
ParameterSpace(int vdim = 1) : vdim(vdim) {}
|
||||
|
||||
/// @brief Get vector dimension at each point
|
||||
///
|
||||
/// This is the number of components at each point in the parametric space.
|
||||
int GetVDim() const { return vdim; }
|
||||
|
||||
/// Get DofToQuad information
|
||||
const DofToQuad& GetDofToQuad() const { return dtq; }
|
||||
|
||||
/// Get total size of the space (T-vector size)
|
||||
///
|
||||
/// returns the true size vsize of the space
|
||||
virtual int GetTrueVSize() const = 0;
|
||||
|
||||
/// Get local vector size (L-vector size)
|
||||
///
|
||||
/// returns the local size of the space
|
||||
virtual int GetVSize() const = 0;
|
||||
|
||||
/// Get spatial dimension
|
||||
///
|
||||
/// returns always 1.
|
||||
int Dimension() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// @brief Get T-vector to L-vector transformation
|
||||
///
|
||||
/// returns identity by default that is lazy evaluated.
|
||||
virtual const Operator* GetProlongationMatrix() const
|
||||
{
|
||||
if (!prolongation)
|
||||
{
|
||||
prolongation.reset(new IdentityOperator(GetTrueVSize()));
|
||||
}
|
||||
return prolongation.get();
|
||||
}
|
||||
|
||||
/// @brief Get L-vector to E-vector transformation
|
||||
/// @note This is a mock call to replicate interface of FiniteElementSpace.
|
||||
/// It should not be used by a user.
|
||||
///
|
||||
/// returns identity by default that is lazy evaluated.
|
||||
virtual const Operator* GetElementRestriction(ElementDofOrdering o) const
|
||||
{
|
||||
if (!elem_restr)
|
||||
{
|
||||
elem_restr.reset(new IdentityOperator(GetVSize()));
|
||||
}
|
||||
return elem_restr.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
int vdim;
|
||||
DofToQuad dtq;
|
||||
mutable std::unique_ptr<Operator> prolongation;
|
||||
mutable std::unique_ptr<Operator> elem_restr;
|
||||
};
|
||||
|
||||
/// @brief Uniform parameter space
|
||||
class UniformParameterSpace : public ParameterSpace
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for a uniform parameter space
|
||||
///
|
||||
/// @param mesh The mesh to determine dimension and number of elements.
|
||||
/// @param ir The integration rule to determine the number of quadrature points.
|
||||
/// @param vdim The vector dimension at each point.
|
||||
/// @param used_in_tensor_product If true, the number of quadrature points is
|
||||
/// calculated as the nth root of the number of points in the integration rule,
|
||||
/// where n is the mesh dimension. If false, the number of quadrature points is
|
||||
/// taken directly from the integration rule.
|
||||
UniformParameterSpace(Mesh &mesh, const IntegrationRule &ir, int vdim,
|
||||
bool used_in_tensor_product = true) :
|
||||
ParameterSpace(vdim)
|
||||
{
|
||||
// Setup DofToQuad information
|
||||
dtq.nqpt = (int)floor(std::pow(ir.GetNPoints(), 1.0 / mesh.Dimension()) + 0.5);
|
||||
dtq.ndof = dtq.nqpt;
|
||||
|
||||
// Calculate sizes
|
||||
const int num_qp = used_in_tensor_product ?
|
||||
std::pow(dtq.nqpt, mesh.Dimension()) :
|
||||
ir.GetNPoints();
|
||||
|
||||
tsize = vdim * num_qp * mesh.GetNE();
|
||||
lsize = tsize;
|
||||
}
|
||||
|
||||
int GetTrueVSize() const override
|
||||
{
|
||||
return tsize;
|
||||
}
|
||||
|
||||
int GetVSize() const override
|
||||
{
|
||||
return lsize;
|
||||
}
|
||||
|
||||
private:
|
||||
/// T-vector size
|
||||
int tsize;
|
||||
|
||||
/// L-vector size
|
||||
int lsize;
|
||||
};
|
||||
|
||||
class ParameterFunction : public Vector
|
||||
{
|
||||
public:
|
||||
ParameterFunction(ParameterSpace &space) :
|
||||
Vector(space.GetTrueVSize()),
|
||||
space(space)
|
||||
{}
|
||||
|
||||
/// @brief Get the ParameterSpace
|
||||
const ParameterSpace& GetParameterSpace() const
|
||||
{
|
||||
return space;
|
||||
}
|
||||
|
||||
using Vector::operator=;
|
||||
|
||||
private:
|
||||
/// the parametric space
|
||||
ParameterSpace &space;
|
||||
};
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,298 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
#include "qfunction_transform.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Call a qfunction with the given parameters.
|
||||
///
|
||||
/// @param qfunc the qfunction to call.
|
||||
/// @param input_shmem the input shared memory.
|
||||
/// @param residual_shmem the residual shared memory.
|
||||
/// @param rs_qp the size of the residual.
|
||||
/// @param num_qp the number of quadrature points.
|
||||
/// @param q1d the number of quadrature points in 1D.
|
||||
/// @param dimension the spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
const int &rs_qp,
|
||||
const int &num_qp,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("unsupported dimension for sum factorization");
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
apply_kernel(r, qfunc, qf_args, input_shmem, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Call a qfunction with the given parameters and
|
||||
/// compute it's derivative action.
|
||||
///
|
||||
/// @param qfunc the qfunction to call.
|
||||
/// @param input_shmem the input shared memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param residual_shmem the residual shared memory.
|
||||
/// @param das_qp the size of the derivative action.
|
||||
/// @param num_qp the number of quadrature points.
|
||||
/// @param q1d the number of quadrature points in 1D.
|
||||
/// @param dimension the spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction_derivative_action(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
const int &das_qp,
|
||||
const int &num_qp,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename args_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel(
|
||||
DeviceTensor<1, real_t> &f_qp,
|
||||
const qfunc_t &qfunc,
|
||||
args_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
int qp)
|
||||
{
|
||||
process_qf_args(u, args, qp);
|
||||
process_qf_result(f_qp, get<0>(apply(qfunc, args)));
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename arg_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel_native_dual(
|
||||
DeviceTensor<1, real_t> &f_qp,
|
||||
const qfunc_t &qfunc,
|
||||
arg_ts &args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
const int &qp_idx)
|
||||
{
|
||||
process_qf_args(u, v, args, qp_idx);
|
||||
auto r = get<0>(apply(qfunc, args));
|
||||
process_derivative_from_native_dual(f_qp, r);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
|
||||
template <typename func_t, typename... arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto qfunction_wrapper(const func_t &f, arg_ts &&...args)
|
||||
{
|
||||
return f(args...);
|
||||
}
|
||||
|
||||
// Version for active function arguments only
|
||||
//
|
||||
// This is an Enzyme regression and can be removed in later versions.
|
||||
template <typename qfunc_t, typename arg_ts, std::size_t... Is,
|
||||
typename inactive_arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme_indexed(qfunc_t &qfunc, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
std::index_sequence<Is...>,
|
||||
inactive_arg_ts &&inactive_args,
|
||||
std::index_sequence<>)
|
||||
{
|
||||
using qf_return_t = typename create_function_signature<
|
||||
decltype(&qfunc_t::operator())>::type::return_t;
|
||||
return __enzyme_fwddiff<qf_return_t>(
|
||||
qfunction_wrapper<qfunc_t, decltype(get<Is>(args))...>, enzyme_const,
|
||||
(void *)&qfunc, enzyme_dup, &get<Is>(args)..., enzyme_interleave,
|
||||
&get<Is>(shadow_args)...);
|
||||
}
|
||||
|
||||
// Interleave function arguments for enzyme
|
||||
template <typename qfunc_t, typename arg_ts, std::size_t... Is,
|
||||
typename inactive_arg_ts, std::size_t... Js>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme_indexed(qfunc_t &qfunc, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
std::index_sequence<Is...>,
|
||||
inactive_arg_ts &&inactive_args,
|
||||
std::index_sequence<Js...>)
|
||||
{
|
||||
using qf_return_t = typename create_function_signature<
|
||||
decltype(&qfunc_t::operator())>::type::return_t;
|
||||
return __enzyme_fwddiff<qf_return_t>(
|
||||
qfunction_wrapper<qfunc_t, decltype(get<Is>(args))...,
|
||||
decltype(get<Js>(inactive_args))...>,
|
||||
enzyme_const, (void *)&qfunc, enzyme_dup, &get<Is>(args)...,
|
||||
enzyme_const, &get<Js>(inactive_args)..., enzyme_interleave,
|
||||
&get<Is>(shadow_args)...);
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename arg_ts, typename inactive_arg_ts>
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto fwddiff_apply_enzyme(qfunc_t &qfunc, arg_ts &&args,
|
||||
arg_ts &&shadow_args,
|
||||
inactive_arg_ts &&inactive_args)
|
||||
{
|
||||
auto arg_indices = std::make_index_sequence<
|
||||
tuple_size<std::remove_reference_t<arg_ts>>::value> {};
|
||||
|
||||
auto inactive_arg_indices = std::make_index_sequence<
|
||||
tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
|
||||
|
||||
return fwddiff_apply_enzyme_indexed(qfunc, args, shadow_args, arg_indices,
|
||||
inactive_args, inactive_arg_indices);
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename arg_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel_fwddiff_enzyme(
|
||||
DeviceTensor<1, real_t> &f_qp,
|
||||
qfunc_t &qfunc,
|
||||
arg_ts &args,
|
||||
arg_ts &shadow_args,
|
||||
const std::array<DeviceTensor<2>, num_args> &u,
|
||||
const std::array<DeviceTensor<2>, num_args> &v,
|
||||
int qp_idx)
|
||||
{
|
||||
process_qf_args(u, args, qp_idx);
|
||||
process_qf_args(v, shadow_args, qp_idx);
|
||||
process_qf_result(f_qp,
|
||||
get<0>(fwddiff_apply_enzyme(qfunc, args, shadow_args, tuple<> {})));
|
||||
}
|
||||
#endif // MFEM_USE_ENZYME
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,346 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
#include "../../linalg/tensor.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_qf_arg(const T0 &, const T1 &, T2 &)
|
||||
{
|
||||
static_assert(dfem::always_false<T0, T1, T2>,
|
||||
"process_qf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
tensor<dual<T, T>, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
dual<T, T> &arg)
|
||||
{
|
||||
arg.value = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
dual<T, T> &arg)
|
||||
{
|
||||
arg.value = u(0);
|
||||
arg.gradient = v(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
tensor<dual<T, T>, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i).value = u(i);
|
||||
arg(i).gradient = v(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
const DeviceTensor<1> &v,
|
||||
tensor<dual<T, T>, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i).value = u((i * m) + j);
|
||||
arg(j, i).gradient = v((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i).value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j).value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<2> &u,
|
||||
const DeviceTensor<2> &v,
|
||||
arg_type &arg,
|
||||
const int &qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
const auto v_qp = Reshape(&v(0, qp), v.GetShape()[0]);
|
||||
process_qf_arg(u_qp, v_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_fields, typename qf_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_args(
|
||||
const std::array<DeviceTensor<2>, num_fields> &u,
|
||||
const std::array<DeviceTensor<2>, num_fields> &v,
|
||||
qf_args &args,
|
||||
const int &qp)
|
||||
{
|
||||
for_constexpr<tuple_size<qf_args>::value>([&](auto i)
|
||||
{
|
||||
process_qf_arg(u[i], v[i], get<i>(args), qp);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j).gradient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<dual<T, T>, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i).gradient;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_derivative_from_native_dual(
|
||||
DeviceTensor<1, T> &r,
|
||||
const dual<T, T> &x)
|
||||
{
|
||||
r(0) = x.gradient;
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(const T0 &, T1 &)
|
||||
{
|
||||
static_assert(dfem::always_false<T0, T1>,
|
||||
"process_qf_arg not implemented for arg type");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
T &arg)
|
||||
{
|
||||
arg = u(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
tensor<T> &arg)
|
||||
{
|
||||
arg(0) = u(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
tensor<T, n> &arg)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
arg(i) = u(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1> &u,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename arg_type>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
|
||||
{
|
||||
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
|
||||
process_qf_arg(u_qp, arg);
|
||||
}
|
||||
|
||||
template <size_t num_fields, typename qf_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_args(
|
||||
const std::array<DeviceTensor<2>, num_fields> &u,
|
||||
qf_args &args,
|
||||
const int &qp)
|
||||
{
|
||||
for_constexpr<tuple_size<qf_args>::value>([&](auto i)
|
||||
{
|
||||
process_qf_arg(u[i], get<i>(args), qp);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T0, typename T1>
|
||||
MFEM_HOST_DEVICE inline
|
||||
Vector process_qf_result(T0, T1)
|
||||
{
|
||||
static_assert(dfem::always_false<T0, T1>,
|
||||
"process_qf_result not implemented for result type");
|
||||
return Vector{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const T &x)
|
||||
{
|
||||
r(0) = x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1> &r,
|
||||
const dual<T, T> &x)
|
||||
{
|
||||
r(0) = x.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<T> &x)
|
||||
{
|
||||
r(0) = x(0);
|
||||
}
|
||||
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<T, n> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
r(i) = x(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_result(
|
||||
DeviceTensor<1, T> &r,
|
||||
const tensor<T, n, m> &x)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
for (size_t j = 0; j < m; j++)
|
||||
{
|
||||
r(i + n * j) = x(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int n, int m>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void process_qf_arg(
|
||||
const DeviceTensor<1, T> &u,
|
||||
const DeviceTensor<1, T> &v,
|
||||
tensor<T, n, m> &arg)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
arg(j, i) = u((i * m) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
@@ -0,0 +1,885 @@
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
// This is serac's tuple implementation
|
||||
|
||||
#include <ostream>
|
||||
#include "../../config/config.hpp"
|
||||
#include <utility>
|
||||
|
||||
// Define a portable unreachable macro
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#if defined(__CUDACC_VER_MAJOR__)
|
||||
#if __CUDACC_VER_MAJOR__ <= 11 && __CUDACC_VER_MINOR__ < 3
|
||||
// nvcc didn't add __builtin_unreachable() until cuda 11.3
|
||||
#define MFEM_UNREACHABLE()
|
||||
#else
|
||||
// nvcc >= 11.3
|
||||
#define MFEM_UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
#else
|
||||
// host-only version
|
||||
#define MFEM_UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#define MFEM_UNREACHABLE() __assume(0)
|
||||
#endif
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief This is a class that mimics most of std::tuple's interface,
|
||||
* except that it is usable in CUDA kernels and admits some arithmetic operator overloads.
|
||||
*
|
||||
* see https://en.cppreference.com/w/cpp/utility/tuple for more information about std::tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
struct tuple
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
*/
|
||||
template <typename T0>
|
||||
struct tuple<T0>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1>
|
||||
struct tuple<T0, T1>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2>
|
||||
struct tuple<T0, T1, T2>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
struct tuple<T0, T1, T2, T3>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
struct tuple<T0, T1, T2, T3, T4>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
* @tparam T6 The seventh type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type that mimics std::tuple
|
||||
*
|
||||
* @tparam T0 The first type stored in the tuple
|
||||
* @tparam T1 The second type stored in the tuple
|
||||
* @tparam T2 The third type stored in the tuple
|
||||
* @tparam T3 The fourth type stored in the tuple
|
||||
* @tparam T4 The fifth type stored in the tuple
|
||||
* @tparam T5 The sixth type stored in the tuple
|
||||
* @tparam T6 The seventh type stored in the tuple
|
||||
* @tparam T7 The eighth type stored in the tuple
|
||||
*/
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
T7 v7; ///< The eighth member of the tuple
|
||||
};
|
||||
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
|
||||
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8>
|
||||
{
|
||||
T0 v0; ///< The first member of the tuple
|
||||
T1 v1; ///< The second member of the tuple
|
||||
T2 v2; ///< The third member of the tuple
|
||||
T3 v3; ///< The fourth member of the tuple
|
||||
T4 v4; ///< The fifth member of the tuple
|
||||
T5 v5; ///< The sixth member of the tuple
|
||||
T6 v6; ///< The seventh member of the tuple
|
||||
T7 v7; ///< The eighth member of the tuple
|
||||
T8 v8;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class template argument deduction rule for tuples
|
||||
* @tparam T The variadic template parameter for tuple types
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE
|
||||
tuple(T...) -> tuple<T...>;
|
||||
|
||||
/**
|
||||
* @brief helper function for combining a list of values into a tuple
|
||||
* @tparam T types of the values to be tuple-d
|
||||
* @param args the actual values to be put into a tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE tuple<T...> make_tuple(const T&... args)
|
||||
{
|
||||
return tuple<T...> {args...};
|
||||
}
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size
|
||||
{
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
struct tuple_size<tuple<Types...>> :
|
||||
std::integral_constant<std::size_t, sizeof...(Types)>
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @tparam i the tuple index to access
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief return a reference to the ith tuple entry
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
MFEM_UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam i the tuple index to access
|
||||
* @tparam T the types stored in the tuple
|
||||
* @brief return a copy of the ith tuple entry
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
MFEM_UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a function intended to be used for extracting the ith type from a tuple.
|
||||
*
|
||||
* @note type<i>(my_tuple) returns a value, whereas get<i>(my_tuple) returns a reference
|
||||
*
|
||||
* @tparam i the index of the tuple to query
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param values the tuple of values
|
||||
* @return a copy of the ith entry of the input
|
||||
*/
|
||||
template <int i, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
|
||||
{
|
||||
static_assert(i < sizeof...(T));
|
||||
if constexpr (i == 0)
|
||||
{
|
||||
return values.v0;
|
||||
}
|
||||
if constexpr (i == 1)
|
||||
{
|
||||
return values.v1;
|
||||
}
|
||||
if constexpr (i == 2)
|
||||
{
|
||||
return values.v2;
|
||||
}
|
||||
if constexpr (i == 3)
|
||||
{
|
||||
return values.v3;
|
||||
}
|
||||
if constexpr (i == 4)
|
||||
{
|
||||
return values.v4;
|
||||
}
|
||||
if constexpr (i == 5)
|
||||
{
|
||||
return values.v5;
|
||||
}
|
||||
if constexpr (i == 6)
|
||||
{
|
||||
return values.v6;
|
||||
}
|
||||
if constexpr (i == 7)
|
||||
{
|
||||
return values.v7;
|
||||
}
|
||||
if constexpr (i == 8)
|
||||
{
|
||||
return values.v8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the + operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple sum
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto plus_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) + get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise sum of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator+(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return plus_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the += operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @tparam i integer sequence used to index the tuples
|
||||
* @param x tuple of values to be incremented
|
||||
* @param y tuple of increment values
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr void plus_equals_helper(tuple<T...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
((get<i>(x) += get<i>(y)), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief add values contained in y, to the tuple x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
return plus_equals_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the -= operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @tparam i integer sequence used to index the tuples
|
||||
* @param x tuple of values to be subracted from
|
||||
* @param y tuple of values to subtract from x
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr void minus_equals_helper(tuple<T...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
((get<i>(x) -= get<i>(y)), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuples x and y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief add values contained in y, to the tuple x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-=(tuple<T...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
return minus_equals_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the - operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple difference
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto minus_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) - get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise difference of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return minus_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the - operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @return the returned tuple difference
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto unary_minus_helper(const tuple<T...>& x,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{-get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @brief return a tuple of values defined by applying the unary minus operator to each element of x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<T...>& x)
|
||||
{
|
||||
return unary_minus_helper(x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise division of x by y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return div_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a the constant numerator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a / get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the / operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a the constant denomenator
|
||||
* @return the returned tuple ratio
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
|
||||
const real_t a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) / a...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple x
|
||||
* @param a the numerator
|
||||
* @param x a tuple of denominator values
|
||||
* @brief return a tuple of values defined by division of a by the elements of x
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const real_t a, const tuple<T...>& x)
|
||||
{
|
||||
return div_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of numerator values
|
||||
* @param a a denominator
|
||||
* @brief return a tuple of values defined by elementwise division of x by a
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const real_t a)
|
||||
{
|
||||
return div_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param y tuple of values
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... S, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<S...>& x,
|
||||
const tuple<T...>& y,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * get<i>(y)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam S the types stored in the tuple x
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @param x a tuple of values
|
||||
* @param y a tuple of values
|
||||
* @brief return a tuple of values defined by elementwise multiplication of x and y
|
||||
*/
|
||||
template <typename... S, typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
|
||||
const tuple<T...>& y)
|
||||
{
|
||||
static_assert(sizeof...(S) == sizeof...(T));
|
||||
return mult_helper(x, y,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a a constant multiplier
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const real_t a,
|
||||
const tuple<T...>& x, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{a * get<i>(x)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper function for the * operator of tuples
|
||||
*
|
||||
* @tparam T the types stored in the tuple y
|
||||
* @tparam i The integer sequence to i
|
||||
* @param x tuple of values
|
||||
* @param a a constant multiplier
|
||||
* @return the returned tuple product
|
||||
*/
|
||||
template <typename... T, int... i>
|
||||
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
|
||||
const real_t a, std::integer_sequence<int, i...>)
|
||||
{
|
||||
return tuple{get<i>(x) * a...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param a a scaling factor
|
||||
* @param x the tuple object
|
||||
* @brief multiply each component of x by the value a on the left
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const real_t a, const tuple<T...>& x)
|
||||
{
|
||||
return mult_helper(a, x,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param x the tuple object
|
||||
* @param a a scaling factor
|
||||
* @brief multiply each component of x by the value a on the right
|
||||
*/
|
||||
template <typename... T>
|
||||
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const real_t a)
|
||||
{
|
||||
return mult_helper(x, a,
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @tparam i a list of indices used to acces each element of the tuple
|
||||
* @param out the ostream to write the output to
|
||||
* @param A the tuple of values
|
||||
* @brief helper used to implement printing a tuple of values
|
||||
*/
|
||||
template <typename... T, std::size_t... i>
|
||||
auto& print_helper(std::ostream& out, const tuple<T...>& A,
|
||||
std::integer_sequence<size_t, i...>)
|
||||
{
|
||||
out << "tuple{";
|
||||
(..., (out << (i == 0 ? "" : ", ") << get<i>(A)));
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam T the types stored in the tuple
|
||||
* @param out the ostream to write the output to
|
||||
* @param A the tuple of values
|
||||
* @brief print a tuple of values
|
||||
*/
|
||||
template <typename... T>
|
||||
auto& operator<<(std::ostream& out, const tuple<T...>& A)
|
||||
{
|
||||
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A helper to apply a lambda to a tuple
|
||||
*
|
||||
* @tparam lambda The functor type
|
||||
* @tparam T The tuple types
|
||||
* @tparam i The integer sequence to i
|
||||
* @param f The functor to apply to the tuple
|
||||
* @param args The input tuple
|
||||
* @return The functor output
|
||||
*/
|
||||
template <typename lambda, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE auto apply_helper(lambda f, tuple<T...>& args,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return f(get<i>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam lambda a callable type
|
||||
* @tparam T the types of arguments to be passed in to f
|
||||
* @param f the callable object
|
||||
* @param args a tuple of arguments
|
||||
* @brief a way of passing an n-tuple to a function that expects n separate arguments
|
||||
*
|
||||
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
|
||||
*/
|
||||
template <typename lambda, typename... T>
|
||||
MFEM_HOST_DEVICE auto apply(lambda f, tuple<T...>& args)
|
||||
{
|
||||
return apply_helper(f, std::move(args),
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
*/
|
||||
template <typename lambda, typename... T, int... i>
|
||||
MFEM_HOST_DEVICE auto apply_helper(lambda f, const tuple<T...>& args,
|
||||
std::integer_sequence<int, i...>)
|
||||
{
|
||||
return f(get<i>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @tparam lambda a callable type
|
||||
* @tparam T the types of arguments to be passed in to f
|
||||
* @param f the callable object
|
||||
* @param args a tuple of arguments
|
||||
* @brief a way of passing an n-tuple to a function that expects n separate arguments
|
||||
*
|
||||
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
|
||||
*/
|
||||
template <typename lambda, typename... T>
|
||||
MFEM_HOST_DEVICE auto apply(lambda f, const tuple<T...>& args)
|
||||
{
|
||||
return apply_helper(f, std::move(args),
|
||||
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief a struct used to determine the type at index I of a tuple
|
||||
*
|
||||
* @note see: https://en.cppreference.com/w/cpp/utility/tuple/tuple_element
|
||||
*
|
||||
* @tparam I the index of the desired type
|
||||
* @tparam T a tuple of different types
|
||||
*/
|
||||
template <size_t I, class T>
|
||||
struct tuple_element;
|
||||
|
||||
// recursive case
|
||||
/// @overload
|
||||
template <size_t I, class Head, class... Tail>
|
||||
struct tuple_element<I, tuple<Head, Tail...>> : tuple_element<I - 1,
|
||||
tuple<Tail...>>
|
||||
{
|
||||
};
|
||||
|
||||
// base case
|
||||
/// @overload
|
||||
template <class Head, class... Tail>
|
||||
struct tuple_element<0, tuple<Head, Tail...>>
|
||||
{
|
||||
using type = Head; ///< the type at the specified index
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type is a @p mfem::tuple
|
||||
*/
|
||||
template <typename T>
|
||||
struct is_tuple : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/// @overload
|
||||
template <typename... T>
|
||||
struct is_tuple<tuple<T...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
|
||||
*/
|
||||
template <typename T>
|
||||
struct is_tuple_of_tuples : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
|
||||
*/
|
||||
template <typename... T>
|
||||
struct is_tuple_of_tuples<tuple<T...>>
|
||||
{
|
||||
static constexpr bool value = (is_tuple<T>::value &&
|
||||
...); ///< true/false result of type check
|
||||
};
|
||||
|
||||
/** @brief Auxiliary template function that merges (concatenates) two
|
||||
mfem::future::tuple types into a single std::tuple that is empty, i.e. it is
|
||||
value initialized. */
|
||||
template <typename... T1s, typename... T2s>
|
||||
constexpr auto merge_mfem_tuples_as_empty_std_tuple(
|
||||
const mfem::future::tuple<T1s...> &,
|
||||
const mfem::future::tuple<T2s...> &)
|
||||
{
|
||||
return std::tuple<T1s..., T2s...> {};
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
+2298
File diff suppressed because it is too large
Load Diff
+16
-24
@@ -16,9 +16,7 @@ namespace mfem
|
||||
|
||||
void DofTransformation::TransformPrimal(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -48,9 +46,7 @@ void DofTransformation::TransformPrimal(real_t *v) const
|
||||
|
||||
void DofTransformation::InvTransformPrimal(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Height();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -80,9 +76,7 @@ void DofTransformation::InvTransformPrimal(real_t *v) const
|
||||
|
||||
void DofTransformation::TransformDual(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -112,9 +106,7 @@ void DofTransformation::TransformDual(real_t *v) const
|
||||
|
||||
void DofTransformation::InvTransformDual(real_t *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
if (IsIdentity()) { return; }
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
@@ -142,33 +134,33 @@ void DofTransformation::InvTransformDual(real_t *v) const
|
||||
}
|
||||
}
|
||||
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformPrimal(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// No action if both transformations are NULL
|
||||
if (ran_dof_trans)
|
||||
if (!ran_dof_trans.IsIdentity())
|
||||
{
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
ran_dof_trans.TransformPrimalCols(elmat);
|
||||
}
|
||||
if (dom_dof_trans)
|
||||
if (!dom_dof_trans.IsIdentity())
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
dom_dof_trans.TransformDualRows(elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformDual(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// No action if both transformations are NULL
|
||||
if (ran_dof_trans)
|
||||
if (!ran_dof_trans.IsIdentity())
|
||||
{
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
ran_dof_trans.TransformDualCols(elmat);
|
||||
}
|
||||
if (dom_dof_trans)
|
||||
if (!dom_dof_trans.IsIdentity())
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
dom_dof_trans.TransformDualRows(elmat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -201,19 +201,19 @@ public:
|
||||
inline int NumRows() const { return dof_trans_->NumRows(); }
|
||||
inline int Width() const { return dof_trans_->Width(); }
|
||||
inline int NumCols() const { return dof_trans_->NumCols(); }
|
||||
inline bool IsIdentity() const { return dof_trans_->IsIdentity(); }
|
||||
inline bool IsIdentity() const { return !dof_trans_ || dof_trans_->IsIdentity(); }
|
||||
|
||||
/** Transform local DoFs to align with the global DoFs. For example, this
|
||||
transformation can be used to map the local vector computed by
|
||||
FiniteElement::Project() to the transformed vector stored within a
|
||||
GridFunction object. */
|
||||
void TransformPrimal(real_t *v) const;
|
||||
inline void TransformPrimal(Vector &v) const
|
||||
{ TransformPrimal(v.GetData()); }
|
||||
inline void TransformPrimal(Vector &v) const { TransformPrimal(v.GetData()); }
|
||||
|
||||
/// Transform groups of DoFs stored as dense matrices
|
||||
inline void TransformPrimalCols(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformPrimal(V.GetColumn(c));
|
||||
@@ -251,6 +251,7 @@ public:
|
||||
/// Transform rows of a dense matrix containing dual DoFs
|
||||
inline void TransformDualRows(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
Vector row;
|
||||
for (int r=0; r<V.Height(); r++)
|
||||
{
|
||||
@@ -263,6 +264,7 @@ public:
|
||||
/// Transform columns of a dense matrix containing dual DoFs
|
||||
inline void TransformDualCols(DenseMatrix &V) const
|
||||
{
|
||||
if (IsIdentity()) { return; }
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformDual(V.GetColumn(c));
|
||||
@@ -274,16 +276,16 @@ public:
|
||||
computed by a DiscreteInterpolator before copying into a
|
||||
DiscreteLinearOperator.
|
||||
*/
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformPrimal(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Transform a matrix of dual DoFs entries from different finite element spaces
|
||||
as computed by a BilinearFormIntegrator before summing into a
|
||||
MixedBilinearForm object.
|
||||
*/
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
void TransformDual(const DofTransformation &ran_dof_trans,
|
||||
const DofTransformation &dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Abstract base class for high-order Nedelec spaces on elements with
|
||||
|
||||
+30
-24
@@ -1891,31 +1891,38 @@ L2Pos_PyramidElement::L2Pos_PyramidElement(const int p)
|
||||
|
||||
Index idx;
|
||||
|
||||
// interior
|
||||
for (int o = 0, k = 0; k <= p; k++)
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
int i1 = p - j - k;
|
||||
int i2 = 0;
|
||||
int i3 = -1;
|
||||
int i4 = j + 1;
|
||||
const int i5 = k;
|
||||
if (p == 0)
|
||||
{
|
||||
dof_map[idx(0,0,0,0,0)] = 0;
|
||||
Nodes.IntPoint(0).Set3(0.375, 0.375, 0.25);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int o = 0, k = 0; k <= p; k++)
|
||||
for (int j = 0; j + k <= p; j++)
|
||||
{
|
||||
int i1 = p - j - k;
|
||||
int i2 = 0;
|
||||
int i3 = -1;
|
||||
int i4 = j + 1;
|
||||
const int i5 = k;
|
||||
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
i3++;
|
||||
i4--;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
for (int i = j + 1; i + k <= p; i++)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
}
|
||||
for (int i = j + 1; i + k <= p; i++)
|
||||
{
|
||||
i1--;
|
||||
i2++;
|
||||
dof_map[idx(i1,i2,i3,i4,i5)] = o;
|
||||
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static method
|
||||
@@ -2197,7 +2204,6 @@ void L2Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
|
||||
{
|
||||
dshape(it.second, d) = m_dshape(it.first, d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
#include "dgmassinv.hpp"
|
||||
#include "hyperbolic.hpp"
|
||||
|
||||
#include "dfem/doperator.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
#include "pgridfunc.hpp"
|
||||
|
||||
+16
-14
@@ -331,7 +331,6 @@ void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -347,7 +346,6 @@ void FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -1936,6 +1934,7 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
|
||||
DenseMatrix eP;
|
||||
IsoparametricTransformation isotr;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int k = 0; k < mesh_ref->GetNE(); k++)
|
||||
{
|
||||
@@ -1956,10 +1955,10 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
|
||||
subY.SetSize(lP.Height());
|
||||
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, dofs);
|
||||
fespace->GetElementDofs(k, dofs, doftrans);
|
||||
old_elem_dof->GetRow(emb.parent, old_dofs);
|
||||
|
||||
if (!doftrans)
|
||||
if (doftrans.IsIdentity())
|
||||
{
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
@@ -1979,7 +1978,7 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans->SetVDim();
|
||||
doftrans.SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
@@ -1990,10 +1989,10 @@ void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
old_DoFTrans.InvTransformPrimal(subX);
|
||||
lP.Mult(subX, subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
doftrans.TransformPrimal(subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
}
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
doftrans.SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2020,6 +2019,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
DenseMatrix eP;
|
||||
IsoparametricTransformation isotr;
|
||||
const FiniteElement *fe = nullptr;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int k = 0; k < mesh_ref->GetNE(); k++)
|
||||
{
|
||||
@@ -2040,10 +2040,10 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
const DenseMatrix &lP = (fespace->IsVariableOrder()) ? eP : localP[geom](
|
||||
emb.matrix);
|
||||
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, f_dofs);
|
||||
fespace->GetElementDofs(k, f_dofs, doftrans);
|
||||
old_elem_dof->GetRow(emb.parent, c_dofs);
|
||||
|
||||
if (!doftrans)
|
||||
if (doftrans.IsIdentity())
|
||||
{
|
||||
subY.SetSize(lP.Width());
|
||||
|
||||
@@ -2074,7 +2074,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans->SetVDim();
|
||||
doftrans.SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
f_dofs.Copy(f_vdofs);
|
||||
@@ -2083,7 +2083,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
doftrans->InvTransformDual(subX);
|
||||
doftrans.InvTransformDual(subX);
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
@@ -2095,7 +2095,7 @@ void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
old_DoFTrans.TransformDual(subYt);
|
||||
y.AddElementVector(c_vdofs, subYt);
|
||||
}
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
doftrans.SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
@@ -3407,6 +3407,8 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(elem, dofs);
|
||||
@@ -3513,7 +3515,6 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation *FiniteElementSpace::GetElementDofs(int elem,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementDofs(elem, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
@@ -3523,6 +3524,8 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(bel, dofs);
|
||||
@@ -3617,7 +3620,6 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation *FiniteElementSpace::GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementDofs(bel, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
+42
-30
@@ -946,8 +946,8 @@ public:
|
||||
/// could be used to produce the appropriate offsets from these local dofs.
|
||||
///@{
|
||||
|
||||
/// @brief Returns indices of degrees of freedom of element 'elem'.
|
||||
/// The returned indices are offsets into an @ref ldof vector. See also
|
||||
/// @brief Returns indices of degrees of freedom of element 'elem'. The
|
||||
/// returned indices are offsets into an @ref ldof vector. See also
|
||||
/// GetElementVDofs().
|
||||
///
|
||||
/// @note In many cases the returned DofTransformation object will be NULL.
|
||||
@@ -957,15 +957,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetElementDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetElementDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
virtual void GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -980,15 +983,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetBdrElementDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetBdrElementDofs(int bel, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
virtual void GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -1192,15 +1198,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetElementVDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetElementVDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
void GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
@@ -1216,15 +1225,18 @@ public:
|
||||
/// needed for Nedelec basis functions of order 2 and above on 3D elements
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
/// @deprecated Use of the returned object is deprecated. The returned object
|
||||
/// should @b not be deleted by the caller. If the DofTransformation is
|
||||
/// needed, use GetBdrElementVDofs(int, Array<int> &, DofTransformation &)
|
||||
/// instead.
|
||||
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-provided
|
||||
/// DofTransformation object.
|
||||
///
|
||||
/// The user can use DofTransformation::IsIdentity on the returned @a
|
||||
/// doftrans object to determine if the DofTransformation needs to actually
|
||||
/// be used.
|
||||
void GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
|
||||
+102
-173
@@ -288,8 +288,6 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
GridFunction &u = *this;
|
||||
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation *udoftrans;
|
||||
DofTransformation *fdoftrans;
|
||||
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
@@ -302,6 +300,7 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
flux = 0.0;
|
||||
count = 0;
|
||||
|
||||
DofTransformation udoftrans, fdoftrans;
|
||||
for (int i = 0; i < nfe; i++)
|
||||
{
|
||||
if (subdomain >= 0 && ufes->GetAttribute(i) != subdomain)
|
||||
@@ -309,23 +308,17 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
continue;
|
||||
}
|
||||
|
||||
udoftrans = ufes->GetElementVDofs(i, udofs);
|
||||
fdoftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
ufes->GetElementVDofs(i, udofs, udoftrans);
|
||||
ffes->GetElementVDofs(i, fdofs, fdoftrans);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
if (udoftrans)
|
||||
{
|
||||
udoftrans->InvTransformPrimal(ul);
|
||||
}
|
||||
udoftrans.InvTransformPrimal(ul);
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
*ffes->GetFE(i), fl, wcoef);
|
||||
|
||||
if (fdoftrans)
|
||||
{
|
||||
fdoftrans->TransformPrimal(fl);
|
||||
}
|
||||
fdoftrans.TransformPrimal(fl);
|
||||
flux.AddElementVector(fdofs, fl);
|
||||
|
||||
FiniteElementSpace::AdjustVDofs(fdofs);
|
||||
@@ -353,12 +346,23 @@ void GridFunction::ComputeFlux(BilinearFormIntegrator &blfi,
|
||||
|
||||
int GridFunction::VectorDim() const
|
||||
{
|
||||
return fes->GetVectorDim();
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return fes->GetVDim();
|
||||
}
|
||||
return fes->GetVDim()*std::max(fes->GetMesh()->SpaceDimension(),
|
||||
fe->GetRangeDim());
|
||||
}
|
||||
|
||||
int GridFunction::CurlDim() const
|
||||
{
|
||||
return fes->GetCurlDim();
|
||||
const FiniteElement *fe = fes->GetTypicalFE();
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return 2 * fes->GetMesh()->SpaceDimension() - 3;
|
||||
}
|
||||
return fes->GetVDim()*fe->GetCurlDim();
|
||||
}
|
||||
|
||||
void GridFunction::GetTrueDofs(Vector &tv) const
|
||||
@@ -394,7 +398,8 @@ void GridFunction::GetNodalValues(int i, Array<real_t> &nval, int vdim) const
|
||||
{
|
||||
Array<int> vdofs;
|
||||
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
const IntegrationRule *ElemVert =
|
||||
Geometries.GetVertices(FElem->GetGeomType());
|
||||
@@ -404,10 +409,7 @@ void GridFunction::GetNodalValues(int i, Array<real_t> &nval, int vdim) const
|
||||
vdim--;
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
@@ -448,7 +450,8 @@ real_t GridFunction::GetValue(int i, const IntegrationPoint &ip, int vdim)
|
||||
const
|
||||
{
|
||||
Array<int> dofs;
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
Vector DofVal(dofs.Size()), LocVec;
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -463,10 +466,7 @@ const
|
||||
fe->CalcPhysShape(*Tr, DofVal);
|
||||
}
|
||||
GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
|
||||
return (DofVal * LocVec);
|
||||
}
|
||||
@@ -477,13 +477,11 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -517,22 +515,19 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
}
|
||||
|
||||
void GridFunction::GetValues(int i, const IntegrationRule &ir, Vector &vals,
|
||||
int vdim)
|
||||
const
|
||||
int vdim) const
|
||||
{
|
||||
Array<int> dofs;
|
||||
int n = ir.GetNPoints();
|
||||
vals.SetSize(n);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Vector DofVal(dof), loc_data(dof);
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (FElem->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
for (int k = 0; k < n; k++)
|
||||
@@ -865,12 +860,12 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
|
||||
Array<int> vdofs;
|
||||
const FiniteElement *fe = NULL;
|
||||
DofTransformation * doftrans = NULL;
|
||||
DofTransformation doftrans;
|
||||
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs, doftrans);
|
||||
fe = fes->GetFE(T.ElementNo);
|
||||
break;
|
||||
case ElementTransformation::EDGE:
|
||||
@@ -960,10 +955,7 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
int dof = fe->GetDof();
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -1007,13 +999,11 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
|
||||
int dof = FElem->GetDof();
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs, doftrans);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
|
||||
int nip = ir.GetNPoints();
|
||||
|
||||
@@ -1104,23 +1094,19 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
DofTransformation * doftrans;
|
||||
DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, orig_loc_values;
|
||||
int i, j, d, ne, dof, odof, vdim;
|
||||
|
||||
ne = fes->GetNE();
|
||||
vdim = fes->GetVDim();
|
||||
DofTransformation doftrans, orig_doftrans;
|
||||
for (i = 0; i < ne; i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
orig_doftrans = orig_fes->GetElementVDofs(i, orig_vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
orig_fes->GetElementVDofs(i, orig_vdofs, orig_doftrans);
|
||||
orig_func.GetSubVector(orig_vdofs, orig_loc_values);
|
||||
if (orig_doftrans)
|
||||
{
|
||||
orig_doftrans->InvTransformPrimal(orig_loc_values);
|
||||
}
|
||||
orig_doftrans.InvTransformPrimal(orig_loc_values);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const FiniteElement *orig_fe = orig_fes->GetFE(i);
|
||||
dof = fe->GetDof();
|
||||
@@ -1137,10 +1123,7 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
loc_values(d*dof+j) = shape * (&orig_loc_values[d * odof]);
|
||||
}
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(loc_values);
|
||||
}
|
||||
doftrans.TransformPrimal(loc_values);
|
||||
SetSubVector(vdofs, loc_values);
|
||||
}
|
||||
}
|
||||
@@ -1150,8 +1133,6 @@ void GridFunction::GetBdrValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
// DofTransformation * doftrans;
|
||||
// DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, loc_values_t, orig_loc_values, orig_loc_values_t;
|
||||
int i, j, d, nbe, dof, odof, vdim;
|
||||
@@ -1191,7 +1172,8 @@ void GridFunction::GetVectorFieldValues(
|
||||
ElementTransformation *transf;
|
||||
|
||||
const int n = ir.GetNPoints();
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const int dof = fe->GetDof();
|
||||
const int sdim = fes->GetMesh()->SpaceDimension();
|
||||
@@ -1203,10 +1185,7 @@ void GridFunction::GetVectorFieldValues(
|
||||
DenseMatrix vshape(dof, vdim);
|
||||
Vector loc_data, val(vdim);
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(k);
|
||||
@@ -1397,6 +1376,7 @@ void GridFunction::GetVectorGradientHat(
|
||||
|
||||
real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
@@ -1424,13 +1404,10 @@ real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
// Assuming RT-type space
|
||||
Array<int> dofs;
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
fes->GetElementDofs(elNo, dofs, doftrans);
|
||||
Vector loc_data, divshape(fe->GetDof());
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
fe->CalcDivShape(T.GetIntPoint(), divshape);
|
||||
return (loc_data * divshape) / T.Weight();
|
||||
}
|
||||
@@ -1483,6 +1460,7 @@ real_t GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
|
||||
void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
@@ -1517,13 +1495,10 @@ void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
// Assuming ND-type space
|
||||
Array<int> dofs;
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
fes->GetElementDofs(elNo, dofs, doftrans);
|
||||
Vector loc_data;
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
DenseMatrix curl_shape(fe->GetDof(), fe->GetCurlDim());
|
||||
curl.SetSize(curl_shape.Width());
|
||||
fe->CalcPhysCurlShape(T, curl_shape);
|
||||
@@ -1724,11 +1699,10 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
MassIntegrator Mi;
|
||||
DenseMatrix loc_mass;
|
||||
DofTransformation * te_doftrans;
|
||||
DofTransformation * tr_doftrans;
|
||||
Array<int> te_dofs, tr_dofs;
|
||||
Vector loc_avgs, loc_this;
|
||||
Vector int_psi(avgs.Size());
|
||||
DofTransformation tr_doftrans, te_doftrans;
|
||||
|
||||
avgs = 0.0;
|
||||
int_psi = 0.0;
|
||||
@@ -1736,19 +1710,13 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
Mi.AssembleElementMatrix2(*fes->GetFE(i), *avgs.FESpace()->GetFE(i),
|
||||
*fes->GetElementTransformation(i), loc_mass);
|
||||
tr_doftrans = fes->GetElementDofs(i, tr_dofs);
|
||||
te_doftrans = avgs.FESpace()->GetElementDofs(i, te_dofs);
|
||||
fes->GetElementDofs(i, tr_dofs, tr_doftrans);
|
||||
avgs.FESpace()->GetElementDofs(i, te_dofs, te_doftrans);
|
||||
GetSubVector(tr_dofs, loc_this);
|
||||
if (tr_doftrans)
|
||||
{
|
||||
tr_doftrans->InvTransformPrimal(loc_this);
|
||||
}
|
||||
tr_doftrans.InvTransformPrimal(loc_this);
|
||||
loc_avgs.SetSize(te_dofs.Size());
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
if (te_doftrans)
|
||||
{
|
||||
te_doftrans->TransformPrimal(loc_avgs);
|
||||
}
|
||||
te_doftrans.TransformPrimal(loc_avgs);
|
||||
avgs.AddElementVector(te_dofs, loc_avgs);
|
||||
loc_this = 1.0; // assume the local basis for 'this' sums to 1
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
@@ -1763,12 +1731,10 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
Array<int> dof_idx;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(el, dof_idx);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(el, dof_idx, doftrans);
|
||||
GetSubVector(dof_idx, dof_vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(dof_vals);
|
||||
}
|
||||
doftrans.InvTransformPrimal(dof_vals);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
@@ -1793,6 +1759,7 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
Array<int> src_vdofs, dest_vdofs;
|
||||
Vector src_lvec, dest_lvec(vdim*P.Height());
|
||||
|
||||
DofTransformation src_doftrans, doftrans;
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
// Assuming the projection matrix P depends only on the element geometry
|
||||
@@ -1804,21 +1771,15 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
cached_geom = geom;
|
||||
}
|
||||
|
||||
DofTransformation * src_doftrans = src.fes->GetElementVDofs(i, src_vdofs);
|
||||
src.fes->GetElementVDofs(i, src_vdofs, src_doftrans);
|
||||
src.GetSubVector(src_vdofs, src_lvec);
|
||||
if (src_doftrans)
|
||||
{
|
||||
src_doftrans->InvTransformPrimal(src_lvec);
|
||||
}
|
||||
src_doftrans.InvTransformPrimal(src_lvec);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
P.Mult(&src_lvec[vd*P.Width()], &dest_lvec[vd*P.Height()]);
|
||||
}
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, dest_vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(dest_lvec);
|
||||
}
|
||||
fes->GetElementVDofs(i, dest_vdofs, doftrans);
|
||||
doftrans.TransformPrimal(dest_lvec);
|
||||
SetSubVector(dest_vdofs, dest_lvec);
|
||||
}
|
||||
}
|
||||
@@ -1827,15 +1788,13 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
const Vector &lo_, const Vector &hi_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
|
||||
GetSubVector(vdofs, vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
doftrans.InvTransformPrimal(vals);
|
||||
|
||||
MFEM_ASSERT(weights.Size() == size, "Different # of weights and dofs.");
|
||||
MFEM_ASSERT(lo_.Size() == size, "Different # of lower bounds and dofs.");
|
||||
@@ -1852,10 +1811,7 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
slbqp.SetPrintLevel(0); // print messages only if not converged
|
||||
slbqp.Mult(vals, new_vals);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
doftrans.TransformPrimal(new_vals);
|
||||
SetSubVector(vdofs, new_vals);
|
||||
}
|
||||
|
||||
@@ -1863,14 +1819,12 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
real_t min_, real_t max_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation doftrans;
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
GetSubVector(vdofs, vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
doftrans.InvTransformPrimal(vals);
|
||||
|
||||
real_t max_val = vals.Max();
|
||||
real_t min_val = vals.Min();
|
||||
@@ -1878,10 +1832,7 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
if (max_val <= min_)
|
||||
{
|
||||
new_vals = min_;
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
doftrans.TransformPrimal(new_vals);
|
||||
SetSubVector(vdofs, new_vals);
|
||||
return;
|
||||
}
|
||||
@@ -1913,7 +1864,6 @@ void GridFunction::RestrictConforming()
|
||||
|
||||
void GridFunction::GetNodalValues(Vector &nval, int vdim) const
|
||||
{
|
||||
int i, j;
|
||||
Array<int> vertices;
|
||||
Array<real_t> values;
|
||||
Array<int> overlap(fes->GetNV());
|
||||
@@ -1921,17 +1871,17 @@ void GridFunction::GetNodalValues(Vector &nval, int vdim) const
|
||||
nval = 0.0;
|
||||
overlap = 0;
|
||||
nval.HostReadWrite();
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVertices(i, vertices);
|
||||
GetNodalValues(i, values, vdim);
|
||||
for (j = 0; j < vertices.Size(); j++)
|
||||
for (int j = 0; j < vertices.Size(); j++)
|
||||
{
|
||||
nval(vertices[j]) += values[j];
|
||||
overlap[vertices[j]]++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < overlap.Size(); i++)
|
||||
for (int i = 0; i < overlap.Size(); i++)
|
||||
{
|
||||
nval(i) /= overlap[i];
|
||||
}
|
||||
@@ -2211,6 +2161,7 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
ElementTransformation *T;
|
||||
Array<int> dofs;
|
||||
Vector lvec;
|
||||
DofTransformation dof_tr;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
@@ -2225,10 +2176,10 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
}
|
||||
fe = fes->GetBE(i);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
DofTransformation *dof_tr = fes->GetBdrElementDofs(i, dofs);
|
||||
fes->GetBdrElementDofs(i, dofs, dof_tr);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
if (dof_tr) { dof_tr->TransformPrimal(lvec); }
|
||||
dof_tr.TransformPrimal(lvec);
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
|
||||
@@ -2333,6 +2284,8 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
DenseMatrix loc_mass;
|
||||
Array<int> vdofs, vertices;
|
||||
Vector vals, loc_mass_vals;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
mesh->GetElementVertices(i, vertices);
|
||||
@@ -2344,11 +2297,8 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
loc_mass);
|
||||
vals.SetSize(fe->GetDof());
|
||||
fe->ProjectDelta(j, vals);
|
||||
const DofTransformation* const doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
loc_mass_vals.SetSize(vals.Size());
|
||||
loc_mass.Mult(vals, loc_mass_vals);
|
||||
@@ -2361,7 +2311,7 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation * doftrans = NULL;
|
||||
DofTransformation doftrans;
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
@@ -2372,13 +2322,10 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2445,23 +2392,19 @@ void GridFunction::ProjectCoefficient(
|
||||
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2528,8 +2471,7 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
@@ -2538,13 +2480,10 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
|
||||
continue;
|
||||
}
|
||||
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2555,7 +2494,6 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
|
||||
real_t val;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *transf;
|
||||
// DofTransformation * doftrans;
|
||||
Array<int> vdofs;
|
||||
|
||||
vdim = fes->GetVDim();
|
||||
@@ -2738,6 +2676,7 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
Array<int> dofs;
|
||||
int dim = vcoeff.GetVDim();
|
||||
Vector vc(dim), nor(dim), lvec;
|
||||
DofTransformation doftrans;
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
@@ -2757,11 +2696,8 @@ void GridFunction::ProjectBdrCoefficientNormal(
|
||||
CalcOrtho(T->Jacobian(), nor);
|
||||
lvec(j) = (vc * nor);
|
||||
}
|
||||
const DofTransformation* const doftrans = fes->GetBdrElementDofs(i, dofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(lvec);
|
||||
}
|
||||
fes->GetBdrElementDofs(i, dofs, doftrans);
|
||||
doftrans.TransformPrimal(lvec);
|
||||
SetSubVector(dofs, lvec);
|
||||
}
|
||||
#endif
|
||||
@@ -4059,6 +3995,7 @@ real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation utrans, ftrans;
|
||||
|
||||
int dim = ufes->GetMesh()->Dimension();
|
||||
int nfe = ufes->GetNE();
|
||||
@@ -4090,19 +4027,13 @@ real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
{
|
||||
if (with_subdomains && ufes->GetAttribute(i) != s) { continue; }
|
||||
|
||||
const DofTransformation* const utrans = ufes->GetElementVDofs(i, udofs);
|
||||
const DofTransformation* const ftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
ufes->GetElementVDofs(i, udofs, utrans);
|
||||
ffes->GetElementVDofs(i, fdofs, ftrans);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
flux.GetSubVector(fdofs, fla);
|
||||
if (utrans)
|
||||
{
|
||||
utrans->InvTransformPrimal(ul);
|
||||
}
|
||||
if (ftrans)
|
||||
{
|
||||
ftrans->InvTransformPrimal(fla);
|
||||
}
|
||||
utrans.InvTransformPrimal(ul);
|
||||
ftrans.InvTransformPrimal(fla);
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
@@ -4317,6 +4248,7 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
MFEM_VERIFY(tichonov_coeff >= 0.0, "tichonov_coeff cannot be negative");
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation utrans;
|
||||
|
||||
Mesh *mesh = ufes->GetMesh();
|
||||
int dim = mesh->Dimension();
|
||||
@@ -4398,12 +4330,9 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
|
||||
flux_order));
|
||||
int num_integration_pts = ir->GetNPoints();
|
||||
|
||||
const DofTransformation* const utrans = ufes->GetElementVDofs(ielem, udofs);
|
||||
ufes->GetElementVDofs(ielem, udofs, utrans);
|
||||
u.GetSubVector(udofs, ul);
|
||||
if (utrans)
|
||||
{
|
||||
utrans->InvTransformPrimal(ul);
|
||||
}
|
||||
utrans.InvTransformPrimal(ul);
|
||||
Transf = ufes->GetElementTransformation(ielem);
|
||||
FiniteElement *dummy = nullptr;
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
|
||||
|
||||
@@ -200,7 +200,7 @@ void PADiffusionSetup2D<3>(const int Q1D,
|
||||
const real_t E = J11*J11 + J21*J21 + J31*J31;
|
||||
const real_t G = J12*J12 + J22*J22 + J32*J32;
|
||||
const real_t F = J11*J12 + J21*J22 + J31*J32;
|
||||
const real_t iw = 1.0 / sqrt(E*G - F*F);
|
||||
const real_t iw = 1.0 / std::sqrt(E*G - F*F);
|
||||
const real_t coeff = const_c ? C(0,0,0) : C(qx,qy,e);
|
||||
const real_t alpha = wq * coeff * iw;
|
||||
D(qx,qy,0,e) = alpha * G; // 1,1
|
||||
|
||||
@@ -147,6 +147,11 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
const GeometricFactors &geom, const DofToQuad &maps, const Vector &x,
|
||||
QuadratureFunction &QVec, Vector &y)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
|
||||
static_assert((i_block < 0) == (j_block < 0),
|
||||
"i_block and j_block must both be non-negative or strictly negative.");
|
||||
static constexpr int d = dim;
|
||||
@@ -277,6 +282,11 @@ void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
const CoefficientVector &mu, const GeometricFactors &geom,
|
||||
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
|
||||
// Assuming all elements are the same
|
||||
const auto &ir = QVec.GetIntRule(0);
|
||||
static constexpr int d = dim;
|
||||
@@ -361,6 +371,11 @@ void ElasticityAssembleEA_(const int i_block,
|
||||
const DofToQuad &maps,
|
||||
Vector &emat)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
|
||||
// Assuming all elements are the same
|
||||
static constexpr int d = dim;
|
||||
const int numPoints = ir.GetNPoints();
|
||||
|
||||
+6
-12
@@ -173,7 +173,6 @@ void LinearForm::Assemble()
|
||||
{
|
||||
Array<int> vdofs;
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation *doftrans;
|
||||
Vector elemvect;
|
||||
|
||||
Vector::operator=(0.0);
|
||||
@@ -198,6 +197,7 @@ void LinearForm::Assemble()
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
@@ -207,14 +207,11 @@ void LinearForm::Assemble()
|
||||
if (markers) { markers->HostRead(); }
|
||||
if ( markers == NULL || (*markers)[elem_attr-1] == 1 )
|
||||
{
|
||||
doftrans = fes -> GetElementVDofs (i, vdofs);
|
||||
fes -> GetElementVDofs (i, vdofs, doftrans);
|
||||
eltrans = fes -> GetElementTransformation (i);
|
||||
domain_integs[k]->AssembleRHSElementVect(*fes->GetFE(i),
|
||||
*eltrans, elemvect);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elemvect);
|
||||
}
|
||||
doftrans.TransformDual(elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
@@ -247,11 +244,12 @@ void LinearForm::Assemble()
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes -> GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
|
||||
fes -> GetBdrElementVDofs (i, vdofs, doftrans);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
for (int k=0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
@@ -260,11 +258,7 @@ void LinearForm::Assemble()
|
||||
|
||||
boundary_integs[k]->AssembleRHSElementVect(*fes->GetBE(i),
|
||||
*eltrans, elemvect);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elemvect);
|
||||
}
|
||||
doftrans.TransformDual(elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from,
|
||||
for (int i=0; i<integrators->Size(); ++i)
|
||||
{
|
||||
BilinearFormIntegrator *integrator = (*integrators)[i];
|
||||
if (*markers[i])
|
||||
if (markers[i] != nullptr)
|
||||
{
|
||||
(a_to.*add_integrator_marker)(integrator, *markers[i]);
|
||||
}
|
||||
|
||||
+10
-3
@@ -485,10 +485,17 @@ void BatchedLORAssembly::Assemble(
|
||||
#endif
|
||||
|
||||
AssembleWithoutBC(a, A);
|
||||
SparseMatrix *A_mat = A.As<SparseMatrix>();
|
||||
|
||||
A_mat->EliminateBC(ess_dofs,
|
||||
Operator::DiagonalPolicy::DIAG_KEEP);
|
||||
const SparseMatrix *P = fes_ho.GetConformingProlongation();
|
||||
if (P)
|
||||
{
|
||||
std::unique_ptr<SparseMatrix> R(Transpose(*P));
|
||||
std::unique_ptr<SparseMatrix> RA(mfem::Mult(*R, *A.As<SparseMatrix>()));
|
||||
A.Reset(mfem::Mult(*RA, *P));
|
||||
}
|
||||
|
||||
A.As<SparseMatrix>()->EliminateBC(ess_dofs,
|
||||
Operator::DiagonalPolicy::DIAG_KEEP);
|
||||
}
|
||||
|
||||
BatchedLORAssembly::BatchedLORAssembly(FiniteElementSpace &fes_ho_)
|
||||
|
||||
+43
-44
@@ -96,7 +96,6 @@ real_t NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
Vector el_x;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
real_t energy = 0.0;
|
||||
|
||||
@@ -123,16 +122,17 @@ real_t NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetElementTransformation(i);
|
||||
x.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
@@ -166,16 +166,17 @@ real_t NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetBE(i);
|
||||
doftrans = fes->GetBdrElementVDofs(i, vdofs);
|
||||
fes->GetBdrElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
x.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
{
|
||||
if (bnfi_marker[k] &&
|
||||
@@ -240,7 +241,6 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
Vector el_x, el_y;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
|
||||
py = 0.0;
|
||||
@@ -268,23 +268,24 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
|
||||
if (doftrans) {doftrans->TransformDual(el_y); }
|
||||
doftrans.TransformDual(el_y);
|
||||
py.AddElementVector(vdofs, el_y);
|
||||
}
|
||||
}
|
||||
@@ -313,23 +314,25 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetBE(i);
|
||||
doftrans = fes->GetBdrElementVDofs(i, vdofs);
|
||||
|
||||
fes->GetBdrElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
{
|
||||
if (bnfi_marker[k] &&
|
||||
(*bnfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
|
||||
if (doftrans) {doftrans->TransformDual(el_y); }
|
||||
doftrans.TransformDual(el_y);
|
||||
py.AddElementVector(vdofs, el_y);
|
||||
}
|
||||
}
|
||||
@@ -453,7 +456,6 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
DenseMatrix elmat;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
const Vector &px = Prolongate(x);
|
||||
|
||||
@@ -489,23 +491,24 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
|
||||
if (doftrans) { doftrans->TransformDual(elmat); }
|
||||
doftrans.TransformDual(elmat);
|
||||
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
// Grad->AddSubMatrix(vdofs, vdofs, elmat, 1);
|
||||
}
|
||||
@@ -535,23 +538,24 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetBE(i);
|
||||
doftrans = fes->GetBdrElementVDofs(i, vdofs);
|
||||
fes->GetBdrElementVDofs(i, vdofs, doftrans);
|
||||
T = fes->GetBdrElementTransformation(i);
|
||||
px.GetSubVector(vdofs, el_x);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
doftrans.InvTransformPrimal(el_x);
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
{
|
||||
if (bnfi_marker[k] &&
|
||||
(*bnfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
|
||||
if (doftrans) { doftrans->TransformDual(elmat); }
|
||||
doftrans.TransformDual(elmat);
|
||||
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -806,7 +810,6 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
Array<const Vector *> el_x_const(fes.Size());
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
real_t energy = 0.0;
|
||||
|
||||
@@ -839,6 +842,7 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < fes[0]->GetNE(); ++i)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
@@ -848,9 +852,9 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
fes[s]->GetElementVDofs(i, *vdofs[s], doftrans);
|
||||
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans.InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -886,6 +890,7 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
@@ -895,9 +900,9 @@ real_t BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetBE(i);
|
||||
doftrans = fes[s]->GetBdrElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetBdrElementVDofs(i, *(vdofs[s]), doftrans);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans.InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
@@ -947,7 +952,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
Array<const FiniteElement *> fe2(fes.Size());
|
||||
ElementTransformation *T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
std::vector<DofTransformation> doftrans(fes.Size());
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
by.UseDevice(true);
|
||||
@@ -992,10 +997,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
T = fes[0]->GetElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
doftrans[s] = fes[s]->GetElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetElementVDofs(i, *(vdofs[s]), doftrans[s]);
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -1009,7 +1014,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
if (el_y[s]->Size() == 0) { continue; }
|
||||
if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); }
|
||||
doftrans[s].TransformDual(*el_y[s]);
|
||||
by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
|
||||
}
|
||||
}
|
||||
@@ -1047,10 +1052,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
T = fes[0]->GetBdrElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
doftrans[s] = fes[s]->GetBdrElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetBdrElementVDofs(i, *(vdofs[s]), doftrans[s]);
|
||||
fe[s] = fes[s]->GetBE(i);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
@@ -1063,7 +1068,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
if (el_y[s]->Size() == 0) { continue; }
|
||||
if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); }
|
||||
doftrans[s].TransformDual(*el_y[s]);
|
||||
by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
|
||||
}
|
||||
}
|
||||
@@ -1231,7 +1236,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
Array<const FiniteElement *>fe(fes.Size());
|
||||
Array<const FiniteElement *>fe2(fes.Size());
|
||||
ElementTransformation * T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
std::vector<DofTransformation> doftrans(fes.Size());
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
for (int i=0; i<fes.Size(); ++i)
|
||||
@@ -1293,9 +1298,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetFE(i);
|
||||
doftrans[s] = fes[s]->GetElementVDofs(i, *vdofs[s]);
|
||||
fes[s]->GetElementVDofs(i, *vdofs[s], doftrans[s]);
|
||||
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
@@ -1310,10 +1315,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int l=0; l<fes.Size(); ++l)
|
||||
{
|
||||
if (elmats(j,l)->Height() == 0) { continue; }
|
||||
if (doftrans[j] || doftrans[l])
|
||||
{
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
}
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l],
|
||||
*elmats(j,l), skip_zeros);
|
||||
}
|
||||
@@ -1354,9 +1356,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fe[s] = fes[s]->GetBE(i);
|
||||
doftrans[s] = fes[s]->GetBdrElementVDofs(i, *(vdofs[s]));
|
||||
fes[s]->GetBdrElementVDofs(i, *(vdofs[s]), doftrans[s]);
|
||||
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
|
||||
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
|
||||
doftrans[s].InvTransformPrimal(*el_x[s]);
|
||||
}
|
||||
|
||||
for (int k = 0; k < bnfi.Size(); k++)
|
||||
@@ -1371,10 +1373,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
for (int l=0; l<fes.Size(); ++l)
|
||||
{
|
||||
if (elmats(j,l)->Height() == 0) { continue; }
|
||||
if (doftrans[j] || doftrans[l])
|
||||
{
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
}
|
||||
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
|
||||
Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l],
|
||||
*elmats(j,l), skip_zeros);
|
||||
}
|
||||
|
||||
+6
-1
@@ -561,6 +561,8 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
|
||||
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(i, dofs);
|
||||
@@ -586,6 +588,8 @@ void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
void ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(i, dofs);
|
||||
@@ -1687,6 +1691,8 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
void ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs, DofTransformation &doftrans) const
|
||||
{
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
|
||||
face_nbr_element_dof.GetRow(i, vdofs);
|
||||
|
||||
if (DoFTransArray[GetFaceNbrFE(i)->GetGeomType()])
|
||||
@@ -1703,7 +1709,6 @@ void ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetFaceNbrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
+24
-50
@@ -55,22 +55,16 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, const GridFunction *gf,
|
||||
int element_counter = 0;
|
||||
const int MyRank = pfes->GetMyRank();
|
||||
const int glob_ne = glob_fes->GetNE();
|
||||
DofTransformation ltrans, gtrans;
|
||||
for (int i = 0; i < glob_ne; i++)
|
||||
{
|
||||
if (partitioning[i] == MyRank)
|
||||
{
|
||||
const DofTransformation* const ltrans = pfes->GetElementVDofs(element_counter,
|
||||
lvdofs);
|
||||
const DofTransformation* const gtrans = glob_fes->GetElementVDofs(i, gvdofs);
|
||||
pfes->GetElementVDofs(element_counter, lvdofs, ltrans);
|
||||
glob_fes->GetElementVDofs(i, gvdofs, gtrans);
|
||||
gf->GetSubVector(gvdofs, lnodes);
|
||||
if (gtrans)
|
||||
{
|
||||
gtrans->InvTransformPrimal(lnodes);
|
||||
}
|
||||
if (ltrans)
|
||||
{
|
||||
ltrans->TransformPrimal(lnodes);
|
||||
}
|
||||
gtrans.InvTransformPrimal(lnodes);
|
||||
ltrans.TransformPrimal(lnodes);
|
||||
SetSubVector(lvdofs, lnodes);
|
||||
element_counter++;
|
||||
}
|
||||
@@ -279,11 +273,11 @@ const
|
||||
Array<int> dofs;
|
||||
Vector DofVal, LocVec;
|
||||
const int nbr_el_no = i - pfes->GetParMesh()->GetNE();
|
||||
DofTransformation doftrans;
|
||||
if (nbr_el_no >= 0)
|
||||
{
|
||||
int fes_vdim = pfes->GetVDim();
|
||||
const DofTransformation* const doftrans = pfes->GetFaceNbrElementVDofs(
|
||||
nbr_el_no, dofs);
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs, doftrans);
|
||||
// Choose fe to be of the order whose number of DOFs matches dofs.Size(),
|
||||
// in the variable order case.
|
||||
const int ndofs = pfes->IsVariableOrder() ? dofs.Size() : 0;
|
||||
@@ -302,10 +296,7 @@ const
|
||||
face_nbr_data.GetSubVector(dofs, LocVec);
|
||||
DofVal.SetSize(dofs.Size());
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
|
||||
if (fe->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
@@ -321,7 +312,7 @@ const
|
||||
}
|
||||
else
|
||||
{
|
||||
const DofTransformation* const doftrans = fes->GetElementDofs(i, dofs);
|
||||
fes->GetElementDofs(i, dofs, doftrans);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
DofVal.SetSize(dofs.Size());
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -336,10 +327,7 @@ const
|
||||
fe->CalcPhysShape(*Tr, DofVal);
|
||||
}
|
||||
GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
}
|
||||
|
||||
return (DofVal * LocVec);
|
||||
@@ -352,15 +340,11 @@ void ParGridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
if (nbr_el_no >= 0)
|
||||
{
|
||||
Array<int> dofs;
|
||||
const DofTransformation* const doftrans = pfes->GetFaceNbrElementVDofs(
|
||||
nbr_el_no,
|
||||
dofs);
|
||||
DofTransformation doftrans;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs, doftrans);
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
const FiniteElement *FElem = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
int dof = FElem->GetDof();
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
@@ -428,8 +412,8 @@ real_t ParGridFunction::GetValue(ElementTransformation &T,
|
||||
|
||||
Array<int> dofs;
|
||||
const FiniteElement * fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
const DofTransformation* const doftrans = pfes->GetFaceNbrElementVDofs(
|
||||
nbr_el_no, dofs);
|
||||
DofTransformation doftrans;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs, doftrans);
|
||||
|
||||
pfes->DofsToVDofs(comp-1, dofs);
|
||||
Vector DofVal(dofs.Size()), LocVec;
|
||||
@@ -442,10 +426,7 @@ real_t ParGridFunction::GetValue(ElementTransformation &T,
|
||||
fe->CalcPhysShape(T, DofVal);
|
||||
}
|
||||
face_nbr_data.GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
doftrans.InvTransformPrimal(LocVec);
|
||||
|
||||
|
||||
return (DofVal * LocVec);
|
||||
@@ -476,13 +457,11 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs);
|
||||
DofTransformation doftrans;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs, doftrans);
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
doftrans.InvTransformPrimal(loc_data);
|
||||
|
||||
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
const int dof = fe->GetDof();
|
||||
@@ -1335,25 +1314,20 @@ real_t L2ZZErrorEstimator(BilinearFormIntegrator &flux_integrator,
|
||||
ParFiniteElementSpace *xfes = x.ParFESpace();
|
||||
Array<int> xdofs, fdofs;
|
||||
Vector el_x, el_f;
|
||||
DofTransformation xtrans, ftrans;
|
||||
|
||||
for (int i = 0; i < xfes->GetNE(); i++)
|
||||
{
|
||||
const DofTransformation* const xtrans = xfes->GetElementVDofs(i, xdofs);
|
||||
xfes->GetElementVDofs(i, xdofs, xtrans);
|
||||
x.GetSubVector(xdofs, el_x);
|
||||
if (xtrans)
|
||||
{
|
||||
xtrans->InvTransformPrimal(el_x);
|
||||
}
|
||||
xtrans.InvTransformPrimal(el_x);
|
||||
|
||||
ElementTransformation *Transf = xfes->GetElementTransformation(i);
|
||||
flux_integrator.ComputeElementFlux(*xfes->GetFE(i), *Transf, el_x,
|
||||
*flux_fes.GetFE(i), el_f, false);
|
||||
|
||||
const DofTransformation* const ftrans = flux_fes.GetElementVDofs(i, fdofs);
|
||||
if (ftrans)
|
||||
{
|
||||
ftrans->TransformPrimal(el_f);
|
||||
}
|
||||
flux_fes.GetElementVDofs(i, fdofs, ftrans);
|
||||
ftrans.TransformPrimal(el_f);
|
||||
flux.SetSubVector(fdofs, el_f);
|
||||
}
|
||||
|
||||
|
||||
@@ -300,12 +300,16 @@ public:
|
||||
real_t ComputeL1Error(Coefficient *exsol[],
|
||||
const IntegrationRule *irs[] = NULL) const override
|
||||
{
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
real_t glb_err = GlobalLpNorm(1.0,
|
||||
GridFunction::ComputeL1Error(exsol, irs),
|
||||
pfes->GetComm());
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
return glb_err;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -200,7 +200,7 @@ static void Det2DSurface(const int NE,
|
||||
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
|
||||
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
|
||||
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
|
||||
Y(qx,qy,e) = sqrt(E*G - F*F);
|
||||
Y(qx,qy,e) = std::sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -326,7 +326,7 @@ static void Eval2D(const int NE,
|
||||
const double E = j(0,0)*j(0,0) + j(1,0)*j(1,0) + j(2,0)*j(2,0);
|
||||
const double F = j(0,0)*j(0,1) + j(1,0)*j(1,1) + j(2,0)*j(2,1);
|
||||
const double G = j(0,1)*j(0,1) + j(1,1)*j(1,1) + j(2,1)*j(2,1);
|
||||
det(q,e) = sqrt(E*G - F*F);
|
||||
det(q,e) = std::sqrt(E*G - F*F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,9 +22,9 @@ namespace mfem
|
||||
/* AD related definitions below ========================================*/
|
||||
|
||||
/// MFEM native AD-type for first derivatives
|
||||
using AD1Type = internal::dual<real_t, real_t>;
|
||||
using AD1Type = future::dual<real_t, real_t>;
|
||||
/// MFEM native AD-type for second derivatives
|
||||
using AD2Type = internal::dual<AD1Type, AD1Type>;
|
||||
using AD2Type = future::dual<AD1Type, AD1Type>;
|
||||
|
||||
/*
|
||||
Functions for 2x2 DenseMatrix cast as std::vector<type>, assuming column-major storage
|
||||
|
||||
+11
-20
@@ -2100,10 +2100,11 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
|
||||
y = 0.0;
|
||||
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
DofTransformation * doftrans_h = hFESpace.GetElementDofs(i, h_dofs);
|
||||
DofTransformation * doftrans_l = lFESpace.GetElementDofs(i, l_dofs);
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom || isvar_order)
|
||||
@@ -2123,15 +2124,9 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
|
||||
h_dofs.Copy(h_vdofs);
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
x.GetSubVector(l_vdofs, subX);
|
||||
if (doftrans_l)
|
||||
{
|
||||
doftrans_l->InvTransformPrimal(subX);
|
||||
}
|
||||
doftrans_l.InvTransformPrimal(subX);
|
||||
loc_prol.Mult(subX, subY);
|
||||
if (doftrans_h)
|
||||
{
|
||||
doftrans_h->TransformPrimal(subY);
|
||||
}
|
||||
doftrans_h.TransformPrimal(subY);
|
||||
y.SetSubVector(h_vdofs, subY);
|
||||
}
|
||||
}
|
||||
@@ -2157,10 +2152,12 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
|
||||
int vdim = lFESpace.GetVDim();
|
||||
|
||||
DofTransformation doftrans_h, doftrans_l;
|
||||
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
DofTransformation * doftrans_h = hFESpace.GetElementDofs(i, h_dofs);
|
||||
DofTransformation * doftrans_l = lFESpace.GetElementDofs(i, l_dofs);
|
||||
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
|
||||
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
|
||||
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
|
||||
if (geom != cached_geom || isvar_order)
|
||||
@@ -2182,10 +2179,7 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
hFESpace.DofsToVDofs(vd, h_vdofs);
|
||||
|
||||
x.GetSubVector(h_vdofs, subX);
|
||||
if (doftrans_h)
|
||||
{
|
||||
doftrans_h->InvTransformDual(subX);
|
||||
}
|
||||
doftrans_h.InvTransformDual(subX);
|
||||
for (int p = 0; p < h_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[lFESpace.DecodeDof(h_dofs[p])])
|
||||
@@ -2195,10 +2189,7 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
|
||||
}
|
||||
|
||||
loc_prol.Mult(subX, subY);
|
||||
if (doftrans_l)
|
||||
{
|
||||
doftrans_l->TransformDual(subY);
|
||||
}
|
||||
doftrans_l.TransformDual(subY);
|
||||
y.AddElementVector(l_vdofs, subY);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ protected:
|
||||
static_assert(std::is_trivial<T>::value, "type T must be trivial");
|
||||
|
||||
public:
|
||||
using value_type = T; ///< Type alias for stl.
|
||||
using reference = T&; ///< Type alias for stl.
|
||||
using const_reference = const T&; ///< Type alias for stl.
|
||||
|
||||
friend void Swap<T>(Array<T> &, Array<T> &);
|
||||
|
||||
/// Creates an empty array
|
||||
@@ -174,6 +178,9 @@ public:
|
||||
/// Append element 'el' to array, resize if necessary.
|
||||
inline int Append(const T & el);
|
||||
|
||||
/// STL-like push_back. Append element 'el' to array, resize if necessary.
|
||||
void push_back(const T &el) { Append(el); }
|
||||
|
||||
/// Append another array to this array, resize if necessary.
|
||||
inline int Append(const T *els, int nels);
|
||||
|
||||
|
||||
+2
-2
@@ -186,7 +186,7 @@ void Device::Configure(const std::string &device, const int device_id)
|
||||
// and avoid the 'singleton_device' to destroy the mm.
|
||||
if (device_env)
|
||||
{
|
||||
std::memcpy(this, &Get(), sizeof(Device));
|
||||
std::memcpy((void*)this, &Get(), sizeof(Device));
|
||||
Get().destroy_mm = false;
|
||||
return;
|
||||
}
|
||||
@@ -243,7 +243,7 @@ void Device::Configure(const std::string &device, const int device_id)
|
||||
Get().UpdateMemoryTypeAndClass(device_option);
|
||||
|
||||
// Copy all data members from the global 'singleton_device' into '*this'.
|
||||
if (this != &Get()) { std::memcpy(this, &Get(), sizeof(Device)); }
|
||||
if (this != &Get()) { std::memcpy((void*)this, &Get(), sizeof(Device)); }
|
||||
|
||||
// Only '*this' will call the MemoryManager::Destroy() method.
|
||||
destroy_mm = true;
|
||||
|
||||
+13
-13
@@ -187,33 +187,33 @@ public:
|
||||
/** The string parameter @a device must be a comma-separated list of backend
|
||||
string names (see below). The @a device_id argument specifies the ID of
|
||||
the actual devices (e.g. GPU) to use.
|
||||
* The available backends are described by the Backend class.
|
||||
* The string name of a backend is the lowercase version of the
|
||||
- The available backends are described by the Backend class.
|
||||
- The string name of a backend is the lowercase version of the
|
||||
Backend::Id enumeration constant with '_' replaced by '-', e.g. the
|
||||
string name of 'RAJA_CPU' is 'raja-cpu'. The string name of the debug
|
||||
backend (Backend::Id 'DEBUG_DEVICE') is exceptionally set to 'debug'.
|
||||
* The 'cpu' backend is always enabled with lowest priority.
|
||||
* The current backend priority from highest to lowest is:
|
||||
- The 'cpu' backend is always enabled with lowest priority.
|
||||
- The current backend priority from highest to lowest is:
|
||||
'ceed-cuda', 'occa-cuda', 'raja-cuda', 'cuda',
|
||||
'ceed-hip', 'hip', 'debug',
|
||||
'occa-omp', 'raja-omp', 'omp',
|
||||
'ceed-cpu', 'occa-cpu', 'raja-cpu', 'cpu'.
|
||||
* Multiple backends can be configured at the same time.
|
||||
* Only one 'occa-*' backend can be configured at a time.
|
||||
* The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
|
||||
- Multiple backends can be configured at the same time.
|
||||
- Only one 'occa-*' backend can be configured at a time.
|
||||
- The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
|
||||
is already enabled.
|
||||
* The backend 'occa-omp' enables the 'omp' backend (if MFEM was built
|
||||
- The backend 'occa-omp' enables the 'omp' backend (if MFEM was built
|
||||
with MFEM_USE_OPENMP=YES) unless 'raja-omp' is already enabled.
|
||||
* Only one 'ceed-*' backend can be configured at a time.
|
||||
* The backend 'ceed-cpu' delegates to a libCEED CPU backend the setup and
|
||||
- Only one 'ceed-*' backend can be configured at a time.
|
||||
- The backend 'ceed-cpu' delegates to a libCEED CPU backend the setup and
|
||||
evaluation of the operator.
|
||||
* The backend 'ceed-cuda' delegates to a libCEED CUDA backend the setup
|
||||
- The backend 'ceed-cuda' delegates to a libCEED CUDA backend the setup
|
||||
and evaluation of operators and enables the 'cuda' backend to avoid
|
||||
transfers between host and device.
|
||||
* The backend 'ceed-hip' delegates to a libCEED HIP backend the setup
|
||||
- The backend 'ceed-hip' delegates to a libCEED HIP backend the setup
|
||||
and evaluation of operators and enables the 'hip' backend to avoid
|
||||
transfers between host and device.
|
||||
* The 'debug' backend should not be combined with other device backends.
|
||||
- The 'debug' backend should not be combined with other device backends.
|
||||
|
||||
@note If the device is actually enabled, this method will also update the
|
||||
current host/device MemoryType and MemoryClass. */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifndef MFEM_ENZYME_HPP
|
||||
#define MFEM_ENZYME_HPP
|
||||
@@ -25,11 +26,28 @@ extern int enzyme_dup;
|
||||
extern int enzyme_dupnoneed;
|
||||
extern int enzyme_out;
|
||||
extern int enzyme_const;
|
||||
extern int enzyme_interleave;
|
||||
|
||||
#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
|
||||
#define MFEM_DEVICE_EXTERN_STMT(name) extern __device__ int name;
|
||||
#else
|
||||
#define MFEM_DEVICE_EXTERN_STMT(name)
|
||||
#endif
|
||||
|
||||
MFEM_DEVICE_EXTERN_STMT(enzyme_dup)
|
||||
MFEM_DEVICE_EXTERN_STMT(enzyme_dupnoneed)
|
||||
MFEM_DEVICE_EXTERN_STMT(enzyme_out)
|
||||
MFEM_DEVICE_EXTERN_STMT(enzyme_const)
|
||||
MFEM_DEVICE_EXTERN_STMT(enzyme_interleave)
|
||||
|
||||
// warning: if inlined, triggers function '__enzyme_autodiff' is not defined
|
||||
template <typename return_type, typename... Args>
|
||||
MFEM_HOST_DEVICE
|
||||
return_type __enzyme_autodiff(Args...);
|
||||
|
||||
// warning: if inlined, triggers function '__enzyme_fwddiff' is not defined
|
||||
template <typename return_type, typename... Args>
|
||||
MFEM_HOST_DEVICE
|
||||
return_type __enzyme_fwddiff(Args...);
|
||||
|
||||
#define MFEM_ENZYME_INACTIVENOFREE __attribute__((enzyme_inactive, enzyme_nofree))
|
||||
|
||||
+5
-1
@@ -162,7 +162,11 @@ private:
|
||||
|
||||
// MFEM_UNROLL pragma macro that can be used inside MFEM_FORALL macros.
|
||||
#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)
|
||||
#endif
|
||||
#else
|
||||
#define MFEM_UNROLL(N)
|
||||
#endif
|
||||
@@ -876,7 +880,7 @@ template<class B, class R> struct reduction_kernel
|
||||
/// helper for computing the reduction block size
|
||||
static int block_log2(unsigned N)
|
||||
{
|
||||
#if defined(__GNUC__) or defined(__clang__)
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return N ? (sizeof(unsigned) * 8 - __builtin_clz(N)) : 0;
|
||||
#elif defined(_MSC_VER)
|
||||
return sizeof(unsigned) * 8 - __lzclz(N);
|
||||
|
||||
+6
-3
@@ -73,9 +73,6 @@ const char *GetConfigStr()
|
||||
#ifdef MFEM_USE_AMGX
|
||||
"MFEM_USE_AMGX\n"
|
||||
#endif
|
||||
#ifdef MFEM_USE_MAGMA
|
||||
"MFEM_USE_MAGMA\n"
|
||||
#endif
|
||||
#ifdef MFEM_USE_CEED
|
||||
"MFEM_USE_CEED\n"
|
||||
#endif
|
||||
@@ -88,6 +85,9 @@ const char *GetConfigStr()
|
||||
#ifdef MFEM_USE_DOUBLE
|
||||
"MFEM_USE_DOUBLE\n"
|
||||
#endif
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
"MFEM_USE_ENZYME\n"
|
||||
#endif
|
||||
#ifdef MFEM_USE_EXCEPTIONS
|
||||
"MFEM_USE_EXCEPTIONS\n"
|
||||
#endif
|
||||
@@ -118,6 +118,9 @@ const char *GetConfigStr()
|
||||
#ifdef MFEM_USE_LIBUNWIND
|
||||
"MFEM_USE_LIBUNWIND\n"
|
||||
#endif
|
||||
#ifdef MFEM_USE_MAGMA
|
||||
"MFEM_USE_MAGMA\n"
|
||||
#endif
|
||||
#ifdef MFEM_USE_MEMALLOC
|
||||
"MFEM_USE_MEMALLOC\n"
|
||||
#endif
|
||||
|
||||
+9
-2
@@ -13,6 +13,7 @@
|
||||
#define MFEM_DTENSOR
|
||||
|
||||
#include "../general/backends.hpp"
|
||||
#include <array>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -42,7 +43,8 @@ public:
|
||||
static inline int result(const int* sizes, T first, Args... args)
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ASSERT(first<sizes[Dim-1],"Trying to access out of boundary.");
|
||||
MFEM_ASSERT(first<static_cast<T>(sizes[Dim-1]),
|
||||
"Trying to access out of boundary.");
|
||||
#endif
|
||||
return static_cast<int>(first);
|
||||
}
|
||||
@@ -87,7 +89,9 @@ protected:
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
DeviceTensor() = delete;
|
||||
// DeviceTensor() = delete;
|
||||
MFEM_HOST_DEVICE
|
||||
DeviceTensor() {}
|
||||
|
||||
/// Constructor to initialize a tensor from the Scalar array data_
|
||||
template <typename... Args> MFEM_HOST_DEVICE
|
||||
@@ -122,6 +126,9 @@ public:
|
||||
{
|
||||
return data[i];
|
||||
}
|
||||
|
||||
/// Returns the shape of the tensor.
|
||||
MFEM_HOST_DEVICE inline auto &GetShape() const { return sizes; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
+7
-9
@@ -15,8 +15,7 @@
|
||||
* @brief This file contains the declaration of a dual number class
|
||||
*/
|
||||
|
||||
#ifndef MFEM_INTERNAL_DUAL_HPP
|
||||
#define MFEM_INTERNAL_DUAL_HPP
|
||||
#pragma once
|
||||
|
||||
#include <type_traits> // for is_arithmetic
|
||||
#include <cmath>
|
||||
@@ -24,7 +23,7 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace internal
|
||||
namespace future
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -40,8 +39,9 @@ struct dual
|
||||
/// the partial derivatives of value w.r.t. some other quantity
|
||||
gradient_type gradient;
|
||||
|
||||
/** @brief assignment of a double to a value of a dual. Promotes a double to
|
||||
/** @brief assignment of a real_t to a value of a dual. Promotes a real_t to
|
||||
* a dual with a zero gradient value. */
|
||||
MFEM_HOST_DEVICE
|
||||
auto operator=(real_t a) -> dual<value_type, gradient_type>&
|
||||
{
|
||||
value = a;
|
||||
@@ -258,7 +258,7 @@ dual<value_type, gradient_type>& operator-=(dual<value_type, gradient_type>& a,
|
||||
return a;
|
||||
}
|
||||
|
||||
/** @brief compound assignment (+) for dual numbers with `double` righthand side */
|
||||
/** @brief compound assignment (+) for dual numbers with `real_t` righthand side */
|
||||
template <typename value_type, typename gradient_type> MFEM_HOST_DEVICE
|
||||
dual<value_type, gradient_type>& operator+=(dual<value_type, gradient_type>& a,
|
||||
real_t b)
|
||||
@@ -267,7 +267,7 @@ dual<value_type, gradient_type>& operator+=(dual<value_type, gradient_type>& a,
|
||||
return a;
|
||||
}
|
||||
|
||||
/** @brief compound assignment (-) for dual numbers with `double` righthand side */
|
||||
/** @brief compound assignment (-) for dual numbers with `real_t` righthand side */
|
||||
template <typename value_type, typename gradient_type> MFEM_HOST_DEVICE
|
||||
dual<value_type, gradient_type>& operator-=(dual<value_type, gradient_type>& a,
|
||||
real_t b)
|
||||
@@ -435,7 +435,5 @@ MFEM_HOST_DEVICE gradient_type get_gradient(dual<value_type, gradient_type> arg)
|
||||
return arg.gradient;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace future
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "batched/batched.hpp"
|
||||
#include "batched/gpu_blas.hpp"
|
||||
#include "batched/solver.hpp"
|
||||
#include "tensor.hpp"
|
||||
|
||||
#ifdef MFEM_USE_AMGX
|
||||
#include "amgxsolver.hpp"
|
||||
|
||||
+231
-50
@@ -15,27 +15,18 @@
|
||||
* @brief Implementation of the tensor class
|
||||
*/
|
||||
|
||||
#ifndef MFEM_INTERNAL_TENSOR_HPP
|
||||
#define MFEM_INTERNAL_TENSOR_HPP
|
||||
#pragma once
|
||||
|
||||
#include "../general/backends.hpp"
|
||||
#include "dual.hpp"
|
||||
#include <limits>
|
||||
#include <type_traits> // for std::false_type
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
namespace internal
|
||||
namespace future
|
||||
{
|
||||
|
||||
#if defined(__CUDACC__)
|
||||
#if __CUDAVER__ >= 75000
|
||||
#define MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING #pragma nv_exec_check_disable
|
||||
#else
|
||||
#define MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING #pragma hd_warning_disable
|
||||
#endif
|
||||
#else //__CUDACC__
|
||||
#define MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING
|
||||
#endif
|
||||
|
||||
template <typename T, int... n>
|
||||
struct tensor;
|
||||
|
||||
@@ -149,7 +140,7 @@ struct tensor<T, n0, n1, n2, n3, n4>
|
||||
*/
|
||||
struct zero
|
||||
{
|
||||
/** @brief `zero` is implicitly convertible to double with value 0.0 */
|
||||
/** @brief `zero` is implicitly convertible to real_t with value 0.0 */
|
||||
MFEM_HOST_DEVICE operator real_t() { return 0.0; }
|
||||
|
||||
/** @brief `zero` is implicitly convertible to a tensor of any shape */
|
||||
@@ -304,7 +295,6 @@ using reduced_tensor = typename std::conditional<
|
||||
* @note the different cases of 0D, 1D, 2D, 3D, and 4D are implemented separately
|
||||
* to work around a limitation in nvcc involving __host__ __device__ lambdas with `auto` parameters.
|
||||
*/
|
||||
MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING
|
||||
template <typename lambda_type>
|
||||
MFEM_HOST_DEVICE constexpr auto make_tensor(lambda_type f) ->
|
||||
tensor<decltype(f())>
|
||||
@@ -323,7 +313,6 @@ tensor<decltype(f())>
|
||||
* @note the different cases of 0D, 1D, 2D, 3D, and 4D are implemented separately
|
||||
* to work around a limitation in nvcc involving __host__ __device__ lambdas with `auto` parameters.
|
||||
*/
|
||||
MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING
|
||||
template <int n1, typename lambda_type>
|
||||
MFEM_HOST_DEVICE auto make_tensor(lambda_type f) ->
|
||||
tensor<decltype(f(n1)), n1>
|
||||
@@ -349,7 +338,6 @@ tensor<decltype(f(n1)), n1>
|
||||
* @note the different cases of 0D, 1D, 2D, 3D, and 4D are implemented separately
|
||||
* to work around a limitation in nvcc involving __host__ __device__ lambdas with `auto` parameters.
|
||||
*/
|
||||
MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING
|
||||
template <int n1, int n2, typename lambda_type>
|
||||
MFEM_HOST_DEVICE auto make_tensor(lambda_type f) ->
|
||||
tensor<decltype(f(n1, n2)), n1, n2>
|
||||
@@ -379,7 +367,6 @@ tensor<decltype(f(n1, n2)), n1, n2>
|
||||
* @note the different cases of 0D, 1D, 2D, 3D, and 4D are implemented separately
|
||||
* to work around a limitation in nvcc involving __host__ __device__ lambdas with `auto` parameters.
|
||||
*/
|
||||
MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING
|
||||
template <int n1, int n2, int n3, typename lambda_type>
|
||||
MFEM_HOST_DEVICE auto make_tensor(lambda_type f) ->
|
||||
tensor<decltype(f(n1, n2, n3)), n1, n2, n3>
|
||||
@@ -413,7 +400,6 @@ tensor<decltype(f(n1, n2, n3)), n1, n2, n3>
|
||||
* @note the different cases of 0D, 1D, 2D, 3D, and 4D are implemented separately
|
||||
* to work around a limitation in nvcc involving __host__ __device__ lambdas with `auto` parameters.
|
||||
*/
|
||||
MFEM_SUPPRESS_NVCC_HOSTDEVICE_WARNING
|
||||
template <int n1, int n2, int n3, int n4, typename lambda_type>
|
||||
MFEM_HOST_DEVICE auto make_tensor(lambda_type f) ->
|
||||
tensor<decltype(f(n1, n2, n3, n4)), n1, n2, n3, n4>
|
||||
@@ -436,6 +422,23 @@ tensor<decltype(f(n1, n2, n3, n4)), n1, n2, n3, n4>
|
||||
return A;
|
||||
}
|
||||
|
||||
// needs to be generalized
|
||||
template <typename T, int m, int n> MFEM_HOST_DEVICE
|
||||
tensor<T, n> get_col(tensor<T, m, n> A, int j)
|
||||
{
|
||||
tensor<T, n> c{};
|
||||
c(0) = A[0][j];
|
||||
c(1) = A[1][j];
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @overload
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
tensor<T, 1> get_col(tensor<T, 1, 1> A, int j)
|
||||
{
|
||||
return tensor<T, 1> {A[0][0]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief return the sum of two tensors
|
||||
* @tparam S the underlying type of the lefthand argument
|
||||
@@ -497,7 +500,7 @@ tensor<decltype(S {} + T{}), n...>
|
||||
|
||||
/**
|
||||
* @brief multiply a tensor by a scalar value
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, double, int) or a dual number
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, real_t, int) or a dual number
|
||||
* @tparam T the underlying type of the tensor (righthand) argument
|
||||
* @tparam n integers describing the tensor shape
|
||||
* @param[in] scale The scaling factor
|
||||
@@ -519,7 +522,7 @@ tensor<decltype(S {} * T{}), n...>
|
||||
|
||||
/**
|
||||
* @brief multiply a tensor by a scalar value
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, double, int) or a dual number
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, real_t, int) or a dual number
|
||||
* @tparam T the underlying type of the tensor (righthand) argument
|
||||
* @tparam n integers describing the tensor shape
|
||||
* @param[in] A The tensor to be scaled
|
||||
@@ -541,7 +544,7 @@ tensor<decltype(T {} * S{}), n...>
|
||||
|
||||
/**
|
||||
* @brief divide a scalar by each element in a tensor
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, double, int) or a dual number
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, real_t, int) or a dual number
|
||||
* @tparam T the underlying type of the tensor (righthand) argument
|
||||
* @tparam n integers describing the tensor shape
|
||||
* @param[in] scale The numerator
|
||||
@@ -563,7 +566,7 @@ tensor<decltype(S {} * T{}), n...>
|
||||
|
||||
/**
|
||||
* @brief divide a tensor by a scalar
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, double, int) or a dual number
|
||||
* @tparam S the scalar value type. Must be arithmetic (e.g. float, real_t, int) or a dual number
|
||||
* @tparam T the underlying type of the tensor (righthand) argument
|
||||
* @tparam n integers describing the tensor shape
|
||||
* @param[in] A The tensor of numerators
|
||||
@@ -697,6 +700,20 @@ auto outer(S A, T B) -> decltype(A * B)
|
||||
return A * B;
|
||||
}
|
||||
|
||||
template <typename T, int n, int m> MFEM_HOST_DEVICE
|
||||
tensor<T, n + m> flatten(tensor<T, n, m> A)
|
||||
{
|
||||
tensor<T, n + m> B{};
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
B(i + j * m) = A(i, j);
|
||||
}
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* @note this overload implements the case where the left argument is a scalar, and the right argument is a tensor
|
||||
@@ -1051,13 +1068,25 @@ decltype(S {} * T{})
|
||||
return AB;
|
||||
}
|
||||
|
||||
template <typename S, typename T, int m, int... n> MFEM_HOST_DEVICE
|
||||
auto dot(const tensor<S, m>& A, const tensor<T, m, n...>& B) ->
|
||||
tensor<decltype(S {} * T{}), n...>
|
||||
template <typename T, int m> MFEM_HOST_DEVICE
|
||||
auto dot(const tensor<T, m>& A, const tensor<T, m>& B) ->
|
||||
decltype(T {})
|
||||
{
|
||||
constexpr int dimensions[] = {n...};
|
||||
tensor<decltype(S{} * T{}), n...> AB{};
|
||||
for (int i = 0; i < dimensions[0]; i++)
|
||||
decltype(T{}) AB{};
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
AB += A[i] * B[i];
|
||||
}
|
||||
return AB;
|
||||
}
|
||||
|
||||
template <typename S, typename T, int m, int n0, int n1, int... n>
|
||||
MFEM_HOST_DEVICE
|
||||
auto dot(const tensor<S, m>& A, const tensor<T, m, n0, n1, n...>& B) ->
|
||||
tensor<decltype(S {} * T{}), n0, n1, n...>
|
||||
{
|
||||
tensor<decltype(S{} * T{}), n0, n1, n...> AB{};
|
||||
for (int i = 0; i < n0; i++)
|
||||
{
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
@@ -1088,7 +1117,7 @@ decltype(S {} * T{} * U{})
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief double dot product, contracting over the two "middle" indices
|
||||
* @brief real_t dot product, contracting over the two "middle" indices
|
||||
* @tparam S the underlying type of the tensor (lefthand) argument
|
||||
* @tparam T the underlying type of the tensor (righthand) argument
|
||||
* @tparam m first dimension of A
|
||||
@@ -1285,7 +1314,7 @@ tensor<T, n, n> dev(const tensor<T, n, n>& A)
|
||||
* @return I_dim
|
||||
*/
|
||||
template <int dim>
|
||||
MFEM_HOST_DEVICE tensor<real_t, dim, dim> Identity()
|
||||
MFEM_HOST_DEVICE tensor<real_t, dim, dim> IdentityMatrix()
|
||||
{
|
||||
tensor<real_t, dim, dim> I{};
|
||||
for (int i = 0; i < dim; i++)
|
||||
@@ -1321,6 +1350,12 @@ tensor<T, n, m> transpose(const tensor<T, m, n>& A)
|
||||
* @param[in] A The matrix to obtain the determinant of
|
||||
*/
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
T det(const tensor<T, 1, 1>& A)
|
||||
{
|
||||
return A[0][0];
|
||||
}
|
||||
/// @overload
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
T det(const tensor<T, 2, 2>& A)
|
||||
{
|
||||
return A[0][0] * A[1][1] - A[0][1] * A[1][0];
|
||||
@@ -1335,6 +1370,145 @@ T det(const tensor<T, 3, 3>& A)
|
||||
A[2][0];
|
||||
}
|
||||
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
std::tuple<tensor<T, 1>, tensor<T, 1, 1>> eig(tensor<T, 1, 1> &A)
|
||||
{
|
||||
return {tensor<T, 1>{A[0][0]}, tensor<T, 1, 1>{{{1.0}}}};
|
||||
}
|
||||
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
std::tuple<tensor<T, 2>, tensor<T, 2, 2>> eig(tensor<T, 2, 2> &A)
|
||||
{
|
||||
tensor<T, 2> e;
|
||||
tensor<T, 2, 2> v;
|
||||
|
||||
real_t d0 = A(0, 0);
|
||||
real_t d2 = A(0, 1);
|
||||
real_t d3 = A(1, 1);
|
||||
real_t c, s;
|
||||
|
||||
if (d2 == 0.0)
|
||||
{
|
||||
c = 1.0;
|
||||
s = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
real_t t;
|
||||
const real_t zeta = (d3 - d0) / (2.0 * d2);
|
||||
const real_t azeta = fabs(zeta);
|
||||
if (azeta < std::sqrt(1.0/std::numeric_limits<T>::epsilon()))
|
||||
{
|
||||
t = copysign(1./(azeta + std::sqrt(1. + zeta*zeta)), zeta);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = copysign(0.5/azeta, zeta);
|
||||
}
|
||||
c = std::sqrt(1./(1. + t*t));
|
||||
s = c*t;
|
||||
t *= d2;
|
||||
d0 -= t;
|
||||
d3 += t;
|
||||
}
|
||||
|
||||
if (d0 <= d3)
|
||||
{
|
||||
e(0) = d0;
|
||||
e(1) = d3;
|
||||
v(0, 0) = c;
|
||||
v(1, 0) = -s;
|
||||
v(0, 1) = s;
|
||||
v(1, 1) = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
e(0) = d3;
|
||||
e(1) = d0;
|
||||
v(0, 0) = s;
|
||||
v(1, 0) = c;
|
||||
v(0, 1) = c;
|
||||
v(1, 1) = -s;
|
||||
}
|
||||
|
||||
return {e, v};
|
||||
}
|
||||
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
void GetScalingFactor(const T &d_max, T &mult)
|
||||
{
|
||||
int d_exp;
|
||||
if (d_max > 0.)
|
||||
{
|
||||
mult = frexp(d_max, &d_exp);
|
||||
if (d_exp == std::numeric_limits<T>::max_exponent)
|
||||
{
|
||||
mult *= std::numeric_limits<T>::radix;
|
||||
}
|
||||
mult = d_max/mult;
|
||||
}
|
||||
else
|
||||
{
|
||||
mult = 1.;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
T calcsv(const tensor<T, 1, 1> A, const int i)
|
||||
{
|
||||
return A[0][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute the i-th singular value of a 2x2 matrix A
|
||||
*/
|
||||
template <typename T> MFEM_HOST_DEVICE
|
||||
T calcsv(const tensor<T, 2, 2> A, const int i)
|
||||
{
|
||||
real_t mult;
|
||||
real_t d0, d1, d2, d3;
|
||||
d0 = A(0, 0);
|
||||
d1 = A(1, 0);
|
||||
d2 = A(0, 1);
|
||||
d3 = A(1, 1);
|
||||
|
||||
real_t d_max = fabs(d0);
|
||||
if (d_max < fabs(d1)) { d_max = fabs(d1); }
|
||||
if (d_max < fabs(d2)) { d_max = fabs(d2); }
|
||||
if (d_max < fabs(d3)) { d_max = fabs(d3); }
|
||||
|
||||
GetScalingFactor(d_max, mult);
|
||||
|
||||
d0 /= mult;
|
||||
d1 /= mult;
|
||||
d2 /= mult;
|
||||
d3 /= mult;
|
||||
|
||||
real_t t = 0.5*((d0+d2)*(d0-d2)+(d1-d3)*(d1+d3));
|
||||
real_t s = d0*d2 + d1*d3;
|
||||
s = std::sqrt(0.5*(d0*d0 + d1*d1 + d2*d2 + d3*d3) + std::sqrt(t*t + s*s));
|
||||
|
||||
if (s == 0.0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
t = fabs(d0*d3 - d1*d2) / s;
|
||||
if (t > s)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return t*mult;
|
||||
}
|
||||
return s*mult;
|
||||
}
|
||||
if (i == 0)
|
||||
{
|
||||
return s*mult;
|
||||
}
|
||||
return t*mult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return whether a square rank 2 tensor is symmetric
|
||||
*
|
||||
@@ -1474,13 +1648,20 @@ tensor<T, n> linear_solve(tensor<T, n, n> A, const tensor<T, n> b)
|
||||
/**
|
||||
* @brief Inverts a matrix
|
||||
* @param[in] A The matrix to invert
|
||||
* @note Uses a shortcut for inverting a 2-by-2 matrix
|
||||
* @note Uses a shortcut for inverting a 1x1, 2x2 and 3x3 matrix
|
||||
*/
|
||||
inline MFEM_HOST_DEVICE tensor<real_t, 2, 2> inv(const tensor<real_t, 2, 2>& A)
|
||||
template <typename T>
|
||||
inline MFEM_HOST_DEVICE tensor<T, 1, 1> inv(const tensor<T, 1, 1>& A)
|
||||
{
|
||||
real_t inv_detA(1.0 / det(A));
|
||||
return tensor<T, 1, 1> {{{T{1.0} / A[0][0]}}};
|
||||
}
|
||||
|
||||
tensor<real_t, 2, 2> invA{};
|
||||
template <typename T>
|
||||
inline MFEM_HOST_DEVICE tensor<T, 2, 2> inv(const tensor<T, 2, 2>& A)
|
||||
{
|
||||
T inv_detA(1.0 / det(A));
|
||||
|
||||
tensor<T, 2, 2> invA{};
|
||||
|
||||
invA[0][0] = A[1][1] * inv_detA;
|
||||
invA[0][1] = -A[0][1] * inv_detA;
|
||||
@@ -1494,11 +1675,12 @@ inline MFEM_HOST_DEVICE tensor<real_t, 2, 2> inv(const tensor<real_t, 2, 2>& A)
|
||||
* @overload
|
||||
* @note Uses a shortcut for inverting a 3-by-3 matrix
|
||||
*/
|
||||
inline MFEM_HOST_DEVICE tensor<real_t, 3, 3> inv(const tensor<real_t, 3, 3>& A)
|
||||
template <typename T>
|
||||
inline MFEM_HOST_DEVICE tensor<T, 3, 3> inv(const tensor<T, 3, 3>& A)
|
||||
{
|
||||
real_t inv_detA(1.0 / det(A));
|
||||
T inv_detA(1.0 / det(A));
|
||||
|
||||
tensor<real_t, 3, 3> invA{};
|
||||
tensor<T, 3, 3> invA{};
|
||||
|
||||
invA[0][0] = (A[1][1] * A[2][2] - A[1][2] * A[2][1]) * inv_detA;
|
||||
invA[0][1] = (A[0][2] * A[2][1] - A[0][1] * A[2][2]) * inv_detA;
|
||||
@@ -1517,10 +1699,12 @@ inline MFEM_HOST_DEVICE tensor<real_t, 3, 3> inv(const tensor<real_t, 3, 3>& A)
|
||||
* @note For N-by-N matrices with N > 3, requires Gaussian elimination
|
||||
* with partial pivoting
|
||||
*/
|
||||
template <typename T, int n> MFEM_HOST_DEVICE
|
||||
tensor<T, n, n> inv(const tensor<T, n, n>& A)
|
||||
template <typename T, int n>
|
||||
MFEM_HOST_DEVICE
|
||||
typename std::enable_if<(n > 3), tensor<T, n, n>>::type
|
||||
inv(const tensor<T, n, n>& A)
|
||||
{
|
||||
auto abs = [](real_t x) { return (x < 0) ? -x : x; };
|
||||
auto abs = [](T x) { return (x < 0) ? -x : x; };
|
||||
auto swap = [](tensor<T, n>& x, tensor<T, n>& y)
|
||||
{
|
||||
auto tmp = x;
|
||||
@@ -1528,12 +1712,12 @@ tensor<T, n, n> inv(const tensor<T, n, n>& A)
|
||||
y = tmp;
|
||||
};
|
||||
|
||||
tensor<real_t, n, n> B = Identity<n>();
|
||||
tensor<T, n, n> B = IdentityMatrix<n>();
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
// Search for maximum in this column
|
||||
real_t max_val = abs(A[i][i]);
|
||||
T max_val = abs(A[i][i]);
|
||||
|
||||
int max_row = i;
|
||||
for (int j = i + 1; j < n; j++)
|
||||
@@ -1553,7 +1737,7 @@ tensor<T, n, n> inv(const tensor<T, n, n>& A)
|
||||
{
|
||||
if (A[j][i] != 0.0)
|
||||
{
|
||||
real_t c = -A[j][i] / A[i][i];
|
||||
T c = -A[j][i] / A[i][i];
|
||||
A[j] += c * A[i];
|
||||
B[j] += c * B[i];
|
||||
A[j][i] = 0;
|
||||
@@ -1716,7 +1900,7 @@ template <typename T1, typename T2>
|
||||
using outer_product_t = typename detail::outer_prod<T1, T2>::type;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the gradient component of a double (which is nothing)
|
||||
* @brief Retrieves the gradient component of a real_t (which is nothing)
|
||||
* @return The sentinel, @see zero
|
||||
*/
|
||||
inline MFEM_HOST_DEVICE zero get_gradient(real_t /* arg */) { return zero{}; }
|
||||
@@ -2046,11 +2230,8 @@ auto ddot(const isotropic_tensor<S, m, m, m, m>& I,
|
||||
const tensor<T, m, m>& A)
|
||||
-> tensor<decltype(S {} * T{}), m, m>
|
||||
{
|
||||
return I.c1 * tr(A) * Identity<m>() + I.c2 * sym(A) + I.c3 * antisym(A);
|
||||
return I.c1 * tr(A) * IdentityMatrix<m>() + I.c2 * sym(A) + I.c3 * antisym(A);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace future
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -124,8 +124,8 @@ EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
|
||||
EXAMPLE_TEST_DIRS := examples
|
||||
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools \
|
||||
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff hooke \
|
||||
multidomain dpg hdiv-linear-solver spde
|
||||
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
|
||||
hooke multidomain dpg hdiv-linear-solver spde
|
||||
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
|
||||
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
|
||||
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
|
||||
@@ -243,10 +243,23 @@ endif
|
||||
|
||||
ifeq ($(MFEM_USE_CUDA),YES)
|
||||
MFEM_CXX ?= $(CUDA_CXX)
|
||||
MFEM_HOST_CXX ?= $(HOST_CXX)
|
||||
CXXFLAGS += $(CUDA_FLAGS) -ccbin $(MFEM_HOST_CXX)
|
||||
XCOMPILER = $(CUDA_XCOMPILER)
|
||||
XLINKER = $(CUDA_XLINKER)
|
||||
ifeq ($(shell $(MFEM_CXX) --version 2>&1 | grep "NVIDIA"),)
|
||||
# assume clang
|
||||
MFEM_HOST_CXX ?= $(MFEM_CXX)
|
||||
CXXFLAGS += $(CLANG_CUDA_FLAGS)
|
||||
XCOMPILER = $(CXX_XCOMPILER)
|
||||
XLINKER = $(CXX_XLINKER)
|
||||
CUDA_LIB := $(CLANG_CUDA_LIB) $(CUDA_LIB)
|
||||
else
|
||||
ifeq ($(MFEM_USE_ENZYME),YES)
|
||||
$(error Cannot use nvcc with Enzyme! Set CUDA_CXX to CUDA-enabled \
|
||||
clang++ or an MPI wrapper of that)
|
||||
endif
|
||||
MFEM_HOST_CXX ?= $(HOST_CXX)
|
||||
CXXFLAGS += $(NVCC_FLAGS) -ccbin $(MFEM_HOST_CXX)
|
||||
XCOMPILER = $(CUDA_XCOMPILER)
|
||||
XLINKER = $(CUDA_XLINKER)
|
||||
endif
|
||||
# CUDA_OPT and CUDA_LIB are added below
|
||||
# Compatibility test against MFEM_USE_HIP
|
||||
ifeq ($(MFEM_USE_HIP),YES)
|
||||
@@ -285,7 +298,7 @@ ifeq ($(MFEM_USE_LEGACY_OPENMP),YES)
|
||||
endif
|
||||
|
||||
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
|
||||
MFEM_REQ_LIB_DEPS = ENZYME SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
|
||||
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
|
||||
SUITESPARSE STRUMPACK GINKGO GNUTLS HDF5 NETCDF SLEPC PETSC MPFR PUMI HIOP\
|
||||
GSLIB OCCA CEED RAJA UMPIRE MKL_CPARDISO MKL_PARDISO AMGX MAGMA CALIPER PARELAG\
|
||||
TRIBOL BENCHMARK MOONOLITH ALGOIM
|
||||
@@ -307,7 +320,7 @@ ifeq ($(MAKECMDGOALS),config)
|
||||
endif
|
||||
|
||||
# List of MFEM dependencies, processed below
|
||||
MFEM_DEPENDENCIES = $(MFEM_REQ_LIB_DEPS) LIBUNWIND OPENMP CUDA HIP
|
||||
MFEM_DEPENDENCIES = ENZYME $(MFEM_REQ_LIB_DEPS) LIBUNWIND OPENMP CUDA HIP
|
||||
|
||||
# List of deprecated MFEM dependencies, processed below
|
||||
MFEM_LEGACY_DEPENDENCIES = OPENMP
|
||||
@@ -429,7 +442,7 @@ DIRS = general linalg linalg/batched linalg/simd mesh mesh/submesh fem \
|
||||
fem/ceed/integrators/mass fem/ceed/integrators/convection \
|
||||
fem/ceed/integrators/diffusion fem/ceed/integrators/nlconvection \
|
||||
fem/ceed/interface fem/ceed/solvers fem/eltrans fem/fe fem/gslib \
|
||||
fem/integ fem/lor fem/moonolith fem/qinterp fem/tmop
|
||||
fem/integ fem/lor fem/moonolith fem/qinterp fem/tmop fem/dfem
|
||||
|
||||
ifeq ($(MFEM_USE_MOONOLITH),YES)
|
||||
MFEM_CXXFLAGS += $(MOONOLITH_CXX_FLAGS)
|
||||
|
||||
+159
-14
@@ -32,6 +32,7 @@
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -1553,6 +1554,7 @@ Geometry::Type Mesh::GetTypicalElementGeometry() const
|
||||
return geom;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::GetExteriorFaceMarker(Array<int> & face_marker) const
|
||||
{
|
||||
const int num_faces = GetNumFaces();
|
||||
@@ -5402,11 +5404,15 @@ void Mesh::MakeRefined_(Mesh &orig_mesh, const Array<int> &ref_factors,
|
||||
Mesh Mesh::MakeSimplicial(const Mesh &orig_mesh)
|
||||
{
|
||||
Mesh mesh;
|
||||
mesh.MakeSimplicial_(orig_mesh, NULL);
|
||||
auto parent_elements = mesh.MakeSimplicial_(orig_mesh, NULL);
|
||||
if (orig_mesh.GetNodes() != nullptr)
|
||||
{
|
||||
mesh.MakeHigherOrderSimplicial_(orig_mesh, parent_elements);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
Array<int> Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
{
|
||||
MFEM_VERIFY(const_cast<Mesh&>(orig_mesh).CheckElementOrientation(false) == 0,
|
||||
"Mesh::MakeSimplicial requires a properly oriented input mesh");
|
||||
@@ -5420,7 +5426,9 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
{
|
||||
Mesh copy(orig_mesh);
|
||||
Swap(copy, true);
|
||||
return;
|
||||
Array<int> parent_elements(GetNE());
|
||||
std::iota(parent_elements.begin(), parent_elements.end(), 0);
|
||||
return parent_elements;
|
||||
}
|
||||
|
||||
int nv = orig_mesh.GetNV();
|
||||
@@ -5462,15 +5470,20 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
// global vertex number). Use the supplied global numbering, if it is
|
||||
// non-NULL, otherwise use the local numbering.
|
||||
Array<int> vglobal_id;
|
||||
if (vglobal == NULL)
|
||||
if (vglobal == nullptr)
|
||||
{
|
||||
vglobal_id.SetSize(nv);
|
||||
for (int i=0; i<nv; ++i) { vglobal_id[i] = i; }
|
||||
std::iota(vglobal_id.begin(), vglobal_id.end(), 0);
|
||||
vglobal = vglobal_id.GetData();
|
||||
}
|
||||
|
||||
// Number of vertices per element
|
||||
constexpr int nv_tri = 3, nv_quad = 4, nv_tet = 4, nv_prism = 6, nv_hex = 8;
|
||||
constexpr int quad_ntris = 2, prism_ntets = 3;
|
||||
constexpr int quad_ntris = 2; // NTriangles per quad
|
||||
constexpr int prism_ntets = 3; // NTets per prism
|
||||
// Map verts of quad to verts of tri, in two possible configurations.
|
||||
// quad_trimap[i][0,2,4] is the first triangle, and quad_trimap[i][1,3,5] is
|
||||
// the second, for each configuration.
|
||||
static const int quad_trimap[2][nv_tri*quad_ntris] =
|
||||
{
|
||||
{
|
||||
@@ -5555,8 +5568,9 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
hex_tetmap0, hex_tetmap1, hex_tetmap2, hex_tetmap3
|
||||
};
|
||||
|
||||
auto find_min = [](const int*a, int n) { return std::min_element(a,a+n)-a; };
|
||||
auto find_min = [](const int *a, int n) { return std::min_element(a,a+n)-a; };
|
||||
|
||||
Array<int> parent_elems;
|
||||
for (int i=0; i<ne; ++i)
|
||||
{
|
||||
const int *v = orig_mesh.elements[i]->GetVertices();
|
||||
@@ -5565,13 +5579,14 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
|
||||
if (num_subdivisions[orig_geom] == 1)
|
||||
{
|
||||
// (num_subdivisions[orig_geom] == 1) implies that the element does
|
||||
// not need to be further split (it is either a segment, triangle,
|
||||
// or tetrahedron), and so it is left unchanged.
|
||||
// (num_subdivisions[orig_geom] == 1) implies that the element does not
|
||||
// need to be further split (it is either a segment, triangle, or
|
||||
// tetrahedron), and so it is left unchanged.
|
||||
Element *e = NewElement(orig_geom);
|
||||
e->SetAttribute(attrib);
|
||||
e->SetVertices(v);
|
||||
AddElement(e);
|
||||
parent_elems.Append(i);
|
||||
}
|
||||
else if (orig_geom == Geometry::SQUARE)
|
||||
{
|
||||
@@ -5585,6 +5600,7 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
v2[iv] = v[quad_trimap[0][itri + iv*quad_ntris]];
|
||||
}
|
||||
AddElement(e);
|
||||
parent_elems.Append(i);
|
||||
}
|
||||
}
|
||||
else if (orig_geom == Geometry::PRISM)
|
||||
@@ -5614,6 +5630,7 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
v2[iv] = vg[tetmap[itet + iv*prism_ntets]];
|
||||
}
|
||||
AddElement(e);
|
||||
parent_elems.Append(i);
|
||||
}
|
||||
}
|
||||
else if (orig_geom == Geometry::CUBE)
|
||||
@@ -5677,6 +5694,7 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
v2[iv] = vg[tetmap[itet + iv*ntets]];
|
||||
}
|
||||
AddElement(e);
|
||||
parent_elems.Append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5726,8 +5744,134 @@ void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
|
||||
|
||||
MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
|
||||
MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
|
||||
|
||||
return parent_elems;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::MakeHigherOrderSimplicial_(const Mesh &orig_mesh, const Array<int> &parent_elements)
|
||||
{
|
||||
// Higher order associated to vertices are unchanged, and those for
|
||||
// previously existing edges. DOFs associated to new elements need to be set.
|
||||
const int sdim = orig_mesh.SpaceDimension();
|
||||
auto *orig_fespace = orig_mesh.GetNodes()->FESpace();
|
||||
SetCurvature(orig_fespace->GetMaxElementOrder(), orig_fespace->IsDGSpace(),
|
||||
orig_mesh.SpaceDimension(), orig_fespace->GetOrdering());
|
||||
|
||||
// The dofs associated with vertices are unchanged, but there can be new dofs
|
||||
// associated to edges, faces and volumes. Additionally, because we know that
|
||||
// the set of vertices is unchanged by the splitting operation, we can use
|
||||
// the vertices to map local coordinates of the "child" elements (the new
|
||||
// simplices introduced), from the "parent" element (the quad, prism, hex
|
||||
// that was split).
|
||||
|
||||
// For segment, triangle and tetrahedron, the dof values are copied directly.
|
||||
// For the others, we have to construct a map from the Node locations in the
|
||||
// new simplex to the parent non-simplex element. This could be sped up by
|
||||
// not repeatedly access the original FE as the accesses will be coherent
|
||||
// (i.e. all child elems are consecutive).
|
||||
|
||||
Array<int> edofs; // element dofs in new element
|
||||
Array<int> parent_vertices, child_vertices; // vertices of parent and child.
|
||||
Array<int> node_map; // node indices of parent from child.
|
||||
Vector edofvals; // values of elements dofs in original element
|
||||
// Storage for evaluating node function on parent element, at node locations
|
||||
// of child element
|
||||
DenseMatrix shape; // ndof_coarse x nnode_refined.
|
||||
DenseMatrix point_matrix; // sdim x nnode_refined
|
||||
IntegrationRule child_nodes_in_parent; // The parent nodes that correspond to the child nodes
|
||||
for (int i = 0; i < parent_elements.Size(); i++)
|
||||
{
|
||||
const int ip = parent_elements[i];
|
||||
const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(ip);
|
||||
orig_mesh.GetNodes()->GetElementDofValues(ip, edofvals);
|
||||
switch (orig_geom)
|
||||
{
|
||||
case Geometry::Type::SEGMENT : // fall through
|
||||
case Geometry::Type::TRIANGLE : // fall through
|
||||
case Geometry::Type::TETRAHEDRON :
|
||||
GetNodes()->FESpace()->GetElementVDofs(i, edofs);
|
||||
GetNodes()->SetSubVector(edofs, edofvals);
|
||||
break;
|
||||
case Geometry::Type::CUBE : // fall through
|
||||
case Geometry::Type::PRISM : // fall through
|
||||
case Geometry::Type::PYRAMID : // fall through
|
||||
case Geometry::Type::SQUARE :
|
||||
{
|
||||
// Extract the vertices of parent and child, can then form the
|
||||
// map from child reference coordinates to parent reference
|
||||
// coordinates. Exploit the fact that for Nodes, the vertex
|
||||
// entries come first, and their indexing matches the vertex
|
||||
// numbering. Thus we have already have an inverse index map.
|
||||
orig_mesh.GetElementVertices(ip, parent_vertices);
|
||||
GetElementVertices(i, child_vertices);
|
||||
node_map.SetSize(0);
|
||||
for (auto cv : child_vertices)
|
||||
for (int ipv = 0; ipv < parent_vertices.Size(); ipv++)
|
||||
if (cv == parent_vertices[ipv])
|
||||
{
|
||||
node_map.Append(ipv);
|
||||
break;
|
||||
}
|
||||
MFEM_ASSERT(node_map.Size() == Geometry::NumVerts[GetElementBaseGeometry(i)], "!");
|
||||
// node_map now says which of the parent vertex nodes map to each
|
||||
// of the child vertex nodes. Using this can build a basis in the
|
||||
// parent element from child Node values, exploit the linearity
|
||||
// to then transform all nodes.
|
||||
child_nodes_in_parent.SetSize(0);
|
||||
const auto *orig_FE = orig_mesh.GetNodes()->FESpace()->GetFE(ip);
|
||||
for (auto pn : node_map)
|
||||
{
|
||||
child_nodes_in_parent.Append(orig_FE->GetNodes()[pn]);
|
||||
}
|
||||
const auto *simplex_FE = GetNodes()->FESpace()->GetFE(i);
|
||||
shape.SetSize(orig_FE->GetDof(), simplex_FE->GetDof()); // One set of evaluations per simplex dof.
|
||||
Vector col;
|
||||
for (int j = 0; j < simplex_FE->GetNodes().Size(); j++)
|
||||
{
|
||||
const auto &simplex_node = simplex_FE->GetNodes()[j];
|
||||
IntegrationPoint simplex_node_in_orig;
|
||||
// Handle the 2D vs 3D case by multiplying .z by zero.
|
||||
simplex_node_in_orig.Set3(
|
||||
child_nodes_in_parent[0].x +
|
||||
simplex_node.x * (child_nodes_in_parent[1].x - child_nodes_in_parent[0].x)
|
||||
+ simplex_node.y * (child_nodes_in_parent[2].x - child_nodes_in_parent[0].x)
|
||||
+ simplex_node.z * (child_nodes_in_parent[(sdim > 2) ? 3 : 0].x - child_nodes_in_parent[0].x),
|
||||
child_nodes_in_parent[0].y +
|
||||
simplex_node.x * (child_nodes_in_parent[1].y - child_nodes_in_parent[0].y)
|
||||
+ simplex_node.y * (child_nodes_in_parent[2].y - child_nodes_in_parent[0].y)
|
||||
+ simplex_node.z * (child_nodes_in_parent[(sdim > 2) ? 3 : 0].y - child_nodes_in_parent[0].y),
|
||||
child_nodes_in_parent[0].z +
|
||||
simplex_node.x * (child_nodes_in_parent[1].z - child_nodes_in_parent[0].z)
|
||||
+ simplex_node.y * (child_nodes_in_parent[2].z - child_nodes_in_parent[0].z)
|
||||
+ simplex_node.z * (child_nodes_in_parent[(sdim > 2) ? 3 : 0].z - child_nodes_in_parent[0].z));
|
||||
shape.GetColumnReference(j, col);
|
||||
orig_FE->CalcShape(simplex_node_in_orig, col);
|
||||
}
|
||||
// All the non-simplex basis functions have now been evaluated at
|
||||
// all the simplex basis function node locations. Now evaluate
|
||||
// the summations and place back into the Nodes vector.
|
||||
orig_mesh.GetNodes()->GetElementDofValues(ip, edofvals);
|
||||
// Dof values are always returned as
|
||||
// [[x_1,x_2,x_3,...],
|
||||
// [y_1,y_2,y_3,...],
|
||||
// [z_1,z_2,z_3,...]]
|
||||
DenseMatrix edofvals_mat(edofvals.GetData(), orig_FE->GetDof(), sdim);
|
||||
point_matrix.SetSize(simplex_FE->GetDof(), sdim);
|
||||
MultAtB(shape, edofvals_mat, point_matrix);
|
||||
GetNodes()->FESpace()->GetElementVDofs(i, edofs);
|
||||
GetNodes()->SetSubVector(edofs, point_matrix.GetData());
|
||||
}
|
||||
break;
|
||||
case Geometry::Type::POINT : // fall through
|
||||
case Geometry::Type::INVALID :
|
||||
case Geometry::Type::NUM_GEOMETRIES :
|
||||
MFEM_ABORT("Internal Error!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mesh Mesh::MakePeriodic(const Mesh &orig_mesh, const std::vector<int> &v2v)
|
||||
{
|
||||
Mesh periodic_mesh(orig_mesh, true); // Make a copy of the original mesh
|
||||
@@ -14518,11 +14662,12 @@ MeshPartitioner::ExtractGridFunction(const MeshPart &mesh_part,
|
||||
for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
|
||||
{
|
||||
const int glob_elem_id = elem_list[loc_elem_id];
|
||||
auto glob_dt = global_gf.FESpace()->GetElementVDofs(glob_elem_id, gvdofs);
|
||||
DofTransformation glob_dt, local_dt;
|
||||
global_gf.FESpace()->GetElementVDofs(glob_elem_id, gvdofs, glob_dt);
|
||||
global_gf.GetSubVector(gvdofs, loc_vals);
|
||||
if (glob_dt) { glob_dt->InvTransformPrimal(loc_vals); }
|
||||
auto local_dt = local_fespace.GetElementVDofs(loc_elem_id, lvdofs);
|
||||
if (local_dt) { local_dt->TransformPrimal(loc_vals); }
|
||||
glob_dt.InvTransformPrimal(loc_vals);
|
||||
local_fespace.GetElementVDofs(loc_elem_id, lvdofs, local_dt);
|
||||
local_dt.TransformPrimal(loc_vals);
|
||||
local_gf->SetSubVector(lvdofs, loc_vals);
|
||||
}
|
||||
return local_gf;
|
||||
|
||||
+27
-1
@@ -662,8 +662,34 @@ protected:
|
||||
Array<int> &elem_vtx, Array<int> &attr) const;
|
||||
|
||||
// Internal helper used in MakeSimplicial (and ParMesh::MakeSimplicial).
|
||||
void MakeSimplicial_(const Mesh &orig_mesh, int *vglobal);
|
||||
|
||||
/**
|
||||
* @brief Internal helper user in MakeSimplicial (and
|
||||
* ParMesh::MakeSimplicial). Optional return is used in assembling a higher
|
||||
* order mesh.
|
||||
* @details The construction of the higher order nodes must be separated out
|
||||
* because the
|
||||
*
|
||||
* @param orig_mesh The mesh from to create the simplices
|
||||
* @param vglobal An optional global ordering of vertices. Necessary for
|
||||
* parallel splitting.
|
||||
* @return Array<int> parent elements from the orig_mesh for each split
|
||||
* element
|
||||
*/
|
||||
Array<int> MakeSimplicial_(const Mesh &orig_mesh, int *vglobal);
|
||||
|
||||
/**
|
||||
* @brief Helper function for constructing higher order nodes from a mesh
|
||||
* transformed into simplices. Only to be called as part of MakeSimplicial
|
||||
* or ParMesh::MakeSimplicial.
|
||||
*
|
||||
* @param orig_mesh The mesh that was used to transform this mesh into
|
||||
* simplices.
|
||||
* @param parent_elements parent_elements[i] gives the element in orig_mesh
|
||||
* split to give element i.
|
||||
*/
|
||||
void MakeHigherOrderSimplicial_(const Mesh &orig_mesh,
|
||||
const Array<int> &parent_elements);
|
||||
public:
|
||||
|
||||
/// @anchor mfem_Mesh_ctors
|
||||
|
||||
+6
-1
@@ -1400,7 +1400,7 @@ ParMesh ParMesh::MakeSimplicial(ParMesh &orig_mesh)
|
||||
{
|
||||
vglobal[iv] = fes.GetGlobalTDofNumber(iv);
|
||||
}
|
||||
mesh.MakeSimplicial_(orig_mesh, vglobal);
|
||||
auto parent_elements = mesh.MakeSimplicial_(orig_mesh, vglobal);
|
||||
|
||||
// count the number of entries in each row of group_s{vert,edge,face}
|
||||
mesh.group_svert.MakeI(mesh.GetNGroups()-1); // exclude the local group 0
|
||||
@@ -1517,6 +1517,11 @@ ParMesh ParMesh::MakeSimplicial(ParMesh &orig_mesh)
|
||||
|
||||
mesh.FinalizeParTopo();
|
||||
|
||||
if (orig_mesh.GetNodes() != nullptr)
|
||||
{
|
||||
mesh.MakeHigherOrderSimplicial_(orig_mesh, parent_elements);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,3 +37,4 @@ add_subdirectory(tribol)
|
||||
add_subdirectory(hooke)
|
||||
add_subdirectory(dpg)
|
||||
add_subdirectory(hdiv-linear-solver)
|
||||
add_subdirectory(dfem)
|
||||
|
||||
@@ -438,7 +438,7 @@ namespace mfem
|
||||
namespace ad
|
||||
{
|
||||
/// MFEM native forward AD-type
|
||||
typedef internal::dual<real_t, real_t> ADFloatType;
|
||||
typedef future::dual<real_t, real_t> ADFloatType;
|
||||
/// Vector type for AD-type numbers
|
||||
typedef TAutoDiffVector<ADFloatType> ADVectorType;
|
||||
/// Matrix type for AD-type numbers
|
||||
@@ -532,7 +532,7 @@ class QVectorFuncAutoDiff
|
||||
{
|
||||
private:
|
||||
/// MFEM native forward AD-type
|
||||
typedef internal::dual<real_t, real_t> ADFType;
|
||||
typedef future::dual<real_t, real_t> ADFType;
|
||||
/// Vector type for AD-type numbers
|
||||
typedef TAutoDiffVector<ADFType> ADFVector;
|
||||
/// Matrix type for AD-type numbers
|
||||
@@ -617,13 +617,13 @@ class QFunctionAutoDiff
|
||||
{
|
||||
private:
|
||||
/// MFEM native AD-type for first derivatives
|
||||
typedef internal::dual<real_t, real_t> ADFType;
|
||||
typedef future::dual<real_t, real_t> ADFType;
|
||||
/// Vector type for AD-numbers(first derivatives)
|
||||
typedef TAutoDiffVector<ADFType> ADFVector;
|
||||
/// Matrix type for AD-numbers(first derivatives)
|
||||
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
|
||||
/// MFEM native AD-type for second derivatives
|
||||
typedef internal::dual<ADFType, ADFType> ADSType;
|
||||
typedef future::dual<ADFType, ADFType> ADSType;
|
||||
/// Vector type for AD-numbers (second derivatives)
|
||||
typedef TAutoDiffVector<ADSType> ADSVector;
|
||||
/// Vector type for AD-numbers (second derivatives)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
add_mfem_miniapp(dfem-minimal-surface
|
||||
MAIN dfem-minimal-surface.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
# Add parallel tests.
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME dfem-minimal-surface=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS} $<TARGET_FILE:dfem-minimal-surface> -der 0 -o 1 -r 2 -no-vis
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,566 @@
|
||||
// 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.
|
||||
//
|
||||
// -------------------------------------
|
||||
// Minimal Surface 2D Problem with dFEM
|
||||
// -------------------------------------
|
||||
//
|
||||
// Compile with: make dfem-minimal-surface
|
||||
//
|
||||
// Sample runs: mpirun -np 4 dfem-minimal-surface -der 0
|
||||
// mpirun -np 4 dfem-minimal-surface -der 0 -o 2
|
||||
// mpirun -np 4 dfem-minimal-surface -der 0 -r 1
|
||||
// mpirun -np 4 dfem-minimal-surface -der 1
|
||||
// mpirun -np 4 dfem-minimal-surface -der 2
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 dfem-minimal-surface -der 0 -r 1 -o 2 -d cuda
|
||||
// mpirun -np 4 dfem-minimal-surface -der 1 -r 1 -o 2 -d cuda
|
||||
// * mpirun -np 4 dfem-minimal-surface -der 0 -r 1 -o 2 -d hip
|
||||
// * mpirun -np 4 dfem-minimal-surface -der 1 -r 1 -o 2 -d hip
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve the
|
||||
// minimal surface problem in 2D:
|
||||
//
|
||||
// $ \min \left( -\nabla \cdot (1 / \sqrt(1 + |\nabla u|^2) \nabla u) \right) $
|
||||
//
|
||||
// with Dirichlet boundary conditions. The nonlinear problem is
|
||||
// solved using Newton's method, where the necessary derivatives
|
||||
// are computed in one of three ways (controlled by -der command
|
||||
// line parameter):
|
||||
//
|
||||
// -der 0 = Automatic differentiation using Enzyme or dual type
|
||||
// (default)
|
||||
// -der 1 = Hand-coded derivatives
|
||||
// -der 2 = Finite differences
|
||||
//
|
||||
// The example demonstrates the use of MFEM's nonlinear solvers,
|
||||
// automatic differentiation capabilities, and GLVis/ParaView
|
||||
// visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
// This example code demonstrates the use of new features in MFEM that are in
|
||||
// development but exposed through the mfem::future namespace. All features
|
||||
// under this namespace might change their interface or behavior in upcoming
|
||||
// releases until they have stabilized.
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
// Derivative type enum
|
||||
// This enum is used to specify the type of derivative computation.
|
||||
// Possibilities are:
|
||||
// - AUTODIFF, which uses automatic differentiation (Enzyme or dual type),
|
||||
// - HANDCODED, which uses a manually implemented derivative, and
|
||||
// - FD, finite difference.
|
||||
enum DerivativeType
|
||||
{
|
||||
AUTODIFF,
|
||||
HANDCODED,
|
||||
FD
|
||||
};
|
||||
|
||||
// Minimal surface operator.
|
||||
//
|
||||
// This class implements the minimal surface equation, which is a nonlinear
|
||||
// operator that provides the residual.
|
||||
template <typename dscalar_t, int dim = 2>
|
||||
class MinimalSurface : public Operator
|
||||
{
|
||||
private:
|
||||
static constexpr int SOLUTION_U = 1;
|
||||
static constexpr int MESH_NODES = 2;
|
||||
|
||||
template <typename T>
|
||||
MFEM_HOST_DEVICE inline
|
||||
static auto coeff(const tensor<T, dim> &a)
|
||||
{
|
||||
return 1_r / sqrt(1_r + sqnorm(a));
|
||||
}
|
||||
|
||||
// The 'nvcc' compiler needs MFApply and ManualDerivativeApply to be public.
|
||||
public:
|
||||
// Matrix-Free version of the pointwise residual form for the minimal
|
||||
// surface equation.
|
||||
struct MFApply
|
||||
{
|
||||
// Using DifferentiableOperator, we can define the residual form as a
|
||||
// matrix-free operation. This allows us to compute the residual without
|
||||
// explicitly forming any matrices or other large, temporary data
|
||||
// structures.
|
||||
//
|
||||
// The inputs are the gradient of the solution in *reference coordinates*,
|
||||
// the Jacobian of the coordinates, and the integration rule weights.
|
||||
//
|
||||
// The output is the residual in *physical coordinates* which also
|
||||
// includes the necessary transformation from reference to physical
|
||||
// coordinates for the gradient of the test function.
|
||||
//
|
||||
// Due to the description of how this pointwise operation is used in
|
||||
// DifferentiableOperator, we know it is applied to the gradient of the
|
||||
// test function in reference coordinates e.g.
|
||||
// $ \int coeff(\nabla_x u) (\nabla_x u) J^{-T} \det(J) w
|
||||
// (\nabla_{\xi} v) d\xi $
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto operator()(
|
||||
const tensor<dscalar_t, dim> &dudxi,
|
||||
const tensor<real_t, dim, dim> &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
const auto invJ = inv(J);
|
||||
const auto dudx = dudxi * invJ;
|
||||
return tuple{coeff(dudx) * dudx * transpose(invJ) * det(J) * w};
|
||||
}
|
||||
};
|
||||
|
||||
// This is the derivative of the residual form with respect to the
|
||||
// solution $u$.
|
||||
//
|
||||
// The inputs and outputs follow the same rules as the MFApply operator.
|
||||
struct ManualDerivativeApply
|
||||
{
|
||||
MFEM_HOST_DEVICE inline
|
||||
auto operator()(
|
||||
const tensor<real_t, dim> &ddelta_udxi,
|
||||
const tensor<real_t, dim> &dudxi,
|
||||
const tensor<real_t, dim, dim> &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
const auto invJ = inv(J);
|
||||
const auto dudx = dudxi * invJ;
|
||||
const auto ddelta_udx = ddelta_udxi * invJ;
|
||||
|
||||
const auto c = coeff(dudx);
|
||||
const auto term1 = c * ddelta_udx;
|
||||
const auto term2 = c * c * c * dot(dudx, ddelta_udx) * dudx;
|
||||
|
||||
return tuple{(term1 - term2) * transpose(invJ) * det(J) * w};
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// This class implements the Jacobian of the minimal surface operator. It
|
||||
// mostly acts as a wrapper to retrieve the Jacobian and apply essential
|
||||
// boundary conditions appropriately.
|
||||
class MinimalSurfaceJacobian : public Operator
|
||||
{
|
||||
public:
|
||||
MinimalSurfaceJacobian(const MinimalSurface *minsurface,
|
||||
const Vector &x) :
|
||||
Operator(minsurface->Height()),
|
||||
minsurface(minsurface),
|
||||
z(minsurface->Height())
|
||||
{
|
||||
minsurface->u.SetFromTrueDofs(x);
|
||||
auto mesh_nodes = static_cast<ParGridFunction*>
|
||||
(minsurface->H1.GetParMesh()->GetNodes());
|
||||
|
||||
// One can retrieve the derivative of a DifferentiableOperator wrt a
|
||||
// field variable if the derivative has been requested during the
|
||||
// DifferentiableOperator::AddDomainIntegrator call.
|
||||
dres_du = minsurface->res->GetDerivative(
|
||||
SOLUTION_U, {&minsurface->u}, {mesh_nodes});
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
z = x;
|
||||
z.SetSubVector(minsurface->ess_tdofs, 0.0);
|
||||
|
||||
dres_du->Mult(z, y);
|
||||
|
||||
auto d_y = y.HostReadWrite();
|
||||
const auto d_x = x.HostRead();
|
||||
for (int i = 0; i < minsurface->ess_tdofs.Size(); i++)
|
||||
{
|
||||
d_y[minsurface->ess_tdofs[i]] = d_x[minsurface->ess_tdofs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// Pointer to the wrapped MinimalSurface operator
|
||||
const MinimalSurface *minsurface = nullptr;
|
||||
|
||||
// Pointer to the DifferentiableOperator that computes the Jacobian
|
||||
std::shared_ptr<DerivativeOperator> dres_du;
|
||||
|
||||
// Temporary vector
|
||||
mutable Vector z;
|
||||
};
|
||||
|
||||
// This class implements the Jacobian of the minimal surface operator using
|
||||
// manually computed derivatives.
|
||||
class MinimalSurfaceHandcodedJacobian : public Operator
|
||||
{
|
||||
// For the Jacobian action we need another field ID for the direction
|
||||
// of u, called du, in dR/du = J * du.
|
||||
static constexpr int DIRECTION_U = 3;
|
||||
|
||||
public:
|
||||
MinimalSurfaceHandcodedJacobian(const MinimalSurface *minsurface,
|
||||
const Vector &x) :
|
||||
Operator(minsurface->Height()),
|
||||
minsurface(minsurface),
|
||||
z(minsurface->Height())
|
||||
{
|
||||
Array<int> all_domain_attr(minsurface->H1.GetMesh()->attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
|
||||
auto &mesh_nodes = *static_cast<ParGridFunction *>
|
||||
(minsurface->H1.GetParMesh()->GetNodes());
|
||||
auto &mesh_nodes_fes = *mesh_nodes.ParFESpace();
|
||||
|
||||
std::vector<FieldDescriptor> solutions =
|
||||
{
|
||||
{DIRECTION_U, &minsurface->H1}
|
||||
};
|
||||
std::vector<FieldDescriptor> parameters =
|
||||
{
|
||||
{SOLUTION_U, &minsurface->H1},
|
||||
{MESH_NODES, &mesh_nodes_fes}
|
||||
};
|
||||
|
||||
dres_du = std::make_shared<DifferentiableOperator>(
|
||||
solutions, parameters, *minsurface->H1.GetParMesh());
|
||||
|
||||
auto input_operators = tuple
|
||||
{
|
||||
Gradient<DIRECTION_U>{},
|
||||
Gradient<SOLUTION_U>{},
|
||||
Gradient<MESH_NODES>{},
|
||||
Weight{}
|
||||
};
|
||||
|
||||
auto output_operators = tuple
|
||||
{
|
||||
Gradient<SOLUTION_U>{}
|
||||
};
|
||||
|
||||
ManualDerivativeApply manual_derivative_apply;
|
||||
dres_du->AddDomainIntegrator(manual_derivative_apply, input_operators,
|
||||
output_operators, minsurface->ir,
|
||||
all_domain_attr);
|
||||
|
||||
minsurface->u.SetFromTrueDofs(x);
|
||||
dres_du->SetParameters({&minsurface->u, &mesh_nodes});
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
z = x;
|
||||
z.SetSubVector(minsurface->ess_tdofs, 0.0);
|
||||
|
||||
dres_du->Mult(z, y);
|
||||
|
||||
auto d_y = y.HostReadWrite();
|
||||
const auto d_x = x.HostRead();
|
||||
for (int i = 0; i < minsurface->ess_tdofs.Size(); i++)
|
||||
{
|
||||
d_y[minsurface->ess_tdofs[i]] = d_x[minsurface->ess_tdofs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
const MinimalSurface *minsurface = nullptr;
|
||||
std::shared_ptr<DifferentiableOperator> dres_du;
|
||||
mutable Vector z;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
MinimalSurface(ParFiniteElementSpace &H1,
|
||||
const IntegrationRule &ir,
|
||||
int deriv_type = AUTODIFF) :
|
||||
Operator(H1.GetTrueVSize(), H1.GetTrueVSize()),
|
||||
H1(H1),
|
||||
ir(ir),
|
||||
u(&H1),
|
||||
derivative_type(deriv_type)
|
||||
{
|
||||
Array<int> all_domain_attr(H1.GetMesh()->attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
|
||||
auto &mesh_nodes =
|
||||
*static_cast<ParGridFunction *>(H1.GetParMesh()->GetNodes());
|
||||
auto &mesh_nodes_fes = *mesh_nodes.ParFESpace();
|
||||
|
||||
// The following section is the heart of this example. It shows how to
|
||||
// create and interact with the DifferentialOperator class.
|
||||
|
||||
// The constructor of DifferentiableOperator takes two vectors of
|
||||
// FieldDescriptors. A FieldDescriptor can be viewed as a a pair of an
|
||||
// identifier (the field ID) and it's accompanying space.
|
||||
std::vector<FieldDescriptor> solutions;
|
||||
solutions.push_back(FieldDescriptor(SOLUTION_U, &H1));
|
||||
std::vector<FieldDescriptor> parameters;
|
||||
parameters.push_back(FieldDescriptor(MESH_NODES, &mesh_nodes_fes));
|
||||
|
||||
// Create the DifferentiableOperator on the desired mesh.
|
||||
res = std::make_shared<DifferentiableOperator>(
|
||||
solutions, parameters, *H1.GetParMesh());
|
||||
|
||||
// DifferentiableOperator::AddIntegrator consists mainly of multiple
|
||||
// components. The input and output operators and the pointwise
|
||||
// "quadrature function" form a description of how the inputs and outputs
|
||||
// to the pointwise function have to be treated.
|
||||
|
||||
// The input operators tuple consists of derived FieldOperator types.
|
||||
// Here, we use Gradient<FIELD_ID> to signal that we request the gradient
|
||||
// on the reference coordinates of the FIELD_ID field to be interpolated
|
||||
// and translated to the pointwise function as the first and second input.
|
||||
// Other choices are possible, e.g. Value<FIELD_ID> to interpolate the
|
||||
// pointwise funciton. `Weight` is a special field that translates the
|
||||
// integration rule weights to the input of the pointwise function.
|
||||
auto input_operators = tuple
|
||||
{
|
||||
Gradient<SOLUTION_U>{},
|
||||
Gradient<MESH_NODES>{},
|
||||
Weight{}
|
||||
};
|
||||
|
||||
// The output operators tuple also consists of derived FieldOperator
|
||||
// types. Currently, only _one_ output operator is allowed. One should
|
||||
// think of this as an operator on the output of a pointwise function. For
|
||||
// example with the above input operators and the output operator below we
|
||||
// create the following operator sequence:
|
||||
//
|
||||
// $ B^T D(B u, B x, w) $
|
||||
//
|
||||
// where B is the gradient interpolation operator, D is the pointwise
|
||||
// function and u and x are solution and coordinate functions,
|
||||
// respectively. The output operator is the gradient of the basis of the
|
||||
// solution, which completes the "diffusion" like weak form.
|
||||
auto output_operators = tuple
|
||||
{
|
||||
Gradient<SOLUTION_U>{}
|
||||
};
|
||||
|
||||
// The pointwise function is defined as a lambda function. Here we just
|
||||
// instantiate an object for it which is passed to
|
||||
// DifferentiableOperator::AddDomainIntegrator.
|
||||
MFApply mf_apply_qf;
|
||||
|
||||
// The integeger sequence is used to specify which derivatives of the
|
||||
// formed integrator should be formed. This is necessary to specify at
|
||||
// compile time in order to instantiate the correct functions.
|
||||
auto derivatives = std::integer_sequence<size_t, SOLUTION_U> {};
|
||||
res->AddDomainIntegrator(mf_apply_qf, input_operators, output_operators,
|
||||
ir, all_domain_attr, derivatives);
|
||||
|
||||
// Before we are able to use DifferentiableOperator::Mult, we need to call
|
||||
// DifferentiableOperator::SetParameters to set the parameters of the
|
||||
// operator. Here, only the mesh node function is required. We do this
|
||||
// here once, because we know that the nodes won't change. If they do,
|
||||
// we'd have to call SetParameters before each call to Mult. This is done
|
||||
// to be mathematically consistent with fixing paramaters.
|
||||
res->SetParameters({&mesh_nodes});
|
||||
|
||||
Array<int> ess_bdr(H1.GetParMesh()->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
H1.GetEssentialTrueDofs(ess_bdr, ess_tdofs);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
res->Mult(x, y);
|
||||
y.SetSubVector(ess_tdofs, 0.0);
|
||||
}
|
||||
|
||||
Operator& GetGradient(const Vector &x) const override
|
||||
{
|
||||
switch (derivative_type)
|
||||
{
|
||||
case FD:
|
||||
fd_jac = std::make_shared<FDJacobian>(*this, x);
|
||||
return *fd_jac;
|
||||
|
||||
case HANDCODED:
|
||||
{
|
||||
man_dres_du = std::make_shared<MinimalSurfaceHandcodedJacobian>(
|
||||
this, x);
|
||||
return *man_dres_du;
|
||||
}
|
||||
|
||||
case AUTODIFF:
|
||||
default:
|
||||
dres_du = std::make_shared<MinimalSurfaceJacobian>(this, x);
|
||||
return *dres_du;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ParFiniteElementSpace &H1;
|
||||
const IntegrationRule &ir;
|
||||
|
||||
mutable ParGridFunction u;
|
||||
|
||||
Array<int> ess_tdofs;
|
||||
|
||||
std::shared_ptr<DifferentiableOperator> res;
|
||||
mutable std::shared_ptr<MinimalSurfaceJacobian> dres_du;
|
||||
mutable std::shared_ptr<MinimalSurfaceHandcodedJacobian> man_dres_du;
|
||||
mutable std::shared_ptr<FDJacobian> fd_jac;
|
||||
int derivative_type;
|
||||
};
|
||||
|
||||
// Boundary function for the minimal surface problem described by the Scherk
|
||||
// surface.
|
||||
// See https://en.wikipedia.org/wiki/Scherk_surface for more details.
|
||||
real_t boundary_func(const Vector &coords)
|
||||
{
|
||||
const real_t x = coords(0);
|
||||
const real_t y = coords(1);
|
||||
if (coords.Size() == 3)
|
||||
{
|
||||
MFEM_ABORT("internal error");
|
||||
}
|
||||
const real_t a = 1.0e-2;
|
||||
return log(cos(a * x) / cos(a * y)) / a;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE
|
||||
Mpi::Init();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options
|
||||
int order = 1;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
int refinements = 0;
|
||||
int derivative_type = AUTODIFF;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&refinements, "-r", "--refinements", "");
|
||||
args.AddOption(&derivative_type, "-der", "--derivative-type",
|
||||
"Derivative computation type: 0=AutomaticDifferentiation,"
|
||||
" 1=HandCoded, 2=FiniteDifference");
|
||||
|
||||
args.ParseCheck();
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA
|
||||
Device device(device_config);
|
||||
if (Mpi::Root()) { device.Print(); }
|
||||
|
||||
// 4. Create a 2D mesh on the square domain [-π/2,π/2]^2
|
||||
Mesh mesh = Mesh::MakeCartesian2D(4, 4, Element::QUADRILATERAL);
|
||||
mesh.SetCurvature(order);
|
||||
|
||||
auto transform_mesh = [](const Vector &cold, Vector &cnew)
|
||||
{
|
||||
cnew = cold;
|
||||
cnew -= 0.5;
|
||||
cnew *= M_PI;
|
||||
};
|
||||
|
||||
mesh.Transform(transform_mesh);
|
||||
|
||||
// 5. Refine the mesh to increase the resolution
|
||||
for (int i = 0; i < refinements; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 6. Define a parallel mesh
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh
|
||||
H1_FECollection fec(order, dim);
|
||||
ParFiniteElementSpace H1(&pmesh, &fec);
|
||||
|
||||
// 8. Set up the integration rule
|
||||
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(),
|
||||
2 * order + 1);
|
||||
|
||||
ParGridFunction u(&H1);
|
||||
Vector X(H1.GetTrueVSize());
|
||||
|
||||
// 9. Create the nonlinear operator for the minimal surface equation
|
||||
std::unique_ptr<Operator> minsurface;
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
// When Enzyme is available, use it for automatic differentiation
|
||||
minsurface = std::make_unique<MinimalSurface<real_t>>(H1, *ir,
|
||||
derivative_type);
|
||||
#else
|
||||
// When Enzyme is not available, use the dual type for automatic
|
||||
// differentiation
|
||||
using mfem::future::dual;
|
||||
using dual_t = dual<real_t, real_t>;
|
||||
minsurface = std::make_unique<MinimalSurface<dual_t>>(H1, *ir,
|
||||
derivative_type);
|
||||
#endif
|
||||
|
||||
// 10. Set up and apply the boundary conditions
|
||||
Array<int> ess_bdr(H1.GetParMesh()->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
|
||||
// 11. Set up the essential boundary conditions and initial condition
|
||||
FunctionCoefficient boundary_coeff(boundary_func);
|
||||
u.ProjectCoefficient(boundary_coeff);
|
||||
u *= 1e-2;
|
||||
u.ProjectBdrCoefficient(boundary_coeff, ess_bdr);
|
||||
|
||||
// 12. Set up the linear solver to be used within Newton's method
|
||||
CGSolver krylov(MPI_COMM_WORLD);
|
||||
krylov.SetAbsTol(0.0);
|
||||
krylov.SetRelTol(1e-4);
|
||||
krylov.SetMaxIter(500);
|
||||
krylov.SetPrintLevel(2);
|
||||
|
||||
// 13. Set up the nonlinear solver (Newton) for the minimal surface equation
|
||||
NewtonSolver newton(MPI_COMM_WORLD);
|
||||
newton.SetOperator(*minsurface);
|
||||
newton.SetAbsTol(0.0);
|
||||
newton.SetRelTol(1e-6);
|
||||
newton.SetMaxIter(10);
|
||||
newton.SetSolver(krylov);
|
||||
newton.SetPrintLevel(1);
|
||||
|
||||
// 14. Solve the nonlinear system using Newton's method
|
||||
H1.GetRestrictionMatrix()->Mult(u, X);
|
||||
Vector zero;
|
||||
newton.Mult(zero, X);
|
||||
H1.GetProlongationMatrix()->Mult(X, u);
|
||||
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel "
|
||||
<< Mpi::WorldSize() << " " << Mpi::WorldRank() << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << u << std::flush;
|
||||
}
|
||||
|
||||
// 16. Save the solution in parallel using ParaView format
|
||||
ParaViewDataCollection dc("dfem-minimal-surface-output", &pmesh);
|
||||
dc.SetHighOrderOutput(true);
|
||||
dc.SetLevelsOfDetail(order);
|
||||
dc.RegisterField("solution", &u);
|
||||
dc.SetCycle(0);
|
||||
dc.Save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
# LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
#
|
||||
# This file is part of the MFEM library. For more information and source code
|
||||
# availability visit https://mfem.org.
|
||||
#
|
||||
# MFEM is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
MFEM_INSTALL_DIR ?= ../../mfem
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/dfem/,)
|
||||
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES =
|
||||
PAR_EXAMPLES = dfem-minimal-surface
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -rf dfem-minimal-surface-output
|
||||
@@ -79,7 +79,7 @@ enum prob_type
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
Vector beta;
|
||||
Vector beta_;
|
||||
real_t epsilon;
|
||||
|
||||
real_t exact_u(const Vector & X);
|
||||
@@ -120,7 +120,7 @@ int main(int argc, char *argv[])
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: manufactured, 1: Erickson-Johnson ");
|
||||
args.AddOption(&beta, "-beta", "--beta",
|
||||
args.AddOption(&beta_, "-beta", "--beta",
|
||||
"Vector Coefficient beta");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
@@ -147,11 +147,11 @@ int main(int argc, char *argv[])
|
||||
int dim = mesh.Dimension();
|
||||
MFEM_VERIFY(dim > 1, "Dimension = 1 is not supported in this example");
|
||||
|
||||
if (beta.Size() == 0)
|
||||
if (beta_.Size() == 0)
|
||||
{
|
||||
beta.SetSize(dim);
|
||||
beta = 0.0;
|
||||
beta[0] = 1.;
|
||||
beta_.SetSize(dim);
|
||||
beta_ = 0.0;
|
||||
beta_[0] = 1.;
|
||||
}
|
||||
|
||||
args.PrintOptions(std::cout);
|
||||
@@ -199,10 +199,10 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient eps2(1/(epsilon*epsilon));
|
||||
|
||||
ConstantCoefficient negeps(-epsilon);
|
||||
VectorConstantCoefficient betacoeff(beta);
|
||||
Vector negbeta = beta; negbeta.Neg();
|
||||
DenseMatrix bbt(beta.Size());
|
||||
MultVVt(beta, bbt);
|
||||
VectorConstantCoefficient betacoeff(beta_);
|
||||
Vector negbeta = beta_; negbeta.Neg();
|
||||
DenseMatrix bbt(beta_.Size());
|
||||
MultVVt(beta_, bbt);
|
||||
MatrixConstantCoefficient bbtcoeff(bbt);
|
||||
VectorConstantCoefficient negbetacoeff(negbeta);
|
||||
|
||||
@@ -598,7 +598,7 @@ void exact_hatf(const Vector & X, Vector & hatf)
|
||||
hatf.SetSize(X.Size());
|
||||
for (int i = 0; i<hatf.Size(); i++)
|
||||
{
|
||||
hatf[i] = beta[i] * u - sigma[i];
|
||||
hatf[i] = beta_[i] * u - sigma[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +612,7 @@ real_t f_exact(const Vector & X)
|
||||
real_t s = 0;
|
||||
for (int i = 0; i<du.Size(); i++)
|
||||
{
|
||||
s += beta[i] * du[i];
|
||||
s += beta_[i] * du[i];
|
||||
}
|
||||
return -epsilon * d2u + s;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ static const char *enum_str[] =
|
||||
};
|
||||
|
||||
prob_type prob;
|
||||
Vector beta;
|
||||
Vector beta_;
|
||||
real_t epsilon;
|
||||
|
||||
real_t exact_u(const Vector & X);
|
||||
@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
|
||||
"Theta parameter for AMR");
|
||||
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
|
||||
" 0: lshape, 1: General");
|
||||
args.AddOption(&beta, "-beta", "--beta",
|
||||
args.AddOption(&beta_, "-beta", "--beta",
|
||||
"Vector Coefficient beta");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
@@ -181,19 +181,19 @@ int main(int argc, char *argv[])
|
||||
case sinusoidal:
|
||||
case EJ:
|
||||
{
|
||||
if (beta.Size() == 0)
|
||||
if (beta_.Size() == 0)
|
||||
{
|
||||
beta.SetSize(dim);
|
||||
beta = 0.0;
|
||||
beta[0] = 1.;
|
||||
beta_.SetSize(dim);
|
||||
beta_ = 0.0;
|
||||
beta_[0] = 1.;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case bdr_layer:
|
||||
{
|
||||
beta.SetSize(dim);
|
||||
beta[0] = 1.;
|
||||
beta[1] = 2.;
|
||||
beta_.SetSize(dim);
|
||||
beta_[0] = 1.;
|
||||
beta_[1] = 2.;
|
||||
exact_known = false;
|
||||
}
|
||||
break;
|
||||
@@ -846,7 +846,7 @@ void beta_function(const Vector & X, Vector & beta_val)
|
||||
}
|
||||
else
|
||||
{
|
||||
beta_val = beta;
|
||||
beta_val = beta_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ void BlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
}
|
||||
|
||||
// Assemble global mat and rhs
|
||||
DofTransformation * doftrans_i, *doftrans_j;
|
||||
DofTransformation doftrans_i, doftrans_j;
|
||||
|
||||
Array<int> faces, ori;
|
||||
int dim = mesh->Dimension();
|
||||
@@ -459,7 +459,7 @@ void BlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
Array<int> vdofs_i;
|
||||
doftrans_i = nullptr;
|
||||
doftrans_i.SetDofTransformation(nullptr);
|
||||
if (IsTraceSpace[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
@@ -472,14 +472,14 @@ void BlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_i = tr_fes[i]->GetElementVDofs(el, vdofs_i);
|
||||
tr_fes[i]->GetElementVDofs(el, vdofs_i, doftrans_i);
|
||||
}
|
||||
int skip_j=0;
|
||||
for (int j = 0; j<tr_fes.Size(); j++)
|
||||
{
|
||||
if (!tr_fes[j]) { continue; }
|
||||
Array<int> vdofs_j;
|
||||
doftrans_j = nullptr;
|
||||
doftrans_j.SetDofTransformation(nullptr);
|
||||
|
||||
if (IsTraceSpace[j])
|
||||
{
|
||||
@@ -493,16 +493,13 @@ void BlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_j = tr_fes[j]->GetElementVDofs(el, vdofs_j);
|
||||
tr_fes[j]->GetElementVDofs(el, vdofs_j, doftrans_j);
|
||||
}
|
||||
|
||||
DenseMatrix Ae;
|
||||
rmatptr->GetSubMatrix(offsets[i],offsets[i+1],
|
||||
offsets[j],offsets[j+1], Ae);
|
||||
if (doftrans_i || doftrans_j)
|
||||
{
|
||||
TransformDual(doftrans_i, doftrans_j, Ae);
|
||||
}
|
||||
TransformDual(doftrans_i, doftrans_j, Ae);
|
||||
S->GetBlock(skip_i,skip_j).AddSubMatrix(vdofs_i,vdofs_j, Ae);
|
||||
skip_j++;
|
||||
}
|
||||
@@ -513,10 +510,7 @@ void BlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
// ref subvector
|
||||
vec1.SetDataAndSize(&data[offsets[i]],
|
||||
offsets[i+1]-offsets[i]);
|
||||
if (doftrans_i)
|
||||
{
|
||||
doftrans_i->TransformDual(vec1);
|
||||
}
|
||||
doftrans_i.TransformDual(vec1);
|
||||
y->GetBlock(skip_i).AddElementVector(vdofs_i,vec1);
|
||||
skip_i++;
|
||||
}
|
||||
|
||||
@@ -470,8 +470,7 @@ void ComplexBlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
}
|
||||
|
||||
// Assemble global mat and rhs
|
||||
DofTransformation * doftrans_i, *doftrans_j;
|
||||
|
||||
DofTransformation doftrans_i, doftrans_j;
|
||||
|
||||
Array<int> faces, ori;
|
||||
int dim = mesh->Dimension();
|
||||
@@ -499,7 +498,7 @@ void ComplexBlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
{
|
||||
if (!tr_fes[i]) { continue; }
|
||||
Array<int> vdofs_i;
|
||||
doftrans_i = nullptr;
|
||||
doftrans_i.SetDofTransformation(nullptr);
|
||||
if (IsTraceSpace[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
@@ -512,14 +511,14 @@ void ComplexBlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_i = tr_fes[i]->GetElementVDofs(el, vdofs_i);
|
||||
tr_fes[i]->GetElementVDofs(el, vdofs_i, doftrans_i);
|
||||
}
|
||||
int skip_j=0;
|
||||
for (int j = 0; j<tr_fes.Size(); j++)
|
||||
{
|
||||
if (!tr_fes[j]) { continue; }
|
||||
Array<int> vdofs_j;
|
||||
doftrans_j = nullptr;
|
||||
doftrans_j.SetDofTransformation(nullptr);
|
||||
|
||||
if (IsTraceSpace[j])
|
||||
{
|
||||
@@ -533,7 +532,7 @@ void ComplexBlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_j = tr_fes[j]->GetElementVDofs(el, vdofs_j);
|
||||
tr_fes[j]->GetElementVDofs(el, vdofs_j, doftrans_j);
|
||||
}
|
||||
|
||||
DenseMatrix Ae_r, Ae_i;
|
||||
@@ -541,11 +540,8 @@ void ComplexBlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
offsets[j],offsets[j+1], Ae_r);
|
||||
rmat->imag().GetSubMatrix(offsets[i],offsets[i+1],
|
||||
offsets[j],offsets[j+1], Ae_i);
|
||||
if (doftrans_i || doftrans_j)
|
||||
{
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_r);
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_i);
|
||||
}
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_r);
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_i);
|
||||
S_r->GetBlock(skip_i,skip_j).AddSubMatrix(vdofs_i,vdofs_j, Ae_r);
|
||||
S_i->GetBlock(skip_i,skip_j).AddSubMatrix(vdofs_i,vdofs_j, Ae_i);
|
||||
skip_j++;
|
||||
@@ -555,11 +551,8 @@ void ComplexBlockStaticCondensation::AssembleReducedSystem(int el,
|
||||
Vector vec1_r(*rvecptr_real, offsets[i], offsets[i+1]-offsets[i]);
|
||||
Vector vec1_i(*rvecptr_imag, offsets[i], offsets[i+1]-offsets[i]);
|
||||
// ref subvector
|
||||
if (doftrans_i)
|
||||
{
|
||||
doftrans_i->TransformDual(vec1_r);
|
||||
doftrans_i->TransformDual(vec1_i);
|
||||
}
|
||||
doftrans_i.TransformDual(vec1_r);
|
||||
doftrans_i.TransformDual(vec1_i);
|
||||
y_r->GetBlock(skip_i).AddElementVector(vdofs_i,vec1_r);
|
||||
y_i->GetBlock(skip_i).AddElementVector(vdofs_i,vec1_i);
|
||||
skip_i++;
|
||||
|
||||
@@ -320,7 +320,7 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
|
||||
ElementTransformation *eltrans;
|
||||
Array<int> faces, ori;
|
||||
|
||||
DofTransformation * doftrans_i, *doftrans_j;
|
||||
DofTransformation doftrans_i, doftrans_j;
|
||||
if (mat_r == NULL)
|
||||
{
|
||||
AllocMat();
|
||||
@@ -540,7 +540,7 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
|
||||
for (int i = 0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
Array<int> vdofs_i;
|
||||
doftrans_i = nullptr;
|
||||
doftrans_i.SetDofTransformation(nullptr);
|
||||
if (IsTraceFes[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
@@ -553,12 +553,12 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_i = trial_fes[i]->GetElementVDofs(iel, vdofs_i);
|
||||
trial_fes[i]->GetElementVDofs(iel, vdofs_i, doftrans_i);
|
||||
}
|
||||
for (int j = 0; j < trial_fes.Size(); j++)
|
||||
{
|
||||
Array<int> vdofs_j;
|
||||
doftrans_j = nullptr;
|
||||
doftrans_j.SetDofTransformation(nullptr);
|
||||
|
||||
if (IsTraceFes[j])
|
||||
{
|
||||
@@ -572,7 +572,7 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_j = trial_fes[j]->GetElementVDofs(iel, vdofs_j);
|
||||
trial_fes[j]->GetElementVDofs(iel, vdofs_j, doftrans_j);
|
||||
}
|
||||
|
||||
DenseMatrix Ae_r, Ae_i;
|
||||
@@ -580,11 +580,8 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
|
||||
trial_offs[j],trial_offs[j+1], Ae_r);
|
||||
A->imag().GetSubMatrix(trial_offs[i],trial_offs[i+1],
|
||||
trial_offs[j],trial_offs[j+1], Ae_i);
|
||||
if (doftrans_i || doftrans_j)
|
||||
{
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_r);
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_i);
|
||||
}
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_r);
|
||||
TransformDual(doftrans_i, doftrans_j, Ae_i);
|
||||
if (!mat_r)
|
||||
{
|
||||
mfem::out << "null matrix " << std::endl;
|
||||
@@ -597,11 +594,9 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
|
||||
Vector vec1_r(b_r,trial_offs[i],trial_offs[i+1]-trial_offs[i]);
|
||||
Vector vec1_i(b_i,trial_offs[i],trial_offs[i+1]-trial_offs[i]);
|
||||
|
||||
if (doftrans_i)
|
||||
{
|
||||
doftrans_i->TransformDual(vec1_r);
|
||||
doftrans_i->TransformDual(vec1_i);
|
||||
}
|
||||
doftrans_i.TransformDual(vec1_r);
|
||||
doftrans_i.TransformDual(vec1_i);
|
||||
|
||||
y_r->GetBlock(i).AddElementVector(vdofs_i,vec1_r);
|
||||
y_i->GetBlock(i).AddElementVector(vdofs_i,vec1_i);
|
||||
}
|
||||
@@ -980,11 +975,11 @@ Vector & ComplexDPGWeakForm::ComputeResidual(const Vector & x)
|
||||
|
||||
int nn = trial_offs.Last();
|
||||
u.SetSize(2*nn);
|
||||
DofTransformation * doftrans = nullptr;
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
vdofs.SetSize(0);
|
||||
doftrans = nullptr;
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
if (IsTraceFes[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
@@ -997,7 +992,7 @@ Vector & ComplexDPGWeakForm::ComputeResidual(const Vector & x)
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans = trial_fes[i]->GetElementVDofs(iel, vdofs);
|
||||
trial_fes[i]->GetElementVDofs(iel, vdofs, doftrans);
|
||||
}
|
||||
Vector vec1_r;
|
||||
Vector vec1_i;
|
||||
@@ -1005,11 +1000,8 @@ Vector & ComplexDPGWeakForm::ComputeResidual(const Vector & x)
|
||||
vec1_i.MakeRef(u, trial_offs[i]+nn, trial_offs[i+1]-trial_offs[i]);
|
||||
x_r.GetBlock(i).GetSubVector(vdofs,vec1_r);
|
||||
x_i.GetBlock(i).GetSubVector(vdofs,vec1_i);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vec1_r);
|
||||
doftrans->InvTransformPrimal(vec1_i);
|
||||
}
|
||||
doftrans.InvTransformPrimal(vec1_r);
|
||||
doftrans.InvTransformPrimal(vec1_i);
|
||||
} // end of loop through trial spaces
|
||||
|
||||
// residual
|
||||
|
||||
@@ -249,7 +249,7 @@ void DPGWeakForm::Assemble(int skip_zeros)
|
||||
ElementTransformation *eltrans;
|
||||
Array<int> faces, ori;
|
||||
|
||||
DofTransformation * doftrans_i, *doftrans_j;
|
||||
DofTransformation doftrans_i, doftrans_j;
|
||||
if (mat == NULL)
|
||||
{
|
||||
AllocMat();
|
||||
@@ -412,7 +412,7 @@ void DPGWeakForm::Assemble(int skip_zeros)
|
||||
for (int i = 0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
Array<int> vdofs_i;
|
||||
doftrans_i = nullptr;
|
||||
doftrans_i.SetDofTransformation(nullptr);
|
||||
if (IsTraceFes[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
@@ -425,12 +425,12 @@ void DPGWeakForm::Assemble(int skip_zeros)
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_i = trial_fes[i]->GetElementVDofs(iel, vdofs_i);
|
||||
trial_fes[i]->GetElementVDofs(iel, vdofs_i, doftrans_i);
|
||||
}
|
||||
for (int j = 0; j<trial_fes.Size(); j++)
|
||||
{
|
||||
Array<int> vdofs_j;
|
||||
doftrans_j = nullptr;
|
||||
doftrans_j.SetDofTransformation(nullptr);
|
||||
|
||||
if (IsTraceFes[j])
|
||||
{
|
||||
@@ -444,16 +444,13 @@ void DPGWeakForm::Assemble(int skip_zeros)
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans_j = trial_fes[j]->GetElementVDofs(iel, vdofs_j);
|
||||
trial_fes[j]->GetElementVDofs(iel, vdofs_j, doftrans_j);
|
||||
}
|
||||
|
||||
DenseMatrix Ae;
|
||||
A.GetSubMatrix(trial_offs[i],trial_offs[i+1],
|
||||
trial_offs[j],trial_offs[j+1], Ae);
|
||||
if (doftrans_i || doftrans_j)
|
||||
{
|
||||
TransformDual(doftrans_i, doftrans_j, Ae);
|
||||
}
|
||||
TransformDual(doftrans_i, doftrans_j, Ae);
|
||||
mat->GetBlock(i,j).AddSubMatrix(vdofs_i,vdofs_j, Ae);
|
||||
}
|
||||
|
||||
@@ -463,10 +460,7 @@ void DPGWeakForm::Assemble(int skip_zeros)
|
||||
// ref subvector
|
||||
vec1.SetDataAndSize(&data[trial_offs[i]],
|
||||
trial_offs[i+1]-trial_offs[i]);
|
||||
if (doftrans_i)
|
||||
{
|
||||
doftrans_i->TransformDual(vec1);
|
||||
}
|
||||
doftrans_i.TransformDual(vec1);
|
||||
y->GetBlock(i).AddElementVector(vdofs_i,vec1);
|
||||
}
|
||||
}
|
||||
@@ -771,11 +765,11 @@ Vector & DPGWeakForm::ComputeResidual(const BlockVector & x)
|
||||
|
||||
u.SetSize(trial_offs.Last());
|
||||
real_t * data = u.GetData();
|
||||
DofTransformation * doftrans = nullptr;
|
||||
DofTransformation doftrans;
|
||||
for (int i = 0; i<trial_fes.Size(); i++)
|
||||
{
|
||||
vdofs.SetSize(0);
|
||||
doftrans = nullptr;
|
||||
doftrans.SetDofTransformation(nullptr);
|
||||
if (IsTraceFes[i])
|
||||
{
|
||||
Array<int> face_vdofs;
|
||||
@@ -788,16 +782,13 @@ Vector & DPGWeakForm::ComputeResidual(const BlockVector & x)
|
||||
}
|
||||
else
|
||||
{
|
||||
doftrans = trial_fes[i]->GetElementVDofs(iel, vdofs);
|
||||
trial_fes[i]->GetElementVDofs(iel, vdofs, doftrans);
|
||||
}
|
||||
Vector vec1;
|
||||
vec1.SetDataAndSize(&data[trial_offs[i]],
|
||||
trial_offs[i+1]-trial_offs[i]);
|
||||
x.GetBlock(i).GetSubVector(vdofs,vec1);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vec1);
|
||||
}
|
||||
doftrans.InvTransformPrimal(vec1);
|
||||
} // end of loop through trial spaces
|
||||
|
||||
Vector v(Bmat[iel]->Height());
|
||||
|
||||
@@ -49,30 +49,19 @@
|
||||
// Sample runs:
|
||||
//
|
||||
// Current source in a sphere with absorbing boundary conditions:
|
||||
// mpirun -np 4 maxwell -m ../../data/ball-nurbs.mesh -rs 2
|
||||
// -abcs '-1'
|
||||
// -dp '-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5'
|
||||
// mpirun -np 4 maxwell -m ../../data/ball-nurbs.mesh -rs 2 -abcs '-1' -dp '-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5'
|
||||
//
|
||||
// Current source in a metal sphere with dielectric and conducting materials:
|
||||
// mpirun -np 4 maxwell -m ../../data/ball-nurbs.mesh -rs 2
|
||||
// -dbcs '-1'
|
||||
// -dp '-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5'
|
||||
// -cs '0.0 0.0 -0.5 .2 3e6'
|
||||
// -ds '0.0 0.0 0.5 .2 10'
|
||||
// mpirun -np 4 maxwell -m ../../data/ball-nurbs.mesh -rs 2 -dbcs '-1' -dp '-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5' -cs '0.0 0.0 -0.5 .2 3e6' -ds '0.0 0.0 0.5 .2 10'
|
||||
//
|
||||
// Current source in a metal box:
|
||||
// mpirun -np 4 maxwell -m ../../data/fichera.mesh -rs 3
|
||||
// -ts 0.25 -tf 10 -dbcs '-1'
|
||||
// -dp '-0.5 -0.5 0.0 -0.5 -0.5 1.0 0.1 1 .5 1'
|
||||
// mpirun -np 4 maxwell -m ../../data/fichera.mesh -rs 3 -ts 0.25 -tf 10 -dbcs '-1' -dp '-0.5 -0.5 0.0 -0.5 -0.5 1.0 0.1 1 .5 1'
|
||||
//
|
||||
// Current source with a mixture of absorbing and reflecting boundaries:
|
||||
// mpirun -np 4 maxwell -m ../../data/fichera.mesh -rs 3
|
||||
// -ts 0.25 -tf 10
|
||||
// -dp '-0.5 -0.5 0.0 -0.5 -0.5 1.0 0.1 1 .5 1'
|
||||
// -dbcs '4 8 19 21' -abcs '5 18'
|
||||
// mpirun -np 4 maxwell -m ../../data/fichera.mesh -rs 3 -ts 0.25 -tf 10 -dp '-0.5 -0.5 0.0 -0.5 -0.5 1.0 0.1 1 .5 1' -dbcs '4 8 19 21' -abcs '5 18'
|
||||
//
|
||||
// By default the sources and fields are all zero:
|
||||
// mpirun -np 4 maxwell
|
||||
// * mpirun -np 4 maxwell
|
||||
|
||||
#include "maxwell_solver.hpp"
|
||||
#include <fstream>
|
||||
|
||||
+10
-19
@@ -122,7 +122,8 @@ int main (int argc, char *argv[])
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&search_on_rank_0, "-sr0", "--search-on-r0", "-no-sr0",
|
||||
"--no-search-on-r0",
|
||||
"Enable search only on rank 0 (disable to search points on all tasks).");
|
||||
"Enable search only on rank 0 (disable to search points on all tasks). "
|
||||
"All points added by other procs are ignored.");
|
||||
args.AddOption(&hrefinement, "-hr", "--h-refinement", "-no-hr",
|
||||
"--no-h-refinement",
|
||||
"Do random h refinements to mesh (does not work for pyramids).");
|
||||
@@ -130,15 +131,15 @@ int main (int argc, char *argv[])
|
||||
"Ordering of points to be found."
|
||||
"0 (default): byNodes, 1: byVDIM");
|
||||
args.AddOption(&gf_ordering, "-gfo", "--gridfunc-ordering",
|
||||
"Ordering of fespace that will be used for grid function to be interpolated."
|
||||
"Ordering of fespace that will be used for grid function to be interpolated. "
|
||||
"0 (default): byNodes, 1: byVDIM");
|
||||
args.AddOption(&devopt, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&randomization, "-random", "--random",
|
||||
"0: generate points randomly in the bounding box of domain,"
|
||||
"0: generate points randomly in the bounding box of domain, "
|
||||
"1: generate points randomly inside each element in mesh.");
|
||||
args.AddOption(&npt, "-npt", "--npt",
|
||||
"# points per proc or element");
|
||||
"# points / rank initialized on entire mesh (random = 0) or every element (random = 1).");
|
||||
args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
|
||||
|
||||
args.Parse();
|
||||
@@ -167,9 +168,6 @@ int main (int argc, char *argv[])
|
||||
randomization = 0;
|
||||
}
|
||||
|
||||
Vector xmin, xmax;
|
||||
mesh->GetBoundingBox(xmin, xmax);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Mesh curvature of the original mesh: ";
|
||||
@@ -184,7 +182,7 @@ int main (int argc, char *argv[])
|
||||
mesh->GetBoundingBox(pos_min, pos_max, mesh_poly_deg);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "--- Generating equidistant point for:\n"
|
||||
cout << "--- Generating points for:\n"
|
||||
<< "x in [" << pos_min(0) << ", " << pos_max(0) << "]\n"
|
||||
<< "y in [" << pos_min(1) << ", " << pos_max(1) << "]" << std::endl;
|
||||
if (dim == 3)
|
||||
@@ -283,9 +281,8 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// Generate equidistant points in physical coordinates over the whole mesh.
|
||||
// Note that some points might be outside, if the mesh is not a box. Note
|
||||
// also that all tasks search the same points (not mandatory).
|
||||
// Generate random points in physical coordinates over the whole mesh.
|
||||
// Note that some points might be outside if the mesh is not a box.
|
||||
int pts_cnt = npt;
|
||||
Vector vxyz;
|
||||
vxyz.UseDevice(!cpu_mode);
|
||||
@@ -370,13 +367,6 @@ int main (int argc, char *argv[])
|
||||
// finder.SetGPUtoCPUFallback(true);
|
||||
finder.FindPoints(vxyz, point_ordering);
|
||||
|
||||
Array<unsigned int> code_out1 = finder.GetCode();
|
||||
Array<unsigned int> el_out1 = finder.GetGSLIBElem();
|
||||
Vector ref_rst1 = finder.GetGSLIBReferencePosition();
|
||||
Vector ref_rst0 = finder.GetReferencePosition();
|
||||
Vector dist1 = finder.GetDist();
|
||||
Array<unsigned int> proc_out1 = finder.GetProc();
|
||||
|
||||
finder.Interpolate(field_vals, interp_vals);
|
||||
if (interp_vals.UseDevice())
|
||||
{
|
||||
@@ -442,7 +432,8 @@ int main (int argc, char *argv[])
|
||||
cout << setprecision(16)
|
||||
<< "Total number of elements: " << nelemglob
|
||||
<< "\nTotal number of procs: " << num_procs
|
||||
<< "\nSearched total points: " << pts_cnt*num_procs
|
||||
<< "\nSearched total points: " << (search_on_rank_0 ? pts_cnt :
|
||||
pts_cnt*num_procs)
|
||||
<< "\nFound locally on ranks: " << found_loc
|
||||
<< "\nFound on other tasks: " << found_away
|
||||
<< "\nPoints not found: " << not_found
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#include "kernel_helpers.hpp"
|
||||
#include "linalg/vector.hpp"
|
||||
|
||||
using mfem::internal::tensor;
|
||||
using mfem::internal::make_tensor;
|
||||
using mfem::future::tensor;
|
||||
using mfem::future::make_tensor;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
#include "general/forall.hpp"
|
||||
#include "linalg/tensor.hpp"
|
||||
|
||||
using mfem::internal::tensor;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace KernelHelpers
|
||||
{
|
||||
|
||||
using mfem::future::tensor;
|
||||
|
||||
// MFEM_SHARED_3D_BLOCK_TENSOR definition
|
||||
// Should be moved in backends/cuda/hip header files.
|
||||
#if defined(__CUDA_ARCH__)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "linalg/tensor.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
using mfem::internal::tensor;
|
||||
using mfem::future::tensor;
|
||||
|
||||
/** @brief Linear elastic material.
|
||||
*
|
||||
@@ -33,7 +33,7 @@ template <int dim> struct LinearElasticMaterial
|
||||
tensor<mfem::real_t, dim, dim>
|
||||
MFEM_HOST_DEVICE stress(const tensor<mfem::real_t, dim, dim> &dudx) const
|
||||
{
|
||||
constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
|
||||
constexpr auto I = mfem::future::IsotropicIdentity<dim>();
|
||||
auto epsilon = sym(dudx);
|
||||
return lambda * tr(epsilon) * I + 2.0 * mu * epsilon;
|
||||
}
|
||||
@@ -61,8 +61,8 @@ template <int dim> struct LinearElasticMaterial
|
||||
tensor<mfem::real_t, dim, dim, dim, dim>
|
||||
MFEM_HOST_DEVICE gradient(tensor<mfem::real_t, dim, dim> /* dudx */) const
|
||||
{
|
||||
return mfem::internal::make_tensor<dim, dim, dim, dim>([&](int i, int j, int k,
|
||||
int l)
|
||||
return mfem::future::make_tensor<dim, dim, dim, dim>([&](int i, int j, int k,
|
||||
int l)
|
||||
{
|
||||
return lambda * (i == j) * (k == l) +
|
||||
mu * ((i == l) * (j == k) + (i == k) * (j == l));
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "linalg/tensor.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
using mfem::internal::tensor;
|
||||
using mfem::internal::make_tensor;
|
||||
using mfem::future::tensor;
|
||||
using mfem::future::make_tensor;
|
||||
|
||||
/**
|
||||
* @brief Neo-Hookean material
|
||||
@@ -51,7 +51,7 @@ struct NeoHookeanMaterial
|
||||
MFEM_HOST_DEVICE tensor<T, dim, dim>
|
||||
stress(const tensor<T, dim, dim> &dudx) const
|
||||
{
|
||||
static constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
|
||||
static constexpr auto I = mfem::future::IsotropicIdentity<dim>();
|
||||
T J = det(I + dudx);
|
||||
T p = -2.0 * D1 * J * (J - 1);
|
||||
auto devB = dev(dudx + transpose(dudx) + dot(dudx, transpose(dudx)));
|
||||
@@ -89,7 +89,7 @@ struct NeoHookeanMaterial
|
||||
MFEM_HOST_DEVICE tensor<mfem::real_t, dim, dim, dim, dim>
|
||||
gradient(tensor<mfem::real_t, dim, dim> dudx) const
|
||||
{
|
||||
static constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
|
||||
static constexpr auto I = mfem::future::IsotropicIdentity<dim>();
|
||||
|
||||
tensor<mfem::real_t, dim, dim> F = I + dudx;
|
||||
tensor<mfem::real_t, dim, dim> invF = inv(F);
|
||||
@@ -153,7 +153,7 @@ struct NeoHookeanMaterial
|
||||
{
|
||||
auto sigma = stress(make_tensor<dim, dim>([&](int i, int j)
|
||||
{
|
||||
return mfem::internal::dual<mfem::real_t, mfem::real_t> {dudx[i][j], ddudx[i][j]};
|
||||
return mfem::future::dual<mfem::real_t, mfem::real_t> {dudx[i][j], ddudx[i][j]};
|
||||
}));
|
||||
return make_tensor<dim, dim>(
|
||||
[&](int i, int j) { return sigma[i][j].gradient; });
|
||||
@@ -210,7 +210,7 @@ struct NeoHookeanMaterial
|
||||
action_of_gradient_symbolic(const tensor<mfem::real_t, dim, dim> &du_dx,
|
||||
const tensor<mfem::real_t, dim, dim> &ddu_dx) const
|
||||
{
|
||||
static constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
|
||||
static constexpr auto I = mfem::future::IsotropicIdentity<dim>();
|
||||
|
||||
tensor<mfem::real_t, dim, dim> F = I + du_dx;
|
||||
tensor<mfem::real_t, dim, dim> invFT = inv(transpose(F));
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "general/forall.hpp"
|
||||
#include "linalg/tensor.hpp"
|
||||
|
||||
using mfem::internal::tensor;
|
||||
using mfem::future::tensor;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -276,7 +276,7 @@ int main(int argc, char *argv[])
|
||||
if (fespaceDim == 1)
|
||||
{
|
||||
const real_t h1error = CheckH1Continuity(x);
|
||||
cout << myid << ": H1 continuity error " << h1error << endl;
|
||||
if (myid == 0) { cout << "H1 continuity error " << h1error << endl; }
|
||||
MFEM_VERIFY(h1error < 1.0e-12, "H1 continuity is not satisfied");
|
||||
}
|
||||
|
||||
@@ -420,7 +420,10 @@ real_t CheckH1Continuity(ParGridFunction & x)
|
||||
}
|
||||
}
|
||||
|
||||
return errorMax;
|
||||
real_t errorMaxGlobal = 0.0;
|
||||
MPI_Allreduce(&errorMax, &errorMaxGlobal, 1, MFEM_MPI_REAL_T, MPI_MAX,
|
||||
fes->GetComm());
|
||||
return errorMaxGlobal;
|
||||
}
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
|
||||
@@ -181,4 +181,4 @@ clean-build:
|
||||
clean-exec:
|
||||
@rm -f refined.mesh sin-fit.mesh ex5.mesh exsol.mesh mesh.* sol.* mode_* naca-cmesh.mesh sol_?.gf
|
||||
@rm -rf Example1* Example3* Example5* Solenoidal_* ParaView
|
||||
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh
|
||||
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh solution.dat
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
//
|
||||
// Compile with: make nurbs_curveint
|
||||
//
|
||||
// Sample runs: ./nurbs_curveint -uw -n 9
|
||||
// ./nurbs_curveint -nw -n 9
|
||||
// Sample runs: nurbs_curveint -uw -n 9
|
||||
// nurbs_curveint -nw -n 9
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to interpolate a
|
||||
// curve in a NURBS patch. We first define a square shaped NURBS
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
//
|
||||
// Sample runs: nurbs_ex24 -m ../../data/pipe-nurbs-2d.mesh -o 2
|
||||
// nurbs_ex24 -m ../../data/pipe-nurbs-2d.mesh -p 2
|
||||
// nurbs_ex24 -m ../../data/cube-nurbs.mesh -o 2
|
||||
// nurbs_ex24 -m ../../data/cube-nurbs.mesh -o 2 -p 1
|
||||
// nurbs_ex24 -m ../../data/cube-nurbs.mesh -o 2 -p 2
|
||||
// nurbs_ex24 -m ../../data/cube-nurbs.mesh -o 2 -r 3
|
||||
// nurbs_ex24 -m ../../data/cube-nurbs.mesh -o 2 -p 1 -r 3
|
||||
// nurbs_ex24 -m ../../data/cube-nurbs.mesh -o 2 -p 2 -r 3
|
||||
// nurbs_ex24 -m ../../data/escher.mesh
|
||||
// nurbs_ex24 -m ../../data/escher.mesh -o 2
|
||||
// nurbs_ex24 -m ../../data/fichera.mesh
|
||||
@@ -15,11 +15,6 @@
|
||||
// nurbs_ex24 -m ../../data/amr-quad.mesh -o 2
|
||||
// nurbs_ex24 -m ../../data/amr-hex.mesh
|
||||
//
|
||||
// Device sample runs -- do not work for NURBS:
|
||||
// nurbs_ex24 -m ../../data/escher.mesh -pa -d cuda
|
||||
// nurbs_ex24 -m ../../data/escher.mesh -pa -d raja-cuda
|
||||
// nurbs_ex24 -m ../../data/escher.mesh -pa -d raja-omp
|
||||
//
|
||||
// Description: This example code illustrates usage of mixed finite element
|
||||
// spaces, with three variants:
|
||||
//
|
||||
|
||||
@@ -36,11 +36,6 @@
|
||||
//
|
||||
// We recommend viewing examples 1-4 before viewing this example.
|
||||
|
||||
// Sample runs: nurbs_ex3 -m ../../data/square-nurbs.mesh
|
||||
// nurbs_ex3 -m ../../data/square-nurbs.mesh -o 2
|
||||
// nurbs_ex3 -m ../../data/cube-nurbs.mesh
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
//
|
||||
// Compile with: make nurbs_naca_cmesh
|
||||
//
|
||||
// Sample run: ./nurbs_naca_cmesh -ntail 80 -nbnd 80 -ntip 20 -nwake 40
|
||||
// -sw 2.0 -sbnd 2.5 -stip 1.1 -aoa 3
|
||||
// Sample run: nurbs_naca_cmesh -ntail 80 -nbnd 80 -ntip 20 -nwake 40 -sw 2.0 -sbnd 2.5 -stip 1.1 -aoa 3
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to create a
|
||||
// C-mesh around a NACA-foil section. The foil section is defined
|
||||
|
||||
@@ -68,7 +68,13 @@ MFEM_PERF_CXXFLAGS = $$(MFEM_PERF_CXXFLAGS_$$(MFEM_PERF_SW))
|
||||
# Add MFEM_PERF_CXXFLAGS to MFEM_CXXFLAGS:
|
||||
ifeq (YES,$$(MFEM_USE_CUDA))
|
||||
ifneq (,$$(MFEM_PERF_CXXFLAGS))
|
||||
MFEM_CXXFLAGS += -Xcompiler="$$(MFEM_PERF_CXXFLAGS)"
|
||||
ifneq ($$(MFEM_HOST_CXX),$$(MFEM_CXX))
|
||||
# using nvcc
|
||||
MFEM_CXXFLAGS += -Xcompiler="$$(MFEM_PERF_CXXFLAGS)"
|
||||
else
|
||||
# using clang+cuda
|
||||
MFEM_CXXFLAGS += $$(MFEM_PERF_CXXFLAGS)
|
||||
endif
|
||||
endif
|
||||
else
|
||||
MFEM_CXXFLAGS += $$(MFEM_PERF_CXXFLAGS)
|
||||
|
||||
@@ -34,19 +34,19 @@
|
||||
// Problem 1: Circular hole of radius 0.2 at the center of the domain.
|
||||
// Solves -nabla^2 u = 1 with homogeneous boundary conditions.
|
||||
// Dirichlet boundary condition
|
||||
// mpirun -np 4 diffusion -rs 3 -o 1 -vis -lst 1
|
||||
// mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 2 -o 2 -vis -lst 1 -ho 1 -alpha 10
|
||||
// mpirun -np 4 diffusion -rs 3 -o 1 -lst 1
|
||||
// mpirun -np 4 diffusion -m ../../data/inline-hex.mesh -rs 2 -o 2 -lst 1 -ho 1 -alpha 10
|
||||
// Neumann boundary condition
|
||||
// mpirun -np 4 diffusion -rs 3 -o 1 -vis -nlst 1 -ho 1
|
||||
// mpirun -np 4 diffusion -rs 3 -o 1 -nlst 1 -ho 1
|
||||
//
|
||||
// Problem 2: Circular hole of radius 0.2 at the center of the domain.
|
||||
// Solves -nabla^2 u = f with inhomogeneous boundary conditions,
|
||||
// and f is setup such that u = x^p + y^p, where p = 2 by default.
|
||||
// This is a 2D convergence test.
|
||||
// Dirichlet BC
|
||||
// mpirun -np 4 diffusion -rs 2 -o 2 -vis -lst 2
|
||||
// mpirun -np 4 diffusion -rs 2 -o 2 -lst 2
|
||||
// Neumann BC (inhomogeneous condition derived using exact solution)
|
||||
// mpirun -np 4 diffusion -rs 2 -o 2 -vis -nlst 2 -ho 1
|
||||
// mpirun -np 4 diffusion -rs 2 -o 2 -nlst 2 -ho 1
|
||||
//
|
||||
// Problem 3: Domain is y = [0, 1] but mesh is shifted to [-1.e-4, 1].
|
||||
// Solves -nabla^2 u = f with inhomogeneous boundary conditions,
|
||||
@@ -57,7 +57,7 @@
|
||||
// the finite element space and the number of high-order terms
|
||||
// (-ho) to be included from the Taylor expansion used to enforce
|
||||
// the boundary conditions.
|
||||
// mpirun -np 4 diffusion -rs 2 -o 1 -vis -lst 3
|
||||
// mpirun -np 4 diffusion -rs 2 -o 1 -lst 3
|
||||
//
|
||||
// Problem 4: Complex 2D / 3D shapes:
|
||||
// Solves -nabla^2 u = 1 with homogeneous boundary conditions.
|
||||
@@ -69,7 +69,7 @@
|
||||
// Problem 5: Circular hole with homogeneous Neumann, triangular hole with
|
||||
// inhomogeneous Dirichlet, and a square hole with homogeneous
|
||||
// Dirichlet boundary condition.
|
||||
// mpirun -np 4 diffusion -rs 3 -o 1 -vis -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dc
|
||||
// mpirun -np 4 diffusion -rs 3 -o 1 -lst 5 -ho 1 -nlst 7 -alpha 10.0 -dc
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "../common/mfem-common.hpp"
|
||||
|
||||
@@ -65,17 +65,17 @@
|
||||
// latter may only work for order 1 on GPUs.
|
||||
//
|
||||
// Sample runs:
|
||||
// lor_elast -m ../../data/beam-tri.mesh
|
||||
// lor_elast -m ../../data/beam-quad.mesh
|
||||
// lor_elast -m ../../data/beam-hex.mesh
|
||||
// mpirun -np 4 lor_elast -m ../../data/beam-hex.mesh -l 3 -vdim
|
||||
// mpirun -np 4 lor_elast -m ../../data/beam-hex.mesh -l 3 -vdim -elast
|
||||
//
|
||||
// ./lor_elast -m ../../data/beam-tri.mesh
|
||||
// ./lor_elast -m ../../data/beam-quad.mesh
|
||||
// ./lor_elast -m ../../data/beam-hex.mesh
|
||||
// mpirun -np 4 ./lor_elast -m ../../data/beam-hex.mesh -l 5 -vdim
|
||||
// mpirun -np 4 ./lor_elast -m ../../data/beam-hex.mesh -l 5 -vdim -elast
|
||||
// ./lor_elast --device cuda -m ../../data/beam-hex.mesh -l 4 -o 2 -pa
|
||||
// ./lor_elast --device cuda -m ../../data/beam-hex.mesh -l 4 -o 2 -pa -pv
|
||||
// ./lor_elast --device cuda -m ../../data/beam-hex.mesh -l 4 -o 2 -pa -ss
|
||||
// ./lor_elast --device cuda -m ../../data/beam-hex.mesh -l 4 -o 2 -pa -ca
|
||||
// ./lor_elast --device cuda -m ../../data/beam-hex.mesh -l 5 -ca
|
||||
// Device sample runs:
|
||||
// lor_elast --device cuda -m ../../data/beam-hex.mesh -l 3 -o 2 -pa
|
||||
// lor_elast --device cuda -m ../../data/beam-hex.mesh -l 3 -o 2 -pa -ss
|
||||
// lor_elast --device cuda -m ../../data/beam-hex.mesh -l 3 -o 2 -pa -ca
|
||||
// lor_elast --device cuda -m ../../data/beam-hex.mesh -l 3 -ca
|
||||
//
|
||||
// References:
|
||||
// [1] Mihajlović, M.D. and Mijalković, S., "A component decomposition
|
||||
|
||||
@@ -56,16 +56,15 @@
|
||||
// lor_solvers -m ../../data/amr-quad.mesh -fe h
|
||||
// lor_solvers -m ../../data/amr-quad.mesh -fe n
|
||||
// lor_solvers -m ../../data/amr-quad.mesh -fe r
|
||||
// lor_solvers -m ../../data/amr-quad.mesh -fe l
|
||||
// lor_solvers -m ../../data/star-surf.mesh -fe h
|
||||
// lor_solvers -m ../../data/star-surf.mesh -fe n
|
||||
// lor_solvers -m ../../data/star-surf.mesh -fe r
|
||||
//
|
||||
// Device sample runs:
|
||||
// lor_solvers -fe h -d cuda
|
||||
// lor_solvers -fe n -d cuda
|
||||
// lor_solvers -fe r -d cuda
|
||||
// lor_solvers -fe l -d cuda
|
||||
// * lor_solvers -fe h -d cuda
|
||||
// * lor_solvers -fe n -d cuda
|
||||
// * lor_solvers -fe r -d cuda
|
||||
// * lor_solvers -fe l -d cuda
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -54,16 +54,15 @@
|
||||
// mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe r
|
||||
// mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe l
|
||||
// mpirun -np 4 plor_solvers -m ../../data/amr-hex.mesh -fe h -rs 0 -o 2
|
||||
// mpirun -np 4 plor_solvers -m ../../data/amr-hex.mesh -fe l -rs 0 -o 2
|
||||
// mpirun -np 4 plor_solvers -m ../../data/star-surf.mesh -fe h
|
||||
// mpirun -np 4 plor_solvers -m ../../data/star-surf.mesh -fe n
|
||||
// mpirun -np 4 plor_solvers -m ../../data/star-surf.mesh -fe r
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe h -d cuda
|
||||
// mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe n -d cuda
|
||||
// mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe r -d cuda
|
||||
// mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe l -d cuda
|
||||
// * mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe h -d cuda
|
||||
// * mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe n -d cuda
|
||||
// * mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe r -d cuda
|
||||
// * mpirun -np 4 plor_solvers -m ../../data/fichera.mesh -fe l -d cuda
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
// plor-transfer -m ../../data/star-q2.mesh -lref 5 -p 4
|
||||
// plor-transfer -m ../../data/star-mixed.mesh -lref 3 -p 2
|
||||
// plor-transfer -lref 4 -o 4 -lo 0 -p 1
|
||||
// plor-transfer -lref 5 -o 4 -lo 0 -p 1
|
||||
// plor-transfer -lref 5 -o 4 -lo 3 -p 2
|
||||
// plor-transfer -lref 5 -o 4 -lo 0 -p 3
|
||||
// plor-transfer -lref 4 -o 4 -lo 0 -p 1
|
||||
// plor-transfer -lref 4 -o 4 -lo 2 -p 2
|
||||
// plor-transfer -lref 4 -o 4 -lo 0 -p 3
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -17,6 +17,9 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# The following list can be updated using (in bash):
|
||||
# for d in general linalg mesh fem enzyme; do ls -1 $d/*.cpp; done
|
||||
set(UNIT_TESTS_SRCS
|
||||
dfem/test_diffusion.cpp
|
||||
dfem/test_divergence.cpp
|
||||
dfem/test_mass.cpp
|
||||
general/test_array.cpp
|
||||
general/test_reduction.cpp
|
||||
general/test_arrays_by_name.cpp
|
||||
@@ -89,6 +92,7 @@ set(UNIT_TESTS_SRCS
|
||||
fem/test_fe.cpp
|
||||
fem/test_fe_compatibility.cpp
|
||||
fem/test_fe_fixed.cpp
|
||||
fem/test_fe_pos.cpp
|
||||
fem/test_fe_symmetry.cpp
|
||||
fem/test_get_value.cpp
|
||||
fem/test_getderivative.cpp
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
// 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 "../unit_tests.hpp"
|
||||
#include "mfem.hpp"
|
||||
#include <utility>
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
using dscalar_t = real_t;
|
||||
#else
|
||||
using mfem::future::dual;
|
||||
using dscalar_t = dual<real_t, real_t>;
|
||||
#endif
|
||||
|
||||
using DOperator = DifferentiableOperator;
|
||||
|
||||
namespace dfem_pa_kernels
|
||||
{
|
||||
|
||||
template <int DIM> struct Diffusion
|
||||
{
|
||||
using dvecd_t = tensor<dscalar_t, DIM>;
|
||||
using matd_t = tensor<real_t, DIM, DIM>;
|
||||
|
||||
struct MFApply
|
||||
{
|
||||
MFEM_HOST_DEVICE inline auto operator()(const dvecd_t &dudxi,
|
||||
const real_t &rho,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
const auto invJ = inv(J), TinJ = transpose(invJ);
|
||||
return tuple{ (dudxi * invJ) * TinJ * det(J) * w * rho };
|
||||
}
|
||||
};
|
||||
|
||||
struct PASetup
|
||||
{
|
||||
MFEM_HOST_DEVICE inline auto operator()(const real_t u,
|
||||
const real_t &rho,
|
||||
const matd_t &J,
|
||||
const real_t &w) const
|
||||
{
|
||||
return tuple{ inv(J) * transpose(inv(J)) * det(J) * w * rho };
|
||||
}
|
||||
};
|
||||
|
||||
struct PAApply
|
||||
{
|
||||
MFEM_HOST_DEVICE inline auto operator()(const dvecd_t &dudxi,
|
||||
const matd_t &q) const
|
||||
{
|
||||
return tuple{ q * dudxi };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
template <int DIM>
|
||||
void DFemDiffusion(const char *filename, int p, const int r)
|
||||
{
|
||||
CAPTURE(filename, DIM, p, r);
|
||||
|
||||
Mesh smesh(filename);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, smesh);
|
||||
MFEM_VERIFY(pmesh.Dimension() == DIM, "Mesh dimension mismatch");
|
||||
|
||||
pmesh.EnsureNodes();
|
||||
auto *nodes = static_cast<ParGridFunction *>(pmesh.GetNodes());
|
||||
p = std::max(p, pmesh.GetNodalFESpace()->GetMaxElementOrder());
|
||||
smesh.Clear();
|
||||
|
||||
Array<int> all_domain_attr;
|
||||
if (pmesh.attributes.Size() > 0)
|
||||
{
|
||||
all_domain_attr.SetSize(pmesh.attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
}
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace pfes(&pmesh, &fec);
|
||||
ParFiniteElementSpace *mfes = nodes->ParFESpace();
|
||||
|
||||
const int NE = pfes.GetNE(), d1d(p + 1), q = 2 * p + r;
|
||||
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(), q);
|
||||
const int q1d(IntRules.Get(Geometry::SEGMENT, ir->GetOrder()).GetNPoints());
|
||||
MFEM_VERIFY(d1d <= q1d, "q1d should be >= d1d");
|
||||
|
||||
ParGridFunction x(&pfes), y(&pfes), z(&pfes);
|
||||
Vector X(pfes.GetTrueVSize()), Y(pfes.GetTrueVSize()), Z(pfes.GetTrueVSize());
|
||||
|
||||
X.Randomize(1);
|
||||
x.SetFromTrueDofs(X);
|
||||
|
||||
auto rho = [](const Vector &xyz)
|
||||
{
|
||||
const real_t x = xyz(0), y = xyz(1), z = DIM == 3 ? xyz(2) : 0.0;
|
||||
real_t r = M_PI * pow(x, 2);
|
||||
if (DIM >= 2) { r += pow(y, 3); }
|
||||
if (DIM >= 3) { r += pow(z, 4); }
|
||||
return r;
|
||||
};
|
||||
FunctionCoefficient rho_coeff(rho);
|
||||
|
||||
ParBilinearForm blf_fa(&pfes);
|
||||
blf_fa.AddDomainIntegrator(new DiffusionIntegrator(rho_coeff, ir));
|
||||
blf_fa.Assemble();
|
||||
blf_fa.Finalize();
|
||||
|
||||
SECTION("[Partial assembly] Diffusion")
|
||||
{
|
||||
ParBilinearForm blf_pa(&pfes);
|
||||
blf_pa.AddDomainIntegrator(new DiffusionIntegrator(rho_coeff, ir));
|
||||
blf_pa.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
blf_pa.Assemble();
|
||||
blf_pa.Mult(x, z);
|
||||
|
||||
blf_fa.Mult(x, y);
|
||||
y -= z;
|
||||
REQUIRE(y.Normlinf() == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
QuadratureSpace qs(pmesh, *ir);
|
||||
CoefficientVector rho_coeff_cv(rho_coeff, qs);
|
||||
MFEM_VERIFY(rho_coeff_cv.GetVDim() == 1, "Coefficient should be scalar");
|
||||
MFEM_VERIFY(rho_coeff_cv.Size() == q1d * q1d * (DIM == 3 ? q1d : 1) * NE, "");
|
||||
|
||||
UniformParameterSpace rho_ps(pmesh, *ir, 1);
|
||||
|
||||
static constexpr int U = 0, Coords = 1, Rho = 3;
|
||||
const auto sol = std::vector{ FieldDescriptor{ U, &pfes } };
|
||||
|
||||
SECTION("[dFEM Matrix free] Diffusion")
|
||||
{
|
||||
DOperator dop_mf(sol, {{Rho, &rho_ps}, {Coords, mfes}}, pmesh);
|
||||
typename Diffusion<DIM>::MFApply mf_apply_qf;
|
||||
dop_mf.AddDomainIntegrator(mf_apply_qf,
|
||||
tuple{ Gradient<U>{}, Identity<Rho>{},
|
||||
Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Gradient<U>{} }, *ir,
|
||||
all_domain_attr);
|
||||
dop_mf.SetParameters({ &rho_coeff_cv, nodes });
|
||||
|
||||
pfes.GetRestrictionMatrix()->Mult(x, X);
|
||||
dop_mf.Mult(X, Z);
|
||||
|
||||
blf_fa.Mult(x, y);
|
||||
pfes.GetProlongationMatrix()->MultTranspose(y, Y);
|
||||
Y -= Z;
|
||||
|
||||
real_t norm_global = 0.0;
|
||||
real_t norm_local = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_local, &norm_global, 1, MPI_DOUBLE, MPI_MAX,
|
||||
pmesh.GetComm());
|
||||
|
||||
REQUIRE(norm_global == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
SECTION("[dFEM Partial assembly] Diffusion")
|
||||
{
|
||||
static constexpr int QData = 2;
|
||||
UniformParameterSpace qd_ps(pmesh, *ir, DIM * DIM);
|
||||
ParameterFunction qdata(qd_ps);
|
||||
qdata.UseDevice(true);
|
||||
|
||||
DOperator dSetup(sol, {{Rho, &rho_ps}, {Coords, mfes}, {QData, &qd_ps}}, pmesh);
|
||||
typename Diffusion<DIM>::PASetup pa_setup_qf;
|
||||
dSetup.AddDomainIntegrator(
|
||||
pa_setup_qf,
|
||||
tuple{ Value<U>{}, Identity<Rho>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Identity<QData>{} }, *ir, all_domain_attr);
|
||||
dSetup.SetParameters({ &rho_coeff_cv, nodes, &qdata });
|
||||
pfes.GetRestrictionMatrix()->Mult(x, X);
|
||||
dSetup.Mult(X, qdata);
|
||||
|
||||
DOperator dop_pa(sol, { { QData, &qd_ps } }, pmesh);
|
||||
typename Diffusion<DIM>::PAApply pa_apply_qf;
|
||||
dop_pa.AddDomainIntegrator(pa_apply_qf,
|
||||
tuple{ Gradient<U>{}, Identity<QData>{} },
|
||||
tuple{ Gradient<U>{} },
|
||||
*ir, all_domain_attr);
|
||||
dop_pa.SetParameters({ &qdata });
|
||||
|
||||
pfes.GetRestrictionMatrix()->Mult(x, X);
|
||||
dop_pa.Mult(X, Z);
|
||||
|
||||
blf_fa.Mult(x, y);
|
||||
pfes.GetProlongationMatrix()->MultTranspose(y, Y);
|
||||
Y -= Z;
|
||||
|
||||
real_t norm_global = 0.0;
|
||||
real_t norm_local = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_local, &norm_global, 1, MPI_DOUBLE, MPI_MAX,
|
||||
pmesh.GetComm());
|
||||
|
||||
REQUIRE(norm_global == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
SECTION("[dFEM Linearization] Diffusion")
|
||||
{
|
||||
DOperator dop_mf(sol, {{Rho, &rho_ps}, {Coords, mfes}}, pmesh);
|
||||
typename Diffusion<DIM>::MFApply mf_apply_qf;
|
||||
auto derivatives = std::integer_sequence<size_t, U> {};
|
||||
dop_mf.AddDomainIntegrator(mf_apply_qf,
|
||||
tuple{ Gradient<U>{}, Identity<Rho>{},
|
||||
Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Gradient<U>{} }, *ir,
|
||||
all_domain_attr, derivatives);
|
||||
dop_mf.SetParameters({ &rho_coeff_cv, nodes });
|
||||
auto dRdU = dop_mf.GetDerivative(U, {&x}, {&rho_coeff_cv, nodes});
|
||||
|
||||
pfes.GetRestrictionMatrix()->Mult(x, X);
|
||||
dop_mf.Mult(X, Z);
|
||||
|
||||
blf_fa.Mult(x, y);
|
||||
pfes.GetProlongationMatrix()->MultTranspose(y, Y);
|
||||
Y -= Z;
|
||||
|
||||
real_t norm_global = 0.0;
|
||||
real_t norm_local = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_local, &norm_global, 1, MPI_DOUBLE, MPI_MAX,
|
||||
pmesh.GetComm());
|
||||
|
||||
REQUIRE(norm_global == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
SECTION("[dFEM Matrix free] vector diffusion")
|
||||
{
|
||||
ParFiniteElementSpace vpfes(&pmesh, &fec, DIM);
|
||||
ParGridFunction vx(&vpfes), vy(&vpfes);
|
||||
Vector vX(vpfes.GetTrueVSize()), vY(vpfes.GetTrueVSize()),
|
||||
vZ(vpfes.GetTrueVSize());
|
||||
|
||||
vX.Randomize(1), vx.SetFromTrueDofs(vX);
|
||||
|
||||
{
|
||||
const auto vsol = std::vector{ FieldDescriptor{ U, &vpfes } };
|
||||
DOperator dop_mf(vsol, {{Coords, mfes}}, pmesh);
|
||||
const auto mf_vector_diffusion_qf =
|
||||
[] MFEM_HOST_DEVICE (const tensor<dscalar_t, DIM, DIM> &dudxi,
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
const auto invJ = inv(J), TinJ = transpose(invJ);
|
||||
return tuple{ (dudxi * invJ) * TinJ * det(J) * w };
|
||||
};
|
||||
dop_mf.AddDomainIntegrator(mf_vector_diffusion_qf,
|
||||
tuple{ Gradient<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Gradient<U>{} },
|
||||
*ir, all_domain_attr);
|
||||
dop_mf.SetParameters({ nodes });
|
||||
vpfes.GetRestrictionMatrix()->Mult(vx, vX), dop_mf.Mult(vX, vZ);
|
||||
}
|
||||
{
|
||||
ConstantCoefficient one(1.0);
|
||||
ParBilinearForm vblf_fa(&vpfes);
|
||||
vblf_fa.AddDomainIntegrator(new VectorDiffusionIntegrator(one, ir));
|
||||
vblf_fa.Assemble(), vblf_fa.Finalize();
|
||||
vblf_fa.Mult(vx, vy), vpfes.GetProlongationMatrix()->MultTranspose(vy, vY);
|
||||
}
|
||||
vY -= vZ;
|
||||
real_t norm_global = 0.0, norm_local = vY.Normlinf();
|
||||
MPI_Allreduce(&norm_local, &norm_global, 1, MPI_DOUBLE, MPI_MAX,
|
||||
pmesh.GetComm());
|
||||
REQUIRE(norm_global == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DFEM Diffusion", "[Parallel][DFEM]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
|
||||
const auto r = !all_tests ? 2 : GENERATE(0, 1, 2, 3);
|
||||
|
||||
SECTION("2D p=" + std::to_string(p) + " r=" + std::to_string(r))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/star.mesh",
|
||||
"../../data/star-q3.mesh",
|
||||
"../../data/rt-2d-q3.mesh",
|
||||
"../../data/inline-quad.mesh",
|
||||
"../../data/periodic-square.mesh");
|
||||
DFemDiffusion<2>(filename, p, r);
|
||||
}
|
||||
|
||||
SECTION("3D p=" + std::to_string(p) + " r=" + std::to_string(r))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/fichera.mesh",
|
||||
"../../data/fichera-q3.mesh",
|
||||
"../../data/inline-hex.mesh",
|
||||
"../../data/toroid-hex.mesh",
|
||||
"../../data/periodic-cube.mesh");
|
||||
DFemDiffusion<3>(filename, p, r);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dfem_pa_kernels
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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 "../unit_tests.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
using dscalar_t = real_t;
|
||||
#else
|
||||
using mfem::future::dual;
|
||||
using dscalar_t = dual<real_t, real_t>;
|
||||
#endif
|
||||
|
||||
namespace dfem_pa_kernels
|
||||
{
|
||||
|
||||
template <int DIM>
|
||||
void dFemVectorDivergence(const char *filename, int p)
|
||||
{
|
||||
CAPTURE(filename, DIM, p);
|
||||
|
||||
Mesh smesh(filename);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, smesh);
|
||||
MFEM_VERIFY(pmesh.Dimension() == DIM, "Mesh dimension mismatch");
|
||||
|
||||
pmesh.EnsureNodes();
|
||||
auto *nodes = static_cast<ParGridFunction *>(pmesh.GetNodes());
|
||||
p = std::max(p, pmesh.GetNodalFESpace()->GetMaxElementOrder());
|
||||
smesh.Clear();
|
||||
|
||||
Array<int> all_domain_attr;
|
||||
if (pmesh.attributes.Size() > 0)
|
||||
{
|
||||
all_domain_attr.SetSize(pmesh.attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
}
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace psfes(&pmesh, &fec);
|
||||
ParFiniteElementSpace pvfes(&pmesh, &fec, DIM);
|
||||
|
||||
const int d1d(p + 1), q = 3 * p + 1;
|
||||
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(), q);
|
||||
const int q1d(IntRules.Get(Geometry::SEGMENT, ir->GetOrder()).GetNPoints());
|
||||
MFEM_VERIFY(d1d <= q1d, "q1d should be >= d1d");
|
||||
|
||||
ParGridFunction vx(&pvfes);
|
||||
ParGridFunction sy(&psfes), sz(&psfes);
|
||||
Vector vX(pvfes.GetTrueVSize());
|
||||
Vector sY(psfes.GetTrueVSize()), sZ(psfes.GetTrueVSize());
|
||||
|
||||
vX.Randomize(1), vx.SetFromTrueDofs(vX);
|
||||
|
||||
MixedBilinearForm mblf_fa(&pvfes, &psfes);
|
||||
mblf_fa.AddDomainIntegrator(new VectorDivergenceIntegrator);
|
||||
mblf_fa.Assemble(), mblf_fa.Finalize();
|
||||
mblf_fa.Mult(vx, sy);
|
||||
|
||||
MixedBilinearForm mblf_pa(&pvfes, &psfes);
|
||||
mblf_pa.AddDomainIntegrator(new VectorDivergenceIntegrator);
|
||||
mblf_pa.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
mblf_pa.Assemble();
|
||||
mblf_pa.Mult(vx, sz);
|
||||
sy -= sz;
|
||||
REQUIRE(sy.Normlinf() == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
|
||||
{
|
||||
static constexpr int P = 0, V = 1, Coords = 2;
|
||||
ParFiniteElementSpace *mfes = nodes->ParFESpace();
|
||||
|
||||
const auto solutions = std::vector{ FieldDescriptor{ P, &psfes } };
|
||||
const auto parameters = std::vector
|
||||
{
|
||||
FieldDescriptor{ V, &pvfes },
|
||||
FieldDescriptor{ Coords, mfes }
|
||||
};
|
||||
|
||||
DifferentiableOperator dop_mf(solutions, parameters, pmesh);
|
||||
|
||||
const auto mf_vector_divergence_qf =
|
||||
[] MFEM_HOST_DEVICE(const tensor<dscalar_t, DIM, DIM> &dudxi,
|
||||
const tensor<mfem::real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
const auto invJ = inv(J);
|
||||
const auto dudx = dudxi * invJ;
|
||||
return tuple{ tr(dudx) * det(J) * w };
|
||||
};
|
||||
|
||||
dop_mf.AddDomainIntegrator(mf_vector_divergence_qf,
|
||||
tuple{ Gradient<V>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Value<P>{} },
|
||||
*ir, all_domain_attr);
|
||||
|
||||
dop_mf.SetParameters({ &vx, nodes });
|
||||
Vector unused(pvfes.GetTrueVSize());
|
||||
dop_mf.Mult(unused, sZ);
|
||||
|
||||
mblf_fa.Mult(vx, sy);
|
||||
psfes.GetProlongationMatrix()->MultTranspose(sy, sY);
|
||||
|
||||
sY -= sZ;
|
||||
real_t norm_global = M_PI, norm_local = sY.Normlinf();
|
||||
MPI_Allreduce(&norm_local, &norm_global, 1, MPI_DOUBLE, MPI_MAX,
|
||||
pmesh.GetComm());
|
||||
REQUIRE(norm_global == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("dFEM VectorDivergence", "[Parallel][DFEM][VectorDivergence]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
|
||||
|
||||
SECTION("2D p=" + std::to_string(p))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/star.mesh",
|
||||
"../../data/star-q3.mesh",
|
||||
"../../data/rt-2d-q3.mesh",
|
||||
"../../data/inline-quad.mesh",
|
||||
"../../data/periodic-square.mesh");
|
||||
dFemVectorDivergence<2>(filename, p);
|
||||
}
|
||||
|
||||
SECTION("3D p=" + std::to_string(p))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/fichera.mesh",
|
||||
"../../data/fichera-q3.mesh",
|
||||
"../../data/inline-hex.mesh",
|
||||
"../../data/toroid-hex.mesh",
|
||||
"../../data/periodic-cube.mesh");
|
||||
dFemVectorDivergence<3>(filename, p);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dfem_pa_kernels
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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 "../unit_tests.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
namespace dfem_pa_kernels
|
||||
{
|
||||
|
||||
template <int DIM>
|
||||
void dfem_functional(const char *filename, int p, const int r)
|
||||
{
|
||||
CAPTURE(filename, DIM, p, r);
|
||||
|
||||
Mesh smesh(filename);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, smesh);
|
||||
pmesh.EnsureNodes();
|
||||
auto *nodes = static_cast<ParGridFunction *>(pmesh.GetNodes());
|
||||
p = std::max(p, pmesh.GetNodalFESpace()->GetMaxElementOrder());
|
||||
smesh.Clear();
|
||||
|
||||
Array<int> all_domain_attr;
|
||||
if (pmesh.attributes.Size() > 0)
|
||||
{
|
||||
all_domain_attr.SetSize(pmesh.attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
}
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace fes(&pmesh, &fec);
|
||||
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(), 2 * p + r);
|
||||
|
||||
ParGridFunction x(&fes), y(&fes), z(&fes);
|
||||
Vector X(fes.GetTrueVSize()), Y(fes.GetTrueVSize()), Z(fes.GetTrueVSize());
|
||||
|
||||
X = 1.0;
|
||||
x.SetFromTrueDofs(X);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ParBilinearForm blf(&fes);
|
||||
blf.AddDomainIntegrator(new MassIntegrator(one, ir));
|
||||
blf.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
blf.Assemble();
|
||||
blf.Mult(x, y);
|
||||
fes.GetProlongationMatrix()->MultTranspose(y, Y);
|
||||
|
||||
real_t sum_g, sum_l = Y.Sum();
|
||||
MPI_Allreduce(&sum_l, &sum_g, 1, MPI_DOUBLE, MPI_SUM, pmesh.GetComm());
|
||||
|
||||
static constexpr int U = 0, Coords = 1;
|
||||
const auto sol = std::vector{ FieldDescriptor{ U, &fes } };
|
||||
DifferentiableOperator dop(sol, {{Coords, nodes->ParFESpace()}}, pmesh);
|
||||
|
||||
const auto functional_qf =
|
||||
[] MFEM_HOST_DEVICE(
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
const real_t &u,
|
||||
#else
|
||||
const dual<real_t, real_t> &u,
|
||||
#endif
|
||||
const tensor<real_t, DIM, DIM> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
return tuple{u * w * det(J)};
|
||||
};
|
||||
|
||||
auto derivatives = std::integer_sequence<size_t, U> {};
|
||||
dop.AddDomainIntegrator(functional_qf,
|
||||
tuple{ Value<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Sum<U>{} },
|
||||
*ir, all_domain_attr, derivatives);
|
||||
dop.SetParameters({ nodes });
|
||||
|
||||
fes.GetRestrictionMatrix()->Mult(x, X);
|
||||
Vector sum(1);
|
||||
dop.Mult(X, sum);
|
||||
|
||||
REQUIRE(MFEM_Approx(0.0) == std::abs(sum_g - sum(0)));
|
||||
|
||||
auto dRdu = dop.GetDerivative(U, {&x}, {nodes});
|
||||
Vector d(1);
|
||||
dRdu->Mult(X, d);
|
||||
REQUIRE(MFEM_Approx(0.0) == std::abs(d(0) - sum(0)));
|
||||
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
TEST_CASE("DFEM Functional", "[Parallel][DFEM][Functional]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
|
||||
const auto r = !all_tests ? 1 : GENERATE(0, 1, 2, 3);
|
||||
|
||||
SECTION("2D p=" + std::to_string(p) + " r=" + std::to_string(r))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/star.mesh",
|
||||
"../../data/star-q3.mesh",
|
||||
"../../data/rt-2d-q3.mesh",
|
||||
"../../data/inline-quad.mesh",
|
||||
"../../data/periodic-square.mesh");
|
||||
dfem_functional<2>(filename, p, r);
|
||||
}
|
||||
|
||||
SECTION("3D p=" + std::to_string(p) + " r=" + std::to_string(r))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/fichera.mesh",
|
||||
"../../data/fichera-q3.mesh",
|
||||
"../../data/inline-hex.mesh",
|
||||
"../../data/toroid-hex.mesh",
|
||||
"../../data/periodic-cube.mesh");
|
||||
dfem_functional<3>(filename, p, r);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dfem_pa_kernels
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,116 @@
|
||||
// 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 "../unit_tests.hpp"
|
||||
#include "mfem.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
using mfem::future::tensor;
|
||||
|
||||
namespace dfem_pa_kernels
|
||||
{
|
||||
|
||||
template <int DIM>
|
||||
void DFemMass(const char *filename, int p, const int r)
|
||||
{
|
||||
CAPTURE(filename, DIM, p, r);
|
||||
|
||||
Mesh smesh(filename);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, smesh);
|
||||
pmesh.EnsureNodes();
|
||||
auto *nodes = static_cast<ParGridFunction *>(pmesh.GetNodes());
|
||||
p = std::max(p, pmesh.GetNodalFESpace()->GetMaxElementOrder());
|
||||
smesh.Clear();
|
||||
|
||||
Array<int> all_domain_attr;
|
||||
if (pmesh.attributes.Size() > 0)
|
||||
{
|
||||
all_domain_attr.SetSize(pmesh.attributes.Max());
|
||||
all_domain_attr = 1;
|
||||
}
|
||||
|
||||
H1_FECollection fec(p, DIM);
|
||||
ParFiniteElementSpace fes(&pmesh, &fec);
|
||||
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(), 2 * p + r);
|
||||
|
||||
ParGridFunction x(&fes), y(&fes), z(&fes);
|
||||
Vector X(fes.GetTrueVSize()), Y(fes.GetTrueVSize()), Z(fes.GetTrueVSize());
|
||||
|
||||
X.Randomize(1);
|
||||
x.SetFromTrueDofs(X);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ParBilinearForm blf(&fes);
|
||||
blf.AddDomainIntegrator(new MassIntegrator(one, ir));
|
||||
blf.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
blf.Assemble();
|
||||
blf.Mult(x, y);
|
||||
fes.GetProlongationMatrix()->MultTranspose(y, Y);
|
||||
|
||||
static constexpr int U = 0, Coords = 1;
|
||||
const auto sol = std::vector{ FieldDescriptor{ U, &fes } };
|
||||
DifferentiableOperator dop(sol, {{Coords, nodes->ParFESpace()}}, pmesh);
|
||||
const auto mf_mass_qf =
|
||||
[] MFEM_HOST_DEVICE(const real_t &dudxi,
|
||||
const tensor<real_t, DIM, DIM> &J, const real_t &w)
|
||||
{ return tuple{dudxi * w * det(J)}; };
|
||||
dop.AddDomainIntegrator(mf_mass_qf,
|
||||
tuple{ Value<U>{}, Gradient<Coords>{}, Weight{} },
|
||||
tuple{ Value<U>{} },
|
||||
*ir, all_domain_attr);
|
||||
dop.SetParameters({ nodes });
|
||||
|
||||
fes.GetRestrictionMatrix()->Mult(x, X);
|
||||
dop.Mult(X, Z);
|
||||
Y -= Z;
|
||||
|
||||
real_t norm_g, norm_l = Y.Normlinf();
|
||||
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
|
||||
REQUIRE(norm_g == MFEM_Approx(0.0));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
TEST_CASE("DFEM Mass", "[Parallel][DFEM]")
|
||||
{
|
||||
const bool all_tests = launch_all_non_regression_tests;
|
||||
|
||||
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
|
||||
const auto r = !all_tests ? 1 : GENERATE(0, 1, 2, 3);
|
||||
|
||||
SECTION("2D p=" + std::to_string(p) + " r=" + std::to_string(r))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/star.mesh",
|
||||
"../../data/star-q3.mesh",
|
||||
"../../data/rt-2d-q3.mesh",
|
||||
"../../data/inline-quad.mesh",
|
||||
"../../data/periodic-square.mesh");
|
||||
DFemMass<2>(filename, p, r);
|
||||
}
|
||||
|
||||
SECTION("3D p=" + std::to_string(p) + " r=" + std::to_string(r))
|
||||
{
|
||||
const auto filename =
|
||||
GENERATE("../../data/fichera.mesh",
|
||||
"../../data/fichera-q3.mesh",
|
||||
"../../data/inline-hex.mesh",
|
||||
"../../data/toroid-hex.mesh",
|
||||
"../../data/periodic-cube.mesh");
|
||||
DFemMass<3>(filename, p, r);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dfem_pa_kernels
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
|
||||
#include "../../../general/enzyme.hpp"
|
||||
|
||||
template<typename VectorT>
|
||||
void square(const VectorT& v, double& y)
|
||||
{
|
||||
|
||||
@@ -83,7 +83,7 @@ void TestCalcShape(FiniteElement* fe, int res, double tol=1e-12)
|
||||
|
||||
Vector weights( fe->GetDof() );
|
||||
|
||||
// Get a uniform grid or integration points
|
||||
// Get a uniform grid of integration points
|
||||
RefinedGeometry* ref = GlobGeometryRefiner.Refine( fe->GetGeomType(), res);
|
||||
const IntegrationRule& intRule = ref->RefPts;
|
||||
|
||||
|
||||
@@ -381,4 +381,4 @@ TEST_CASE("ParaView VTKHDF restart mode", "[ParaView]")
|
||||
REQUIRE(remove("ParaView.vtkhdf") == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // MFEM_USE_HDF5
|
||||
|
||||
@@ -280,10 +280,12 @@ TEST_CASE("DoF Transformation Functions",
|
||||
DenseMatrix At;
|
||||
DenseMatrix tAt;
|
||||
|
||||
nAn = A; TransformPrimal(NULL, NULL, nAn);
|
||||
At = A; TransformPrimal(NULL, &Tq, At);
|
||||
tA = A; TransformPrimal( &Tp, NULL, tA);
|
||||
tAt = A; TransformPrimal( &Tp, &Tq, tAt);
|
||||
DofTransformation id;
|
||||
|
||||
nAn = A; TransformPrimal(id, id, nAn);
|
||||
At = A; TransformPrimal(id, Tq, At);
|
||||
tA = A; TransformPrimal( Tp, id, tA);
|
||||
tAt = A; TransformPrimal( Tp, Tq, tAt);
|
||||
|
||||
double fAv = A.InnerProduct(v, f);
|
||||
|
||||
@@ -312,10 +314,12 @@ TEST_CASE("DoF Transformation Functions",
|
||||
DenseMatrix At;
|
||||
DenseMatrix tAt;
|
||||
|
||||
nAn = A; TransformDual(NULL, NULL, nAn);
|
||||
At = A; TransformDual(NULL, &Tq, At);
|
||||
tA = A; TransformDual( &Tp, NULL, tA);
|
||||
tAt = A; TransformDual( &Tp, &Tq, tAt);
|
||||
DofTransformation id;
|
||||
|
||||
nAn = A; TransformDual(id, id, nAn);
|
||||
At = A; TransformDual(id, Tq, At);
|
||||
tA = A; TransformDual(Tp, id, tA);
|
||||
tAt = A; TransformDual(Tp, Tq, tAt);
|
||||
|
||||
double uAv = A.InnerProduct(v, u);
|
||||
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
// 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 "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
FiniteElement * GetH1PosFiniteElement(Geometry::Type type, int order)
|
||||
{
|
||||
FiniteElement *fe = NULL;
|
||||
switch (type)
|
||||
{
|
||||
case Geometry::SEGMENT:
|
||||
fe = new H1Pos_SegmentElement(order);
|
||||
break;
|
||||
case Geometry::TRIANGLE:
|
||||
fe = new H1Pos_TriangleElement(order);
|
||||
break;
|
||||
case Geometry::SQUARE:
|
||||
fe = new H1Pos_QuadrilateralElement(order);
|
||||
break;
|
||||
case Geometry::TETRAHEDRON:
|
||||
fe = new H1Pos_TetrahedronElement(order);
|
||||
break;
|
||||
case Geometry::CUBE:
|
||||
fe = new H1Pos_HexahedronElement(order);
|
||||
break;
|
||||
case Geometry::PRISM:
|
||||
fe = new H1Pos_WedgeElement(order);
|
||||
break;
|
||||
case Geometry::PYRAMID:
|
||||
fe = new H1Pos_PyramidElement(order);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return fe;
|
||||
}
|
||||
|
||||
FiniteElement * GetL2PosFiniteElement(Geometry::Type type, int order)
|
||||
{
|
||||
FiniteElement *fe = NULL;
|
||||
switch (type)
|
||||
{
|
||||
case Geometry::SEGMENT:
|
||||
fe = new L2Pos_SegmentElement(order);
|
||||
break;
|
||||
case Geometry::TRIANGLE:
|
||||
fe = new L2Pos_TriangleElement(order);
|
||||
break;
|
||||
case Geometry::SQUARE:
|
||||
fe = new L2Pos_QuadrilateralElement(order);
|
||||
break;
|
||||
case Geometry::TETRAHEDRON:
|
||||
fe = new L2Pos_TetrahedronElement(order);
|
||||
break;
|
||||
case Geometry::CUBE:
|
||||
fe = new L2Pos_HexahedronElement(order);
|
||||
break;
|
||||
case Geometry::PRISM:
|
||||
fe = new L2Pos_WedgeElement(order);
|
||||
break;
|
||||
case Geometry::PYRAMID:
|
||||
fe = new L2Pos_PyramidElement(order);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return fe;
|
||||
}
|
||||
|
||||
TEST_CASE("Positive H1 Bases",
|
||||
"[H1Pos_SegmentElement]"
|
||||
"[H1Pos_TriangleElement]"
|
||||
"[H1Pos_QuadrilateralElement]"
|
||||
"[H1Pos_TetrahedronElement]"
|
||||
"[H1Pos_HexahedronElement]"
|
||||
"[H1Pos_WedgeElement]"
|
||||
"[H1Pos_PyramidElement]")
|
||||
{
|
||||
const int maxOrder = 5;
|
||||
const int resolution = 10;
|
||||
|
||||
auto geom = GENERATE(Geometry::SEGMENT,
|
||||
Geometry::TRIANGLE, Geometry::SQUARE,
|
||||
Geometry::TETRAHEDRON, Geometry::CUBE,
|
||||
Geometry::PRISM, Geometry::PYRAMID);
|
||||
auto p = GENERATE_COPY(range(1, maxOrder + 1));
|
||||
|
||||
CAPTURE(geom);
|
||||
CAPTURE(p);
|
||||
|
||||
SECTION("H1 Basis Summation")
|
||||
{
|
||||
FiniteElement *fe = GetH1PosFiniteElement(geom, p);
|
||||
|
||||
int dim = fe->GetDim();
|
||||
int ndof = fe->GetDof();
|
||||
Vector ones(ndof); ones = 1.0;
|
||||
Vector zeros(dim);
|
||||
Vector shape(ndof);
|
||||
DenseMatrix dshape(ndof, dim);
|
||||
|
||||
// Get a uniform grid of integration points
|
||||
RefinedGeometry* ref = GlobGeometryRefiner.Refine( fe->GetGeomType(),
|
||||
resolution);
|
||||
const IntegrationRule& intRule = ref->RefPts;
|
||||
|
||||
int npoints = intRule.GetNPoints();
|
||||
for (int i=0; i < npoints; ++i)
|
||||
{
|
||||
// Get the current integration point from intRule
|
||||
IntegrationPoint pt = intRule.IntPoint(i);
|
||||
|
||||
fe->CalcShape(pt, shape);
|
||||
|
||||
// Verify that the basis functions are non-negative
|
||||
REQUIRE(shape.Min() >= -2*std::numeric_limits<real_t>::epsilon());
|
||||
|
||||
// Verify that the basis functions sum to one
|
||||
REQUIRE(shape * ones == MFEM_Approx(1.0));
|
||||
|
||||
// Verify that the basis functions are non-negative
|
||||
REQUIRE(shape.Norml1() == MFEM_Approx(1.0));
|
||||
|
||||
fe->CalcDShape(pt, dshape);
|
||||
|
||||
dshape.MultTranspose(ones, zeros);
|
||||
|
||||
// Verify that the gradients sum to zero
|
||||
REQUIRE(zeros.Norml2() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
delete fe;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Positive L2 Bases",
|
||||
"[L2Pos_SegmentElement]"
|
||||
"[L2Pos_TriangleElement]"
|
||||
"[L2Pos_QuadrilateralElement]"
|
||||
"[L2Pos_TetrahedronElement]"
|
||||
"[L2Pos_HexahedronElement]"
|
||||
"[L2Pos_WedgeElement]"
|
||||
"[L2Pos_PyramidElement]")
|
||||
{
|
||||
const int maxOrder = 5;
|
||||
const int resolution = 10;
|
||||
|
||||
auto geom = GENERATE(Geometry::SEGMENT,
|
||||
Geometry::TRIANGLE, Geometry::SQUARE,
|
||||
Geometry::TETRAHEDRON, Geometry::CUBE,
|
||||
Geometry::PRISM, Geometry::PYRAMID);
|
||||
auto p = GENERATE_COPY(range(0, maxOrder + 1));
|
||||
|
||||
CAPTURE(geom);
|
||||
CAPTURE(p);
|
||||
|
||||
SECTION("L2 Basis Summation")
|
||||
{
|
||||
FiniteElement *fe = GetL2PosFiniteElement(geom, p);
|
||||
|
||||
int dim = fe->GetDim();
|
||||
int ndof = fe->GetDof();
|
||||
Vector ones(ndof); ones = 1.0;
|
||||
Vector zeros(dim);
|
||||
Vector shape(ndof);
|
||||
DenseMatrix dshape(ndof, dim);
|
||||
|
||||
// Get a uniform grid of integration points
|
||||
RefinedGeometry* ref = GlobGeometryRefiner.Refine( fe->GetGeomType(),
|
||||
resolution);
|
||||
const IntegrationRule& intRule = ref->RefPts;
|
||||
|
||||
int npoints = intRule.GetNPoints();
|
||||
for (int i=0; i < npoints; ++i)
|
||||
{
|
||||
// Get the current integration point from intRule
|
||||
IntegrationPoint pt = intRule.IntPoint(i);
|
||||
|
||||
fe->CalcShape(pt, shape);
|
||||
|
||||
// Verify that the basis functions are non-negative
|
||||
REQUIRE(shape.Min() >= -2*std::numeric_limits<real_t>::epsilon());
|
||||
|
||||
// Verify that the basis functions sum to one
|
||||
REQUIRE(shape * ones == MFEM_Approx(1.0));
|
||||
|
||||
// Verify that the basis functions are non-negative
|
||||
REQUIRE(shape.Norml1() == MFEM_Approx(1.0));
|
||||
|
||||
fe->CalcDShape(pt, dshape);
|
||||
|
||||
dshape.MultTranspose(ones, zeros);
|
||||
|
||||
// Verify that the gradients sum to zero
|
||||
REQUIRE(zeros.Norml2() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
delete fe;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user