Compare commits

..
Author SHA1 Message Date
Tucker Hartland 0e5f41221f including an obstacle problem variant wherein the essential dofs are not given to the optimizer as free variables that it must choose the values for. 2024-03-01 14:58:16 -08:00
Tucker Hartland d2794dd0da removing bug in the evaluation of the optimality measures wherein all processors now see the same value of the optimality error 2023-11-10 10:43:20 -08:00
Tucker Hartland 88372c99ee clean up... using a GeneralOptimizationProblem class and an OptimizationProblem class and no longer using the specific ContactProblem class as the generality of the Parent class does not have anything per say to do with contact 2023-10-10 11:33:29 -07:00
Tucker Hartland 52bfc8618d Merge branch 'contactIPM-dev' of https://github.com/mfem/mfem into contactIPM-dev 2023-08-07 10:07:55 -07:00
Tucker Hartland 977cb839b4 adding the ability to use the Hessian of the constraints in the optimizer, for the two sided Dirichlet obstacle problem it is observed that the number of outer Newton iterations is relatively constant only when the Hessian of the constraints are included in the Newton linear system 2023-08-07 10:06:35 -07:00
Socratis Petrides a52c7f036a bug fix 2023-07-25 19:20:04 -07:00
Socratis Petrides 39c32b9454 removing leftover print 2023-07-25 17:01:44 -07:00
Socratis Petrides 49041b062b simplifying dirichlet dofs handling 2023-07-25 16:26:21 -07:00
Socratis Petrides ed2d0846f6 small edits fixing compiler warnings 2023-07-24 14:34:22 -07:00
Tucker Hartland a1f6b3cf42 renaming descriptions of Optimization Problems... the general optimization problem which is more useful for PDE- and bound-constrained optimization is now described just as that GeneralOptProblem... the problem format min E(u) s.t. g(u) >= 0 and where (as the interior-point solver sees) the bound-constrained variable is a slack variable is now the less general but fairly descriptive OptProblem... the QPOptProblem (formerly QPContactProblem) now is more aptly described as there is nothing specific to contact that the class invokes 2023-07-21 12:40:04 -07:00
Tucker Hartland b8f4fbc84f updating a problem description 2023-07-21 10:11:17 -07:00
Tucker Hartland a2fef97289 removing the distinction between DirichletObstacle and ObstacleProblems 2023-07-21 10:06:58 -07:00
Tucker Hartland 07f22f98d3 removing DirichletObstacleProblem in favor of the ObstacleProblem class which has the ability to utilize Dirichlet boundary conditions 2023-07-20 18:44:34 -07:00
Tucker Hartland 9d5f4fd4ea removed all usages of typedef Number and all unnecessary ipopt stuff... moving meshes to the example/contact subdirectory and out of data directory... makefile now copies the meshes to the out of source directory when appropriate... problems now have more functionality for the inclusion of the Hessian of the constraint functions 2023-07-20 16:40:48 -07:00
tuckerh 17139c5fe8 fixing issue of a null Jacobian that shows up with certain builds/compilers/systems... the order of the gap function evaluation and gap function Jacobian in exQPContact was out of order and the Jacobian that is owned by the problem and passed to main was being deleted after a subsequent call of the gap function. Strange that this issue did not show up with all compilers 2023-07-20 09:51:05 -07:00
Tucker Hartland 3d201cd4ef removing comment about how function is leaking memory, since it is no longer leaking memory 2023-07-19 16:42:07 -07:00
Tucker Hartland 272f816245 fixing a memory leak via a FreeData call on the FindPointsGSLIB finder object in FindPointsInMesh 2023-07-19 16:29:54 -07:00
Tucker Hartland d18539aae2 fixing bug when not compiled with SUITESPARSE 2023-07-19 14:13:25 -07:00
Tucker Hartland 03aa2308d1 using more utility functions... ownership of blocks of IP-Newton system now owned by the problem and not the optimizer/solver... less copying in QPcontact and removing more of the functionality for ipopt 2023-07-17 17:36:44 -07:00
Tucker Hartland 27bcb49294 removing unnecessary copy when using dyanmic_cast, as well as removing unnecessary delete of the dynamic_cast variable to avoid dangling pointers 2023-07-17 15:27:23 -07:00
Tucker Hartland 90ca8bd551 including the QP contact block problem example... the infastructure in problems, additional meshes in data 2023-07-17 11:09:18 -07:00
Tucker Hartland 762fbedb42 more descriptions in Parallel Spherical example problem, as well as outputting the error of the numerical solution and that of the analytic solution 2023-07-06 12:09:32 -07:00
Tucker Hartland 028bbfbc20 Spherical obstacle problem in parallel... more consistent/descriptive variables for the parallel interior-point solver...including a parallel Dirichlet obstacle problem description 2023-07-05 17:35:17 -07:00
Tucker Hartland c2f85de2f9 added a new example wherein the Dirichlet condition and the obstacle coincide 2023-07-03 18:26:04 -07:00
Tucker Hartland c4c9962a0b small modification of the DirichletObstacleProblem that fixes a memory leak 2023-07-03 13:08:27 -07:00
tuckerh 5d15f6c2eb removing an unnecessary item 2023-06-29 13:38:48 -07:00
tuckerh 64e3cf5a52 making the ParIPSolver consistent with the IPsolver.... in particular the reduced print messages and also what the linSolver i.d.s correspond to 2023-06-29 13:18:43 -07:00
Tucker Hartland 81c0094f96 Spherical obstacle problem -- solving an obstacle problem with Dirichlet boundary conditions, able to check numerical solution against analytic to see convergence of the optimizer under mesh refinement to the analytic solution. problems.*pp contains new functionality for being able to describe an obstacle problem with Dirichlet boundary conditions. There has been a minor change to the IPsolver, so that there are fewer statements about the intermediate status of the optimizer 2023-06-29 12:35:21 -07:00
Tucker Hartland 91d8d5da29 removing unnecessary iterative solve option with the Schur-complement approximation Huu + D of the true Schur-complement Huu + Ju^T D Ju 2023-06-06 13:37:57 -07:00
tuckerh e4dd3399ac adding direct and iterative method options to solve IP-Newton-contact system via the Schur complement/stiffness by contact system matrix... including option to set the linear solve tolerance used by the iterative solver from the application code 2023-05-25 08:41:24 -07:00
Tucker Hartland aeff128c42 altering the logic of how the linear solver is chosen in the interior-point method, specifically to expose the newly available iterative method to builds that do not use SUITESPARSE 2023-05-25 07:14:05 -07:00
Tucker Hartland 617c9e2f21 adding new linear solver options and reducing the optimization tolerance so as to avoid conflicts with tolerances for Krylov-subspace solvers 2023-05-24 18:28:39 -07:00
Tucker Hartland 9fd2ae2229 using more descriptive language to describe various member functions 2023-05-24 10:57:18 -07:00
tuckerh bec6766ddb having sorted out the issue with the HypreMatrix from blocks function, we now remove unnecessary print statements and also include the fix 2023-05-15 17:07:02 -07:00
tuckerh 81fa2ca541 updating so that things are current even though there are failures with cpardiso on quartz 2023-05-15 14:12:35 -07:00
tuckerh dadfad3321 updated makefile so we can use either MUMPS or CPARDISO for the parallel sparse direct solver... using NULL instead of nullptr in order to try to be more consistent with the use of HypreParMatrixFromBlocks 2023-05-15 10:00:39 -07:00
tuckerh 18d3d7182c adding ability to use CPardiso sparse direct solvers 2023-05-12 13:23:47 -07:00
Socratis Petrides 994310c49e bug fix 2023-05-10 18:27:10 -07:00
Socratis Petrides 6396772300 first iteration of obstacle problem in parallel 2023-05-09 16:30:50 -07:00
Socratis Petrides a2b5fca7d4 minor changes to fix compiler warnings 2023-05-09 16:30:00 -07:00
Tucker Hartland 891a1aa72d new Mult function included in IPSolver so that the user does not need to be aware of the variable for which the bound-constraints are applied m >= ml, this is useful for contact mechanics problems wherein the bound-constraint variable is actually a slack variable and will not be especially important to the user. obstacleProblem.cpp is now cleaned up a bit, there is no longer a need to create a BlockVector and use this as input to the Mult IPM solution call and so it has been removed in favor of just working with a Vector representation of the primal variable 2023-05-05 15:21:50 -07:00
Tucker Hartland 332cc0e9da including another argument in the constructor of the abstract ContactProblem class, so that we pass the number of inequality constraints, this removes the need to copy the so-called boiler plate code into each of the child ContractProblem classes 2023-05-05 14:13:09 -07:00
Tucker Hartland 82c14b7544 altering various optimization problem member functions so that they no longer are of void type and take a reference to a SparseMatrix pointer but rather they just return a SparseMatrix pointer. the obstacleProblem now expects a function pointer to be passed when initialized, so that the right hand side forcing term is specified from the application side 2023-05-04 13:25:34 -07:00
Tucker Hartland c4cc5b600b adding initial serial features for contact... optimizer and the obstacleProblem. 2023-04-18 11:24:44 -07:00
330 changed files with 21059 additions and 38588 deletions
+6 -8
View File
@@ -162,7 +162,7 @@ jobs:
- name: get hypre
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.4
uses: mfem/github-actions/build-hypre@v2.2
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -171,7 +171,7 @@ jobs:
- name: get hypre (Windows)
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.4
uses: mfem/github-actions/build-hypre@v2.2
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -190,7 +190,7 @@ jobs:
- name: install metis
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.4
uses: mfem/github-actions/build-metis@v2.2
with:
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
@@ -217,7 +217,7 @@ jobs:
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v2.4
uses: mfem/github-actions/build-mfem@v2.3
env:
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
with:
@@ -263,15 +263,13 @@ jobs:
if: matrix.build-system == 'cmake' && matrix.target == 'opt' && matrix.os != 'ubuntu-latest'
run: |
CTEST_CONFIG="Release"
cd ${{ env.MFEM_TOP_DIR }}/build && \
ctest --output-on-failure -C ${CTEST_CONFIG} || \
ctest --rerun-failed --output-on-failure -C ${CTEST_CONFIG}
cd ${{ env.MFEM_TOP_DIR }}/build && ctest --output-on-failure -C ${CTEST_CONFIG}
shell: bash
# Code coverage (process and upload reports)
- name: codecov
if: matrix.codecov == 'YES'
uses: mfem/github-actions/upload-coverage@v2.4
uses: mfem/github-actions/upload-coverage@v2.2
with:
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
project_dir: ${{ env.MFEM_TOP_DIR }}
+3 -3
View File
@@ -57,7 +57,7 @@ jobs:
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.4
uses: mfem/github-actions/build-hypre@v2.2
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -72,14 +72,14 @@ jobs:
- name: Install Metis
if: steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.4
uses: mfem/github-actions/build-metis@v2.2
with:
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build-mfem
uses: mfem/github-actions/build-mfem@v2.4
uses: mfem/github-actions/build-mfem@v2.2
with:
os: ${{ runner.os }}
target: opt
-70
View File
@@ -1,70 +0,0 @@
# Copyright (c) 2010-2023, 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.
name: "Sanitizer"
permissions:
actions: write
on:
push:
branches:
- master
- next
pull_request:
workflow_dispatch:
jobs:
Serial:
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.11.0
with:
access_token: ${{ github.token }}
- name: MFEM Checkout
uses: actions/checkout@v3
with:
path: mfem
- name: MFEM Build
uses: mfem/github-actions/build-mfem@v2.4
with:
os: ${{ runner.os }}
target: opt
mpi: seq
hypre-dir: unused-hypre-dir
metis-dir: unused-metis-dir
mfem-dir: mfem
build-system: make
library-only: false
config-options:
CXX="clang++-14"
CXXFLAGS="-g -O1 -std=c++11
-fsanitize=address
-fno-omit-frame-pointer
-fsanitize-address-use-after-scope"
- name: MFEM Info
working-directory: mfem
run: make info
- name: MFEM Sanitize
working-directory: mfem
run:
ASAN_OPTIONS="detect_leaks=1,
strict_init_order=1,
strict_string_checks=1,
check_initialization_order=1,
detect_stack_use_after_return=1"
make test
-20
View File
@@ -29,7 +29,6 @@ CMakeFiles/
config/_config.hpp
config/config.mk
config/sample-runs-build.log
config/user.cmake
config/user.mk
doc/CodeDocumentation.conf
doc/CodeDocumentation.html
@@ -215,7 +214,6 @@ miniapps/meshing/pmesh-fitting
miniapps/meshing/minimal-surface
miniapps/meshing/pminimal-surface
miniapps/meshing/polar-nc
miniapps/meshing/mesh-quality
miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
miniapps/meshing/toroid-*.mesh
@@ -318,28 +316,12 @@ miniapps/solvers/ParaView
miniapps/solvers/mesh.*
miniapps/solvers/sol.*
miniapps/hdiv-linear-solver/darcy
miniapps/hdiv-linear-solver/grad_div
miniapps/parelag/MultilevelHcurlHdivSolver
miniapps/parelag/*.mesh
miniapps/multidomain/multidomain
miniapps/hooke/hooke
miniapps/dpg/diffusion
miniapps/dpg/pdiffusion
miniapps/dpg/convection-diffusion
miniapps/dpg/pconvection-diffusion
miniapps/dpg/acoustics
miniapps/dpg/pacoustics
miniapps/dpg/maxwell
miniapps/dpg/pmaxwell
miniapps/dpg/ParaView
miniapps/spde/generate_random_field
miniapps/spde/ParaView
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
@@ -352,8 +334,6 @@ tests/unit/tmop_pa_tests_*
tests/unit/ptmop_pa_tests_*
tests/unit/ceed_tests
tests/unit/debug_device_tests
tests/unit/parallel_in_serial.mesh
tests/unit/parallel_in_serial.gf
# Benchmark binaries
tests/benchmarks/bench_ceed
+10 -6
View File
@@ -22,10 +22,12 @@
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
while ! flock -w 5 9; do
true
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
@@ -55,10 +57,12 @@
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
while ! flock -w 5 9; do
true
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
+5 -3
View File
@@ -47,10 +47,12 @@ setup_baseline:
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
while ! flock -w 5 9; do
true
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
+11 -7
View File
@@ -35,11 +35,13 @@ setup:
(
date
echo "Waiting to acquire lock on '$PWD/mfem-data.lock' ..."
# try to get an exclusive lock on fd 9 (mfem-data.lock) repeating the
# try every 5 seconds; we may want to add a counter for the number of
# try to get an exclusive lock on fd 9 (mfem-data.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
while ! flock -w 5 9; do
true
done
echo "Acquired lock on '$PWD/mfem-data.lock'"
date
@@ -67,10 +69,12 @@ setup:
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
while ! flock -w 5 9; do
true
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
+3 -3
View File
@@ -14,14 +14,14 @@ stages:
- build_and_test
- report
opt_mpi_cuda_xl_16_1_1_12:
opt_mpi_cuda_xl_16_1_1_8:
variables:
SPEC: "%xl@16.1.1.12 +mpi +cuda cuda_arch=70"
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=70"
extends: .build_and_test_on_lassen
opt_mpi_cuda_hypre_cuda_xl:
variables:
SPEC: "%xl@16.1.1.12 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
extends: .build_and_test_on_lassen
# Jobs report
+9 -11
View File
@@ -51,8 +51,6 @@ cleanup:
script:
- echo "BUILD_ROOT=${BUILD_ROOT}"
- rm -rf "${BUILD_ROOT}" || true
- echo "CI_PROJECT_DIR=${CI_PROJECT_DIR}"
- make -C "${CI_PROJECT_DIR}" distclean
report_baseline:
extends: [.on_quartz]
@@ -68,10 +66,12 @@ report_baseline:
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
while ! flock -w 5 9; do
true
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
@@ -82,14 +82,12 @@ report_baseline:
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
if [[ -f ${rundir}/${BASELINE_TEST}.err ]]; then
cp ${rundir}/${BASELINE_TEST}.err ${rundir}/autotest-email.html
fi
printf "%s\n" "" "Pipeline URL:" "$CI_PIPELINE_URL" \
>> ${rundir}/pipeline.txt
# We create an autotest-email.html file, because that's how we signal
# that there was an error / diff (temporary).
if [[ -f ${rundir}/${BASELINE_TEST}.err ]] || \
[[ -f ${rundir}/${BASELINE_TEST}-${SYS_TYPE}.diff ]]; then
cp ${rundir}/pipeline.txt ${rundir}/autotest-email.html
fi
msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
git pull && \
+14 -14
View File
@@ -27,39 +27,39 @@ allocate_resource:
timeout: 6h
# GitLab jobs for the Quartz machine at LLNL
debug_ser_gcc_10:
debug_ser_gcc_6_1_0:
variables:
SPEC: "%gcc@10.3.1 +debug~mpi"
SPEC: "%gcc@6.1.0 +debug~mpi"
extends: .build_and_test_on_quartz
debug_par_gcc_10:
debug_par_gcc_6_1_0:
variables:
SPEC: "%gcc@10.3.1 +debug+mpi"
SPEC: "%gcc@6.1.0 +debug+mpi"
extends: .build_and_test_on_quartz
opt_ser_gcc_10:
opt_ser_gcc_6_1_0:
variables:
SPEC: "%gcc@10.3.1 ~mpi"
SPEC: "%gcc@6.1.0 ~mpi"
extends: .build_and_test_on_quartz
opt_par_gcc_10:
opt_par_gcc_6_1_0:
variables:
SPEC: "%gcc@10.3.1"
SPEC: "%gcc@6.1.0"
extends: .build_and_test_on_quartz
opt_par_gcc_10_sundials:
opt_par_gcc_6_1_0_sundials:
variables:
SPEC: "%gcc@10.3.1 +sundials"
SPEC: "%gcc@6.1.0 +sundials"
extends: .build_and_test_on_quartz
opt_par_gcc_10_petsc:
opt_par_gcc_6_1_0_petsc:
variables:
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
SPEC: "%gcc@6.1.0 +petsc ^petsc+mumps~superlu-dist"
extends: .build_and_test_on_quartz
opt_par_gcc_10_pumi:
opt_par_gcc_6_1_0_pumi:
variables:
SPEC: "%gcc@10.3.1 +pumi"
SPEC: "%gcc@6.1.0 +pumi"
extends: .build_and_test_on_quartz
# Release
+28 -15
View File
@@ -42,34 +42,47 @@ fi
# post
mkdir ${artifacts_path}
status=0
if [[ -f ${BASELINE_TEST}.out ]]; then
cp ${BASELINE_TEST}.out ${artifacts_path}
fi
if [[ -s ${glob_err} ]]; then
echo "ERROR during ${BASELINE_TEST} execution"
echo "Here is the ${glob_err} file content"
if [[ -s ${glob_err} ]]
then
echo "ERROR during ${BASELINE_TEST} execution";
echo "Here is the ${glob_err} file content";
cat ${glob_err}
cp ${glob_err} ${artifacts_path}/${glob_err}
status=1
fi
if [[ -f ${base_patch} ]]; then
exit 1;
elif [[ ! -f ${base_patch} && ! -f ${base_out} ]]
then
echo "Something went WRONG in ${BASELINE_TEST}:";
echo "Either ${base_patch} or ${base_out} should exists";
exit 1;
elif [[ -f ${base_patch} ]]
then
echo "${BASELINE_TEST}: Differences found, patch generated"
cp ${base_patch} ${artifacts_path}/${base_patch}
elif [[ -f ${base_out} ]]; then
elif [[ -f ${base_out} ]]
then
echo "${BASELINE_TEST}: Differences found, replacement file generated"
cp ${base_out} ${artifacts_path}/${base_out}
fi
if [[ -f ${BASELINE_TEST}.out ]]; then
cp ${BASELINE_TEST}.out ${artifacts_path}
fi
# base_diff won't even exist if there is no difference.
if [[ -f ${base_diff} ]]; then
if [[ -f ${base_diff} ]]
then
echo "${BASELINE_TEST}: Relevant differences (filtered diff) ..."
cat ${base_diff}
cp ${base_diff} ${artifacts_path}/${base_diff}
status=1
# We create a .err file, because that's how we signal that there was a diff.
cp ${base_diff} ${artifacts_path}/gitlab-${BASELINE_TEST}-${MACHINE_NAME}.err
fi
if [[ $status -eq 0 ]]; then
if [[ ! -s ${base_diff} ]]
then
echo "${BASELINE_TEST}: PASSED"
true
else
echo "${BASELINE_TEST}: FAILED"
false
fi
exit $status
+5 -57
View File
@@ -11,39 +11,14 @@
Version 4.5.3 (development)
===========================
New and updated examples and miniapps
-------------------------------------
- Added a new miniapp, Mesh Quality, for evaluating mesh quality using size,
skewness, and aspect-ratio computed from the Jacobian of the transformation.
- Added a new miniapp for interface and boundary fitting to implicit domains
defined using level-set functions. See miniapps/meshing/pmesh-fitting.cpp
- Added new Discontinuous Petrov-Galerkin (DPG) miniapp which includes serial
and parallel examples for diffusion, convection-diffusion, acoustics and
Maxwell equations. The miniapp includes new classes such as (Par)DPGWeakForm,
(Par)ComplexDPGWeakForm and (Complex)BlockStaticCondensation. Three new
integrators are added in support of DPG systems: TraceIntegrator,
NormalTraceIntegrator and TangentTraceIntegrator.
- Added a new H(div) solvers miniapp in miniapps/hdiv-linear-solver,
demonstrating the use of a matrix-free saddle-point solver methodology,
suitable for high-order discretizations and for GPU acceleration. Examples
illustrating the solution of Darcy and grad-div problems are included.
- Added a random refinement option to the mesh-explorer miniapp to assist users
in experimenting with nonconforming meshes.
- Moved the distance solver methods from miniapps/shifted to miniapps/common.
Meshing improvements
--------------------
- Added new methods in the Mesh class to set and get attributes on NURBS patches
and patch boundaries.
- TMOP improvement: added asymptotically-balanced compound metrics 90, 94, 328,
338. Added the tmop-metric-magnitude tool for tracking how metrics change
under geometric perturbations.
New and updated examples and miniapps
-------------------------------------
- Added a miniapp pmesh-fitting in miniapps/meshing for interface and boundary fitting to implicit domains defined using level-set functions.
- Moved the distance solver methods from miniapps/shifted to miniapps/common.
Discretization improvements
---------------------------
@@ -52,33 +27,6 @@ Discretization improvements
- VectorFEBoundaryFluxLFIntegrator is now supported on device/GPU.
- Added support for p-refined meshes in FindPointsGSLIB.
Linear and nonlinear solvers
----------------------------
- Updated interface to MUMPS direct solver to support multiple right-hand
sides, block low-rank compression, builds using 64-bit integers, and other
improvements.
- Added an interface to the MKL Pardiso sparse direct solver developed by Intel.
This interface provides a serial (OpenMP shared memory) version of Pardiso for
use with SparseMatrix. This complements the existing parallel (MPI distributed
memory) version already available through the CPardiso MFEM integration.
Integrations, testing and documentation
---------------------------------------
- Added an address sanitizer GitHub action for a serial build/test on Ubuntu,
based on Clang/LLVM (https://clang.llvm.org/docs/AddressSanitizer.html).
Miscellaneous
-------------
- Improved lambda body debugging with the addition of mfem::forall functions.
These functions can take the place of the MFEM_FORALL macros, which have been
preserved for backwards compatibility.
- Reorganized files for bilinear form, linear form, and nonlinear form integrators
in the fem/integ/ subdirectory.
Version 4.5.2, released on March 23, 2023
=========================================
+3 -10
View File
@@ -82,7 +82,7 @@ if (MFEM_USE_CONDUIT OR
# * find_package(PETSc REQUIRED)
set(XSDK_ENABLE_C ON)
endif()
if (MFEM_USE_STRUMPACK OR MFEM_USE_MUMPS)
if (MFEM_USE_STRUMPACK)
# Just needed to find the MPI_Fortran libraries to link with
set(XSDK_ENABLE_Fortran ON)
endif()
@@ -333,7 +333,6 @@ endif()
if (MFEM_USE_MUMPS)
if (MFEM_USE_MPI)
find_package(MUMPS REQUIRED mumps_common pord)
set(MFEM_MUMPS_VERSION ${MUMPS_VERSION})
else()
message(FATAL_ERROR " *** MUMPS requires that MPI be enabled.")
endif()
@@ -467,18 +466,12 @@ if (MFEM_USE_ADIOS2)
find_package(ADIOS2 REQUIRED)
endif()
# MKL CPardiso
if (MFEM_USE_MKL_CPARDISO)
if (MFEM_USE_MPI)
find_package(MKL_CPARDISO REQUIRED MKL_SEQUENTIAL MKL_LP64 MKL_MPI_WRAPPER)
endif()
endif()
# MKL Pardiso
if (MFEM_USE_MKL_PARDISO)
find_package(MKL_PARDISO REQUIRED MKL_SEQUENTIAL MKL_LP64)
endif()
# PARELAG
if (MFEM_USE_PARELAG)
find_package(PARELAG REQUIRED)
@@ -528,8 +521,8 @@ find_package(Threads REQUIRED)
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
ADIOS2 CUBLAS CUSPARSE MKL_CPARDISO MKL_PARDISO AMGX CALIPER CODIPACK
BENCHMARK PARELAG MPI_CXX HIP HIPSPARSE MOONOLITH BLITZ ALGOIM ENZYME)
ADIOS2 CUBLAS CUSPARSE MKL_CPARDISO AMGX CALIPER CODIPACK BENCHMARK PARELAG
MPI_CXX HIP HIPSPARSE MOONOLITH BLITZ ALGOIM ENZYME)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
+1 -3
View File
@@ -121,7 +121,6 @@ The MFEM source code has the following structure:
├── fem
│ ├── ceed
│ ├── fe
│ ├── integ
│ ├── lor
│ ├── moonolith
│ ├── qinterp
@@ -137,7 +136,6 @@ The MFEM source code has the following structure:
│ ├── common
│ ├── electromagnetics
│ ├── gslib
│ ├── hdiv-linear-solver
│ ├── hooke
│ ├── meshing
│ ├── mtop
@@ -211,7 +209,7 @@ device/host memory manager.
- The main device-relevant classes and sources are:
+ [`Device`](https://docs.mfem.org/html/device_8hpp.html)
+ [`MemoryManager`](https://docs.mfem.org/html/mem_manager_8hpp.html)
+ the [`mfem::forall`](https://docs.mfem.org/html/forall_8hpp.html) function
+ the [`MFEM_FORALL`](https://docs.mfem.org/html/forall_8hpp.html) macro
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
#### Utilities, building and documentation
+58 -63
View File
@@ -80,102 +80,97 @@
// Internal MFEM option: enable group/batch allocation for some small objects.
#cmakedefine MFEM_USE_MEMALLOC
// Which library functions to use in class StopWatch for measuring time.
// For a list of the available options, see INSTALL.
// If not defined, an option is selected automatically.
#cmakedefine MFEM_TIMER_TYPE @MFEM_TIMER_TYPE@
// Enable MFEM functionality based on the SUNDIALS libraries.
#cmakedefine MFEM_USE_SUNDIALS
// Enable MFEM functionality based on the SuiteSparse library.
#cmakedefine MFEM_USE_SUITESPARSE
// Enable MFEM functionality based on the SuperLU_DIST library.
#cmakedefine MFEM_USE_SUPERLU
#cmakedefine MFEM_USE_SUPERLU5
// Enable MFEM functionality based on the MUMPS library.
#cmakedefine MFEM_USE_MUMPS
#cmakedefine MFEM_MUMPS_VERSION @MFEM_MUMPS_VERSION@
// Enable MFEM functionality based on the STRUMPACK library.
#cmakedefine MFEM_USE_STRUMPACK
// Enable functionality based on the Ginkgo library.
// Enable functionality based on the Ginkgo library
#cmakedefine MFEM_USE_GINKGO
// Enable MFEM functionality based on the AmgX library.
// Enable MFEM functionality based on the AmgX library
#cmakedefine MFEM_USE_AMGX
// Enable secure socket streams based on the GNUTLS library.
// Enable MFEM functionality based on the GnuTLS library
#cmakedefine MFEM_USE_GNUTLS
// Enable Sidre support.
#cmakedefine MFEM_USE_SIDRE
// Enable the use of SIMD in the high performance templated classes.
#cmakedefine MFEM_USE_SIMD
// Enable FMS support.
#cmakedefine MFEM_USE_FMS
// Enable Conduit support.
#cmakedefine MFEM_USE_CONDUIT
// Enable functionality based on the NetCDF library (reading CUBIT files).
#cmakedefine MFEM_USE_NETCDF
// Enable functionality based on the PETSc library.
#cmakedefine MFEM_USE_PETSC
// Enable functionality based on the SLEPc library.
#cmakedefine MFEM_USE_SLEPC
// Enable functionality based on the MPFR library.
#cmakedefine MFEM_USE_MPFR
// Enable MFEM functionality based on the PUMI library.
#cmakedefine MFEM_USE_PUMI
// Enable Moonolith-based general interpolation between finite element spaces.
#cmakedefine MFEM_USE_MOONOLITH
// Enable MFEM functionality based on the HIOP library.
#cmakedefine MFEM_USE_HIOP
// Enable MFEM functionality based on the GSLIB library.
// Enable MFEM functionality based on the GSLIB library
#cmakedefine MFEM_USE_GSLIB
// Build the NVIDIA GPU/CUDA-enabled version of the MFEM library.
// Enable MFEM functionality based on the NetCDF library
#cmakedefine MFEM_USE_NETCDF
// Enable MFEM functionality based on the PETSc library
#cmakedefine MFEM_USE_PETSC
// Enable MFEM functionality based on the SLEPc library
#cmakedefine MFEM_USE_SLEPC
// Enable MFEM functionality based on the Sidre library
#cmakedefine MFEM_USE_SIDRE
// Enable the use of SIMD in the high performance templated classes
#cmakedefine MFEM_USE_SIMD
// Enable MFEM functionality based on the FMS library
#cmakedefine MFEM_USE_FMS
// Enable MFEM functionality based on Conduit
#cmakedefine MFEM_USE_CONDUIT
// Enable MFEM functionality based on the PUMI library
#cmakedefine MFEM_USE_PUMI
// Enable MFEM functionality based on the Moonolith library
#cmakedefine MFEM_USE_MOONOLITH
// Enable MFEM functionality based on the HiOp library
#cmakedefine MFEM_USE_HIOP
// Build the GPU/CUDA-enabled version of the MFEM library.
// Requires a CUDA compiler (nvcc).
#cmakedefine MFEM_USE_CUDA
// Build the AMD GPU/HIP-enabled version of the MFEM library.
// Build the HIP-enabled version of the MFEM library.
// Requires a HIP compiler (hipcc).
#cmakedefine MFEM_USE_HIP
// Enable functionality based on the RAJA library.
// Enable MFEM functionality based on the RAJA library
#cmakedefine MFEM_USE_RAJA
// Enable functionality based on the OCCA library.
// Enable MFEM functionality based on the OCCA library
#cmakedefine MFEM_USE_OCCA
// Enable functionality based on the libCEED library.
// Enable MFEM functionality based on the libCEED library
#cmakedefine MFEM_USE_CEED
// Enable functionality based on the Caliper library.
#cmakedefine MFEM_USE_CALIPER
// Enable functionality based on the Algoim library.
#cmakedefine MFEM_USE_ALGOIM
// Enable functionality based on the Umpire library.
// Enable MFEM functionality based on the Umpire library
#cmakedefine MFEM_USE_UMPIRE
// Enable IO functionality based on the ADIOS2 library.
// Enable MFEM functionality based on the ADIOS2 library
#cmakedefine MFEM_USE_ADIOS2
// Enable MFEM functionality based on the Caliper library
#cmakedefine MFEM_USE_CALIPER
// Enable MFEM functionality based on the Algoim library
#cmakedefine MFEM_USE_ALGOIM
// Which library functions to use in class StopWatch for measuring time.
// For a list of the available options, see INSTALL.
// If not defined, an option is selected automatically.
#define MFEM_TIMER_TYPE @MFEM_TIMER_TYPE@
// Enable MFEM functionality based on the SUNDIALS libraries.
#cmakedefine MFEM_USE_SUNDIALS
// Version of HYPRE used for building MFEM.
#cmakedefine MFEM_HYPRE_VERSION @MFEM_HYPRE_VERSION@
@@ -186,13 +181,13 @@
// Enable interface to the MKL CPardiso library.
#cmakedefine MFEM_USE_MKL_CPARDISO
// Use forward mode for automatic differentiation.
// Use forward mode for automatic differentiation
#cmakedefine MFEM_USE_ADFORWARD
// Enable the use of the CoDiPack library for AD.
// Enable the use of the CoDiPack library for AD
#cmakedefine MFEM_USE_CODIPACK
// Enable functionality based on the Google Benchmark library.
// Enable MFEM functionality based on the Google Benchmark library.
#cmakedefine MFEM_USE_BENCHMARK
// Enable Enzyme for AD
@@ -1,27 +0,0 @@
# Copyright (c) 2010-2023, 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.
# Defines the following variables:
# - MKL_PARDISO_FOUND
# - MKL_PARDISO_LIBRARIES
# - MKL_PARDISO_INCLUDE_DIRS
if(NOT MKL_LIBRARY_DIR)
message(WARNING "Using default MKL library path. Double check the variable MKL_LIBRARY_DIR")
set(MKL_LIBRARY_DIR "lib/intel64")
endif()
include(MfemCmakeUtilities)
mfem_find_package(MKL_PARDISO MKL_PARDISO
MKL_PARDISO_DIR "include" mkl_pardiso.h ${MKL_LIBRARY_DIR} mkl_core
"Paths to headers required by MKL Pardiso." "Libraries required by MKL PARDISO."
ADD_COMPONENT MKL_LP64 "include" "" ${MKL_LIBRARY_DIR} mkl_intel_lp64
ADD_COMPONENT MKL_SEQUENTIAL "include" "" ${MKL_LIBRARY_DIR} mkl_sequential)
+1 -17
View File
@@ -11,9 +11,8 @@
# Sets the following variables:
# - MUMPS_FOUND
# - MUMPS_LIBRARIES
# - MUMPS_INCLUDE_DIRS
# - MUMPS_VERSION
# - MUMPS_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(MUMPS MUMPS MUMPS_DIR
@@ -22,18 +21,3 @@ mfem_find_package(MUMPS MUMPS MUMPS_DIR
"Libraries required by MUMPS."
ADD_COMPONENT mumps_common "include" dmumps_c.h "lib" mumps_common
ADD_COMPONENT pord "include" dmumps_c.h "lib" pord)
if (MUMPS_FOUND AND (NOT MUMPS_VERSION))
try_run(MUMPS_VERSION_RUN_RESULT MUMPS_VERSION_COMPILE_RESULT
${CMAKE_CURRENT_BINARY_DIR}/config
${CMAKE_CURRENT_SOURCE_DIR}/config/get_mumps_version.cpp
CMAKE_FLAGS -DINCLUDE_DIRECTORIES:STRING=${MUMPS_INCLUDE_DIRS}
RUN_OUTPUT_VARIABLE MUMPS_VERSION_OUTPUT)
if ((MUMPS_VERSION_RUN_RESULT EQUAL 0) AND MUMPS_VERSION_OUTPUT)
string(STRIP "${MUMPS_VERSION_OUTPUT}" MUMPS_VERSION)
set(MUMPS_VERSION ${MUMPS_VERSION} CACHE STRING "MUMPS version." FORCE)
message(STATUS "Found MUMPS version ${MUMPS_VERSION}")
else()
message(FATAL_ERROR "Unable to determine MUMPS version.")
endif()
endif()
+16 -19
View File
@@ -30,10 +30,10 @@
#define MFEM_VERSION_MINOR (((MFEM_VERSION)/100)%100)
#define MFEM_VERSION_PATCH ((MFEM_VERSION)%100)
// The absolute path of the MFEM source prefix.
// The absolute path of the MFEM source prefix
// #define MFEM_SOURCE_DIR "@MFEM_SOURCE_DIR@"
// The absolute path of the MFEM installation prefix.
// The absolute path of the MFEM installation prefix
// #define MFEM_INSTALL_DIR "@MFEM_INSTALL_DIR@"
// Description of the git commit used to build MFEM.
@@ -91,7 +91,7 @@
// Enable MFEM functionality based on the SuiteSparse library.
// #define MFEM_USE_SUITESPARSE
// Enable MFEM functionality based on the SuperLU_DIST library.
// Enable MFEM functionality based on the SuperLU library.
// #define MFEM_USE_SUPERLU
// #define MFEM_USE_SUPERLU5
@@ -102,40 +102,40 @@
// Enable MFEM functionality based on the STRUMPACK library.
// #define MFEM_USE_STRUMPACK
// Enable MFEM features based on the Ginkgo library.
// Enable MFEM features based on the Ginkgo library
// #define MFEM_USE_GINKGO
// Enable MFEM functionality based on the AmgX library.
// #define MFEM_USE_AMGX
// Enable secure socket streams based on the GNUTLS library.
// Enable secure socket streams based on the GNUTLS library
// #define MFEM_USE_GNUTLS
// Enable Sidre support.
// Enable Sidre support
// #define MFEM_USE_SIDRE
// Enable the use of SIMD in the high performance templated classes.
// Enable the use of SIMD in the high performance templated classes
// #define MFEM_USE_SIMD
// Enable FMS support.
// Enable FMS support
// #define MFEM_USE_FMS
// Enable Conduit support.
// Enable Conduit support
// #define MFEM_USE_CONDUIT
// Enable functionality based on the NetCDF library (reading CUBIT files).
// Enable functionality based on the NetCDF library (reading CUBIT files)
// #define MFEM_USE_NETCDF
// Enable functionality based on the PETSc library.
// Enable functionality based on the PETSc library
// #define MFEM_USE_PETSC
// Enable functionality based on the SLEPc library.
// Enable functionality based on the SLEPc library
// #define MFEM_USE_SLEPC
// Enable functionality based on the MPFR library.
// #define MFEM_USE_MPFR
// Enable MFEM functionality based on the PUMI library.
// Enable MFEM functionality based on the PUMI library
// #define MFEM_USE_PUMI
// Enable Moonolith-based general interpolation between finite element spaces.
@@ -144,7 +144,7 @@
// Enable MFEM functionality based on the HIOP library.
// #define MFEM_USE_HIOP
// Enable MFEM functionality based on the GSLIB library.
// Enable MFEM functionality based on the GSLIB library
// #define MFEM_USE_GSLIB
// Build the NVIDIA GPU/CUDA-enabled version of the MFEM library.
@@ -186,13 +186,10 @@
// Enable interface to the MKL CPardiso library.
// #define MFEM_USE_MKL_CPARDISO
// Enable interface to the MKL Pardiso library.
// #define MFEM_USE_MKL_PARDISO
// Use forward mode for automatic differentiation.
// Use forward mode for automatic differentiation
// #define MFEM_USE_ADFORWARD
// Enable the use of the CoDiPack library for AD.
// Enable the use of the CoDiPack library for AD
// #define MFEM_USE_CODIPACK
// Enable functionality based on the Google Benchmark library.
-1
View File
@@ -57,7 +57,6 @@ MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
MFEM_USE_SIMD = @MFEM_USE_SIMD@
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
MFEM_USE_MKL_CPARDISO = @MFEM_USE_MKL_CPARDISO@
MFEM_USE_MKL_PARDISO = @MFEM_USE_MKL_PARDISO@
MFEM_USE_MOONOLITH = @MFEM_USE_MOONOLITH@
MFEM_USE_ADFORWARD = @MFEM_USE_ADFORWARD@
MFEM_USE_CODIPACK = @MFEM_USE_CODIPACK@
+5 -10
View File
@@ -60,7 +60,6 @@ option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
option(MFEM_USE_CALIPER "Enable Caliper support" OFF)
option(MFEM_USE_ALGOIM "Enable Algoim support" OFF)
option(MFEM_USE_MKL_CPARDISO "Enable MKL CPardiso" OFF)
option(MFEM_USE_MKL_PARDISO "Enable MKL Pardiso" OFF)
option(MFEM_USE_ADFORWARD "Enable forward mode for AD" OFF)
option(MFEM_USE_CODIPACK "Enable automatic differentiation (AD) using CoDiPack" OFF)
option(MFEM_USE_BENCHMARK "Enable Google Benchmark" OFF)
@@ -135,18 +134,16 @@ set(ParMETIS_DIR "${MFEM_DIR}/../parmetis-4.0.3" CACHE PATH
set(ParMETIS_REQUIRED_PACKAGES "METIS" CACHE STRING
"Additional packages required by ParMETIS.")
set(SuperLUDist_DIR "${MFEM_DIR}/../SuperLU_DIST_8.1.2" CACHE PATH
set(SuperLUDist_DIR "${MFEM_DIR}/../SuperLU_DIST_6.3.1" CACHE PATH
"Path to the SuperLU_DIST library.")
# SuperLU_DIST may also depend on "OpenMP", depending on how it was compiled.
set(SuperLUDist_REQUIRED_PACKAGES "MPI" "ParMETIS" "METIS"
"LAPACK" "BLAS" CACHE STRING
set(SuperLUDist_REQUIRED_PACKAGES "MPI" "BLAS" "ParMETIS" CACHE STRING
"Additional packages required by SuperLU_DIST.")
set(MUMPS_DIR "${MFEM_DIR}/../MUMPS_5.5.0" CACHE PATH
set(MUMPS_DIR "${MFEM_DIR}/../MUMPS_5.2.0" CACHE PATH
"Path to the MUMPS library.")
# MUMPS may also depend on "OpenMP", depending on how it was compiled.
set(MUMPS_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "ParMETIS" "METIS"
"ScaLAPACK" "LAPACK" "BLAS" CACHE STRING
# Packages required by MUMPS, depending on how it was compiled.
set(MUMPS_REQUIRED_PACKAGES "MPI" "BLAS" "METIS" "ScaLAPACK" CACHE STRING
"Additional packages required by MUMPS.")
# If the MPI package does not find all required Fortran libraries:
# set(MUMPS_REQUIRED_LIBRARIES "gfortran" "mpi_mpifh" CACHE STRING
@@ -229,8 +226,6 @@ set(MKL_CPARDISO_DIR "" CACHE STRING "MKL installation path.")
set(MKL_MPI_WRAPPER_LIB "mkl_blacs_mpich_lp64" CACHE STRING "MKL MPI wrapper library")
set(MKL_LIBRARY_DIR "" CACHE STRING "Custom library subdirectory")
set(MKL_PARDISO_DIR "" CACHE STRING "MKL installation path.")
set(OCCA_DIR "${MFEM_DIR}/../occa" CACHE PATH "Path to OCCA")
set(RAJA_DIR "${MFEM_DIR}/../raja" CACHE PATH "Path to RAJA")
set(CEED_DIR "${MFEM_DIR}/../libCEED" CACHE PATH "Path to libCEED")
+4 -12
View File
@@ -160,7 +160,6 @@ MFEM_USE_UMPIRE = NO
MFEM_USE_SIMD = NO
MFEM_USE_ADIOS2 = NO
MFEM_USE_MKL_CPARDISO = NO
MFEM_USE_MKL_PARDISO = NO
MFEM_USE_MOONOLITH = NO
MFEM_USE_ADFORWARD = NO
MFEM_USE_CODIPACK = NO
@@ -285,10 +284,10 @@ ifeq ($(MFEM_USE_SUPERLU5),YES)
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib\
-lsuperlu_dist_5.1.0
else
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_8.1.2
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_6.3.1
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64\
-lsuperlu_dist $(LAPACK_LIB)
-lsuperlu_dist -lblas
endif
# SCOTCH library configuration (required by STRUMPACK <= v2.1.0, optional in
@@ -312,7 +311,7 @@ MPI_FORTRAN_LIB = -lmpifort
# MPI_FORTRAN_LIB += -lgfortran
# MUMPS library configuration
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.5.0
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.2.0
MUMPS_OPT = -I$(MUMPS_DIR)/include
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
-lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
@@ -485,6 +484,7 @@ ifdef GOTCHA_DIR
CALIPER_LIB += $(XLINKER)-rpath,$(GOTCHA_DIR)/lib64 $(XLINKER)-rpath,$(GOTCHA_DIR)/lib -L$(GOTCHA_DIR)/lib64 -L$(GOTCHA_DIR)/lib -lgotcha
endif
# BLITZ library configuration
BLITZ_DIR = @MFEM_DIR@/../blitz
BLITZ_OPT = -I$(BLITZ_DIR)/include
@@ -539,14 +539,6 @@ MKL_CPARDISO_LIB = $(XLINKER)-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
# MKL Pardiso library configuration
MKL_PARDISO_DIR ?=
MKL_LIBRARY_SUBDIR ?= lib
MKL_PARDISO_OPT = -I$(MKL_PARDISO_DIR)/include
MKL_PARDISO_LIB = $(XLINKER)-rpath,$(MKL_PARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
-L$(MKL_PARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
# PARELAG library configuration
PARELAG_DIR = @MFEM_DIR@/../parelag
PARELAG_OPT = -I$(PARELAG_DIR)/src -I$(PARELAG_DIR)/build/src
+109
View File
@@ -0,0 +1,109 @@
MFEM NURBS mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# SEGMENT = 1
# SQUARE = 3
# CUBE = 5
#
dimension
2
elements
5
1 3 4 5 6 7
1 3 0 1 5 4
1 3 1 2 6 5
1 3 3 7 6 2
1 3 0 4 7 3
boundary
4
1 1 0 1
1 1 2 3
1 1 1 2
1 1 3 0
edges
12
0 0 1
0 4 5
0 7 6
0 3 2
1 1 2
1 5 6
1 4 7
1 0 3
2 0 4
2 1 5
2 2 6
2 3 7
vertices
8
knotvectors
3
2 3 0 0 0 1 1 1
2 3 0 0 0 1 1 1
2 3 0 0 0 1 1 1
weights
1
1
1
1
1
1
1
1
0.70710678118655
1
1
0.70710678118655
0.70710678118655
1
1
0.70710678118655
1
1
1
1
1
0.85355339059327
0.85355339059327
0.85355339059327
0.85355339059327
FiniteElementSpace
FiniteElementCollection: NURBS2
VDim: 2
Ordering: 1
-0.70710678118 -0.70710678118
0.70710678118 -0.70710678118
0.70710678118 0.70710678118
-0.70710678118 0.70710678118
-0.35355339059 -0.35355339059
0.35355339059 -0.35355339059
0.35355339059 0.35355339059
-0.35355339059 0.35355339059
0 -1.41421356236
0 -0.35355339059
0 0.35355339059
0 1.41421356236
1.41421356236 0
0.35355339059 0
-0.35355339059 0
-1.41421356236 0
-0.530330085885 -0.530330085885
0.530330085885 -0.530330085885
0.530330085885 0.530330085885
-0.530330085885 0.530330085885
0 0
0 -0.883883476475
0.883883476475 0
0 0.883883476475
-0.883883476475 0
+1 -5
View File
@@ -795,7 +795,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/common \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
@MFEM_SOURCE_DIR@/miniapps/hooke \
@MFEM_SOURCE_DIR@/miniapps/hooke/kernels \
@MFEM_SOURCE_DIR@/miniapps/hooke/materials \
@@ -811,10 +810,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/shifted \
@MFEM_SOURCE_DIR@/miniapps/solvers \
@MFEM_SOURCE_DIR@/miniapps/tools \
@MFEM_SOURCE_DIR@/miniapps/toys \
@MFEM_SOURCE_DIR@/miniapps/spde \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util
@MFEM_SOURCE_DIR@/miniapps/toys
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+2 -6
View File
@@ -39,7 +39,7 @@ namespace mfem {
* - Device
* - Memory
* - MemoryManager
* - mfem::forall functions in forall.hpp
* - MFEM_FORALL macro in forall.hpp
*
* <H3>Example codes</H3>
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Laplace problem
@@ -186,7 +186,6 @@ namespace mfem {
* - <a class="el" href="extruder_8cpp_source.html">Extruder</a>: extrude a low-dimensional mesh into a higher dimension
* - <a class="el" href="mesh-explorer_8cpp_source.html">Mesh Explorer</a>: visualize and manipulate meshes
* - <a class="el" href="mesh-optimizer_8cpp_source.html">Mesh Optimizer</a>: optimize high-order meshes, <a class="el" href="mesh-optimizer_8cpp_source.html">serial</a> and <a class="el" href="pmesh-optimizer_8cpp_source.html">parallel</a> versions
* - <a class="el" href="mesh-quality_8cpp_source.html">Mesh Quality</a>: visualize and check mesh quality
* - <a class="el" href="trimmer_8cpp_source.html">Trimmer</a>: trim elements from existing meshes
* - <a class="el" href="display-basis_8cpp_source.html">Display Basis</a>: visualize finite element basis functions
* - <a class="el" href="get-values_8cpp_source.html">Get Values</a>: extract field values via DataCollection classes
@@ -199,15 +198,12 @@ namespace mfem {
* - <a class="el" href="distance_8cpp_source.html">Distance</a>: finite element distance function solver
* - <a class="el" href="diffusion_8cpp_source.html">Shifted Diffusion</a>: shifted boundary diffusion solver
* - <a class="el" href="extrapolate_8cpp_source.html">Extrapolation</a>: PDE-based extrapolation of finite element functions
* - <a class="el" href="block-solvers_8cpp_source.html">Block Solvers</a>: comparison of saddle point system solvers
* - <a class="el" href="distance_8cpp_source.html">Block Solvers</a>: comparison of saddle point system solvers
* - <a class="el" href="parheat_8cpp_source.html">Optimization gradients</a>: Gradients of PDE-constrained function
* - <a class="el" href="par__example_8cpp_source.html">Parallel AD</a>: Parallel p-Laplacian example
* - <a class="el" href="seq__example_8cpp_source.html">Serial AD</a>: Serial p-Laplacian example
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
*
* See also the <a class="el" href="https://mfem.org/examples/">examples documentation</a> online.
*/
-11
View File
@@ -40,8 +40,6 @@ list(APPEND ALL_EXE_SRCS
ex30.cpp
ex31.cpp
ex33.cpp
ex34.cpp
ex35.cpp
)
if (MFEM_USE_MPI)
@@ -163,15 +161,6 @@ if (MFEM_ENABLE_TESTING)
$<TARGET_FILE:ex11p> "-no-vis" "--superlu"
${MPIEXEC_POSTFLAGS})
endif()
# If MUMPS is enabled, add a test run that uses it.
if (MFEM_USE_MUMPS)
add_test(NAME ex25p_mumps_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:ex25p> "-no-vis" "--mumps-solver"
${MPIEXEC_POSTFLAGS})
endif()
endif()
# Include the examples/amgx directory if AmgX is enabled
@@ -0,0 +1,141 @@
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double fRhs(const Vector &pt);
double obstacle(const Vector &pt);
double dmanufacturedFun(const Vector &pt);
int main(int argc, char *argv[])
{
int FEorder = 1; // order of the finite elements
int linSolver = 0;
int maxIPMiters = 30;
bool iAmRoot = true;
int ref_levels = 3;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/inline-quad.mesh";
Mesh *mesh = new Mesh(meshFile, 1, 1);
int dim = mesh->Dimension(); // geometric dimension of the domain
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
FiniteElementSpace *Vh = new FiniteElementSpace(mesh, fec);
Array<int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
Vh->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
double DC_val = 0.0;
int dimD = Vh->GetTrueVSize();
Vector x0(dimD); x0 = DC_val;
Vector xf(dimD); xf = 0.0;
ObstacleProblem problem(Vh, x0, &fRhs, &obstacle, ess_tdof_list);
InteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
double Einitial = problem.E(x0);
double Efinal = problem.E(xf);
cout << "Energy objective at initial point = " << Einitial << endl;
cout << "Energy objective at optimizer = " << Efinal << endl;
GridFunction d_gf(Vh);
d_gf = xf;
FunctionCoefficient dm_fc(dmanufacturedFun); // pseudo-manufactured solution
GridFunction dm_gf(Vh);
dm_gf.ProjectCoefficient(dm_fc);
ParaViewDataCollection paraview_dc("BarrierProblemSolution", mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("d(x) (numerical)", &d_gf);
paraview_dc.RegisterField("d(x) (pseudo-manufactured)", &dm_gf);
paraview_dc.Save();
delete Vh;
delete fec;
delete mesh;
return 0;
}
double dmanufacturedFun(const Vector &pt)
{
double alpha = 16.5;
return sin(M_PI * pt(1)) * (sin(M_PI * pt(0)) - alpha * pow(pt(0) * (1. - pt(0)), 2));
}
// f(x) forcing term... which enters the objective energy functional
// E(d) = 0.5 d^T K d - f^T d, where f is a discrete vector representation
// of f(x). f(x) is such that in the absence of bound-constraints then
// the solution of the optimization problem satisfies the PDE
// -div(grad(d)) + d = f + homogeneous Neumann conditions on the unit interval,
// for d(x) = cos(2 \pi x) + a0 + a3 (x^3 - 1.5 x^2), a2 = 0.2, a3 = -2
double fRhs(const Vector &pt)
{
double alpha = 16.5;
double fx;
fx = pow(M_PI, 2) * sin(M_PI * pt(0));
fx += alpha * (2. * pow(pt(0), 2) + 2. * pow(1.-pt(0), 2) - 8. * pt(0) * (1.-pt(0)));
fx += pow(M_PI, 2) * sin(M_PI * pt(0)) * dmanufacturedFun(pt);
fx *= sin(M_PI * pt(1));
return fx;
}
double obstacle(const Vector &pt)
{
return 0.0;
}
@@ -0,0 +1,156 @@
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double fRhs(const Vector &pt);
double obstacle(const Vector &pt);
double dmanufacturedFun(const Vector &pt);
int main(int argc, char *argv[])
{
int FEorder = 1; // order of the finite elements
int linSolver = 0;
int maxIPMiters = 30;
bool iAmRoot = true;
int ref_levels = 3;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/inline-quad.mesh";
Mesh *mesh = new Mesh(meshFile, 1, 1);
int dim = mesh->Dimension(); // geometric dimension of the domain
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
FiniteElementSpace *Vh = new FiniteElementSpace(mesh, fec);
Array<int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
Vh->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
double DC_val = 0.06;
Vector x0DC(Vh->GetTrueVSize()); x0DC = DC_val;
int dimD = Vh->GetTrueVSize() - ess_tdof_list.Size();
Vector x0(dimD); x0 = 0.0;
Vector xf(dimD); xf = 0.0;
ObstacleProblemVariant problem(Vh, x0DC, &fRhs, &obstacle, ess_tdof_list);
InteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
double Einitial = problem.E(x0);
double Efinal = problem.E(xf);
cout << "Energy objective at initial point = " << Einitial << endl;
cout << "Energy objective at optimizer = " << Efinal << endl;
Array<int> noness_tdof_list;
noness_tdof_list.SetSize(dimD);
int i = 0;
for(int j = 0; j < Vh->GetTrueVSize(); j++)
{
if(ess_tdof_list.Find(j) == -1)
{
noness_tdof_list[i] = j;
i += 1;
}
}
GridFunction d_gf(Vh);
d_gf.Set(1.0, x0DC);
d_gf.SetSubVector(noness_tdof_list, xf);
FunctionCoefficient dm_fc(dmanufacturedFun); // pseudo-manufactured solution
GridFunction dm_gf(Vh);
dm_gf.ProjectCoefficient(dm_fc);
ParaViewDataCollection paraview_dc("BarrierProblemSolution", mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("d(x) (numerical)", &d_gf);
paraview_dc.RegisterField("d(x) (pseudo-manufactured)", &dm_gf);
paraview_dc.Save();
delete Vh;
delete fec;
delete mesh;
return 0;
}
double dmanufacturedFun(const Vector &pt)
{
double alpha = 16.5;
return sin(M_PI * pt(1)) * (sin(M_PI * pt(0)) - alpha * pow(pt(0) * (1. - pt(0)), 2));
}
// f(x) forcing term... which enters the objective energy functional
// E(d) = 0.5 d^T K d - f^T d, where f is a discrete vector representation
// of f(x). f(x) is such that in the absence of bound-constraints then
// the solution of the optimization problem satisfies the PDE
// -div(grad(d)) + d = f + homogeneous Neumann conditions on the unit interval,
// for d(x) = cos(2 \pi x) + a0 + a3 (x^3 - 1.5 x^2), a2 = 0.2, a3 = -2
double fRhs(const Vector &pt)
{
double alpha = 16.5;
double fx;
fx = pow(M_PI, 2) * sin(M_PI * pt(0));
fx += alpha * (2. * pow(pt(0), 2) + 2. * pow(1.-pt(0), 2) - 8. * pt(0) * (1.-pt(0)));
fx += pow(M_PI, 2) * sin(M_PI * pt(0)) * dmanufacturedFun(pt);
fx *= sin(M_PI * pt(1));
return fx;
}
double obstacle(const Vector &pt)
{
return 0.0;
}
+827
View File
@@ -0,0 +1,827 @@
#include "mfem.hpp"
#include "IPsolver.hpp"
#include "Problems.hpp"
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace mfem;
InteriorPointSolver::InteriorPointSolver(GeneralOptProblem * Problem) : optProblem(Problem), block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
Huu(nullptr), Hum(nullptr), Hmu(nullptr), Hmm(nullptr), Wmm(nullptr), D(nullptr), Ju(nullptr), Jm(nullptr), JuT(nullptr), JmT(nullptr), Huucl(nullptr), HLuu(nullptr), saveLogBarrierIterates(false)
{
rel_tol = 1.e-2;
max_iter = 20;
mu_k = 1.0;
sMax = 1.e2;
kSig = 1.e10; // control deviation from primal Hessian
tauMin = 0.8; // control rate at which iterates can approach the boundary
eta = 1.e-4; // backtracking constant
thetaMin = 1.e-4; // allowed violation of the equality constraints
// constants in line-step A-5.4
delta = 1.0;
sTheta = 1.1;
sPhi = 2.3;
// control the rate at which the penalty parameter is decreased
kMu = 0.2;
thetaMu = 1.5;
// TO DO -- include the filter
thetaMax = 1.e6; // maximum constraint violation
// data for the second order correction
kSoc = 0.99;
// equation (18)
gTheta = 1.e-5;
gPhi = 1.e-5;
kEps = 1.e1;
dimU = optProblem->GetDimU();
dimM = optProblem->GetDimM();
dimC = optProblem->GetDimC();
ckSoc.SetSize(dimC);
block_offsetsumlz[0] = 0;
block_offsetsumlz[1] = dimU; // u
block_offsetsumlz[2] = dimM; // m
block_offsetsumlz[3] = dimC; // lambda
block_offsetsumlz[4] = dimM; // zl
block_offsetsumlz.PartialSum();
for(int i = 0; i < block_offsetsuml.Size(); i++) { block_offsetsuml[i] = block_offsetsumlz[i]; }
for(int i = 0; i < block_offsetsx.Size(); i++) { block_offsetsx[i] = block_offsetsuml[i] ; }
// lower-bound for the inequality constraint m >= ml
ml = optProblem->Getml();
lk.SetSize(dimC); lk = 0.0;
zlk.SetSize(dimM); zlk = 0.0;
linSolver = 0;
MyRank = 0;
iAmRoot = MyRank == 0 ? true : false;
}
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
{
double alphaMaxloc = 1.0;
double alphaTmp;
for(int i = 0; i < x.Size(); i++)
{
if( xhat(i) < 0. )
{
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
alphaMaxloc = min(alphaMaxloc, alphaTmp);
}
}
// alphaMaxloc is the local maximum step size which is
// distinct on each MPI process. Need to compute
// the global maximum step size
double alphaMaxglb;
alphaMaxglb = alphaMaxloc;
return alphaMaxglb;
}
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
{
Vector zero(x.Size()); zero = 0.0;
return MaxStepSize(x, zero, xhat, tau);
}
void InteriorPointSolver::Mult(const Vector &x0, Vector &xf)
{
BlockVector x0block(block_offsetsx); x0block = 0.0;
x0block.GetBlock(0).Set(1.0, x0);
// To do: give options for user specificiation of initialization m0
x0block.GetBlock(1) = 100.;
x0block.GetBlock(1).Add(1.0, ml);
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
Mult(x0block, xfblock);
xf.Set(1.0, xfblock.GetBlock(0));
}
void InteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
{
converged = false;
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
Vector zlhat(dimM); zlhat = 0.0;
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
// running estimate of the final values of the Lagrange multipliers
lk = 0.0;
zlk = 0.0;
for(int i = 0; i < dimM; i++)
{
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
}
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
/* set theta0 = theta(x0)
* thetaMin
* thetaMax
* when theta(xk) < thetaMin and the switching condition holds
* then we ask for the Armijo sufficient decrease of the barrier
* objective to be satisfied, in order to accept the trial step length alphakl
*
* thetaMax controls how the filter is initialized for each log-barrier subproblem
* F0 = {(th, phi) s.t. th > thetaMax}
* that is the filter does not allow for iterates where the constraint violation
* is larger than that of thetaMax
*/
double theta0 = theta(xk);
thetaMin = 1.e-4 * max(1.0, theta0);
thetaMax = 1.e8 * thetaMin;
double Eeval, maxBarrierSolves, Eevalmu0;
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
maxBarrierSolves = 10;
for(jOpt = 0; jOpt < max_iter; jOpt++)
{
mfem::out << "interior-point solve step " << jOpt << endl;
// A-2. Check convergence of overall optimization problem
printOptimalityError = false;
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
if(Eevalmu0 < rel_tol)
{
converged = true;
mfem::out << "solved optimization problem :)\n";
break;
}
if(jOpt > 0) { maxBarrierSolves = 1; }
for(int i = 0; i < maxBarrierSolves; i++)
{
// A-3. Check convergence of the barrier subproblem
printOptimalityError = true;
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
if(Eeval < kEps * mu_k)
{
mfem::out << "solved barrier subproblem, for mu = " << mu_k << endl;
// A-3.1. Recompute the barrier parameter
mu_k = max(rel_tol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
// A-3.2. Re-initialize the filter
F1.DeleteAll();
F2.DeleteAll();
}
else
{
break;
}
}
// A-4. Compute the search direction
// solve for (uhat, mhat, lhat)
mfem::out << "\n** A-4. IP-Newton solve **\n";
zlhat = 0.0; Xhatuml = 0.0;
// why do we have Xhatuml ....???
// TO DO: remove Xhatuml in favor of passing Xhat
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
// assign data stack, X = (u, m, l, zl)
Xk = 0.0;
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
Xhat = 0.0;
for(int i = 0; i < 3; i++)
{
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
}
Xhat.GetBlock(3).Set(1.0, zlhat);
// A-5. Backtracking line search.
mfem::out << "\n** A-5. Linesearch **\n";
mfem::out << "mu = " << mu_k << endl;
lineSearch(Xk, Xhat, mu_k);
if(lineSearchSuccess)
{
if(!switchCondition || !sufficientDecrease)
{
F1.Append( (1. - gTheta) * thx0);
F2.Append( phx0 - gPhi * thx0);
}
// ----- A-6: Accept the trial point
// print info regarding zl...
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
lk.Add(alpha, Xhat.GetBlock(2));
zlk.Add(alphaz, Xhat.GetBlock(3));
projectZ(xk, zlk, mu_k);
}
else
{
mfem::out << "lineSearch not successful :(\n";
mfem::out << "attempting feasibility restoration with theta = " << thx0 << endl;
mfem::out << "no feasibility restoration implemented, exiting now \n";
break;
}
//
if(jOpt + 1 == max_iter)
{
mfem::out << "maximum optimization iterations :(\n";
}
}
// done with optimization routine, just reassign data to xf reference so
// that the application code has access to the optimal point
xf = 0.0;
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
}
void InteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
{
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
Huu = optProblem->Duuf(x); Hum = optProblem->Dumf(x);
Hmu = optProblem->Dmuf(x); Hmm = optProblem->Dmmf(x);
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
for(int ii = 0; ii < dimM; ii++)
{
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
}
if(saveLogBarrierIterates)
{
std::ofstream diagStream;
char diagString[100];
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
diagStream.open(diagString, ios::out | ios::trunc);
for(int ii = 0; ii < dimM; ii++)
{
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
}
diagStream.close();
}
D = new SparseMatrix(DiagLogBar);
if(Hmm != nullptr)
{
Wmm = new SparseMatrix(*Hmm);
Wmm->Add(1.0, *D);
}
else
{
Wmm = D;
}
Ju = optProblem->Duc(x); JuT = Transpose(*Ju);
Jm = optProblem->Dmc(x); JmT = Transpose(*Jm);
Huucl = optProblem->lDuuc(x, l);
if(Huucl != nullptr)
{
HLuu = Add(*Huucl, *Huu);
Ak.SetBlock(0, 0, HLuu);
}
else
{
Ak.SetBlock(0, 0, Huu);
}
// IP-Newton system matrix
// Ak = [[H_(u,u) H_(u,m) J_u^T]
// [H_(m,u) W_(m,m) J_m^T]
// [ J_u J_m 0 ]]
Ak.SetBlock(0, 2, JuT);
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
}
// perturbed KKT system solve
// determine the search direction
void InteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
{
// solve A x = b, where A is the IP-Newton matrix
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
FormIPNewtonMat(x, l, zl, A);
// [grad_u phi + Ju^T l]
// b = - [grad_m phi + Jm^T l]
// [ c ]
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
BlockVector JTl(block_offsetsx); JTl = 0.0;
Dxphi(x, mu, gradphi);
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
for(int ii = 0; ii < 2; ii++)
{
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
}
if(!socSolve)
{
optProblem->c(x, b.GetBlock(2));
}
else
{
b.GetBlock(2).Set(1.0, ckSoc);
}
b *= -1.0;
Xhat = 0.0;
#ifdef MFEM_USE_SUITESPARSE
// Direct solve for IP-Newton saddle-point system
// A = [ [ Huu 0 Ju^T]
// [ 0 D -I ]
// [ Ju -I 0 ]]
if(linSolver == 0)
{
BlockMatrix ABlockMatrix(block_offsetsuml, block_offsetsuml);
for(int ii = 0; ii < 3; ii++)
{
for(int jj = 0; jj < 3; jj++)
{
if(!A.IsZeroBlock(ii, jj))
{
ABlockMatrix.SetBlock(ii, jj, dynamic_cast<SparseMatrix *>(&(A.GetBlock(ii, jj))));
}
}
}
/* direct solve of the 3x3 IP-Newton linear system */
UMFPackSolver ASolver;
SparseMatrix *ASparse = ABlockMatrix.CreateMonolithic();
ASolver.SetOperator(*ASparse);
ASolver.Mult(b, Xhat);
Vector residual(Xhat.Size());
ASparse->Mult(Xhat, residual);
residual.Add(-1.0, b);
delete ASparse;
}
else if(linSolver == 1)
{
// Direct solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// where Wmm = D for contact problems
SparseMatrix * Huuloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0)));
SparseMatrix * Wmmloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1)));
SparseMatrix * Juloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0)));
SparseMatrix * JuTloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2)));
Vector DVec(dimM); DVec = 0.0;
Vector one(dimM); one = 1.0;
D->Mult(one, DVec);
SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, DVec); // Ju^T D Ju
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
// solve the reduced linear system
UMFPackSolver AreducedSolver;
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete JuTDJu;
delete Areduced;
}
#else
MFEM_VERIFY(linSolver > 1, "linSolver = 0, 1 require MFEM_USE_SUITESPARSE=YES");
#endif
if(linSolver > 1)
{
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// where Wmm = D for contact problems
// here the iterative solver is a Jacobi-preconditioned CG-solve
SparseMatrix * Huuloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0)));
SparseMatrix * Wmmloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1)));
SparseMatrix * Juloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0)));
SparseMatrix * JuTloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2)));
Vector DVec(dimM); DVec = 0.0;
Vector one(dimM); one = 1.0;
D->Mult(one, DVec);
SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, DVec); // Ju^T D Ju
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
if (linSolver == 2)
{
/* Jacobi preconditioned conjugate-gradient solve */
DSmoother AreducedPrec((SparseMatrix &)(*Areduced));
CGSolver AreducedSolver;
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.SetAbsTol(1.e-12);
AreducedSolver.SetRelTol(1.e-8);
AreducedSolver.SetMaxIter(500);
AreducedSolver.SetPreconditioner(AreducedPrec);
AreducedSolver.SetPrintLevel(1);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
}
else
{
/* Gauss-Seidel preconditioned GMRES solve */
GSSmoother AreducedPrec((SparseMatrix &)(*Areduced));
GMRESSolver AreducedSolver;
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.SetAbsTol(1.e-12);
AreducedSolver.SetRelTol(1.e-8);
AreducedSolver.SetMaxIter(500);
AreducedSolver.SetPreconditioner(AreducedPrec);
AreducedSolver.SetPrintLevel(1);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
}
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete JuTDJu;
delete Areduced;
}
/* backsolve to determine zlhat */
for(int ii = 0; ii < dimM; ii++)
{
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
}
// free memory
if(Hmm != nullptr)
{
delete Wmm;
}
if( Huucl != nullptr)
{
delete HLuu; HLuu = nullptr;
}
delete D;
delete JuT;
delete JmT;
}
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
void InteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
{
double tau = max(tauMin, 1.0 - mu);
Vector u0 = X0.GetBlock(0);
Vector m0 = X0.GetBlock(1);
Vector l0 = X0.GetBlock(2);
Vector z0 = X0.GetBlock(3);
Vector uhat = Xhat.GetBlock(0);
Vector mhat = Xhat.GetBlock(1);
Vector lhat = Xhat.GetBlock(2);
Vector zhat = Xhat.GetBlock(3);
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
double alphaMaxz = MaxStepSize(z0, zhat, tau);
alphaz = alphaMaxz;
BlockVector x0(block_offsetsx); x0 = 0.0;
x0.GetBlock(0).Set(1.0, u0);
x0.GetBlock(1).Set(1.0, m0);
BlockVector xhat(block_offsetsx); xhat = 0.0;
xhat.GetBlock(0).Set(1.0, uhat);
xhat.GetBlock(1).Set(1.0, mhat);
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
int maxBacktrack = 20;
alpha = alphaMax;
Vector ck0(dimC); ck0 = 0.0;
Vector zhatsoc(dimM); zhatsoc = 0.0;
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
Vector uhatsoc(dimU); uhatsoc = 0.0;
Vector mhatsoc(dimM); mhatsoc = 0.0;
Dxphi(x0, mu, Dxphi0);
Dxphi0_xhat = InnerProduct(Dxphi0, xhat);
descentDirection = Dxphi0_xhat < 0. ? true : false;
if(descentDirection)
{
mfem::out << "is a descent direction for the log-barrier objective\n";
}
else
{
mfem::out << "is not a descent direction for the log-barrier objective\n";
}
mfem::out << "Dxphi^T xhat / (|| Dxphi||_2 * || xhat ||_2) = " << Dxphi0_xhat / (xhat.Norml2() * Dxphi0.Norml2()) << endl;
thx0 = theta(x0);
phx0 = phi(x0, mu);
lineSearchSuccess = false;
for(int i = 0; i < maxBacktrack; i++)
{
mfem::out << "\n--------- alpha = " << alpha << " ---------\n";
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
xtrial.Set(1.0, x0);
xtrial.Add(alpha, xhat);
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
thxtrial = theta(xtrial);
phxtrial = phi(xtrial, mu);
filterCheck(thxtrial, phxtrial);
if(!inFilterRegion)
{
mfem::out << "not in filter region :)\n";
// ------ A.5.4: Check sufficient decrease
if(!descentDirection)
{
switchCondition = false;
}
else
{
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
}
mfem::out << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
mfem::out << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
mfem::out << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
// Case I
if(thx0 <= thetaMin && switchCondition)
{
sufficientDecrease = phxtrial <= phx0 + eta * alpha * Dxphi0_xhat ? true : false;
if(sufficientDecrease)
{
mfem::out << "Accepted step length -- sufficient decrease in log-barrier objective.\n";
// accept the trial step
lineSearchSuccess = true;
break;
}
}
else
{
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
{
mfem::out << "Accepted step length -- decrease in either constraint violation or log-barrier objective.\n";
// accept the trial step
lineSearchSuccess = true;
break;
}
}
// A-5.5: Initialize the second-order correction
if((!(thx0 < thxtrial)) && i == 0)
{
mfem::out << "second order correction\n";
optProblem->c(xtrial, ckSoc);
optProblem->c(x0, ck0);
ckSoc.Add(alphaMax, ck0);
// A-5.6 Compute the second-order correction.
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
// alphasoc = MaxStepSize(m0, ml, mhatsoc, tau);
//WARNING: not complete but currently solver isn't entering this region
}
}
else
{
mfem::out << "in filter region\n";
}
// include more if needed
alpha *= 0.5;
}
}
void InteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
{
double zi;
double mudivmml;
for(int i = 0; i < dimM; i++)
{
zi = z(i);
mudivmml = mu / (x(i + dimU) - ml(i));
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
}
}
void InteriorPointSolver::filterCheck(double th, double ph)
{
inFilterRegion = false;
if(th > thetaMax)
{
inFilterRegion = true;
}
else
{
for(int i = 0; i < F1.Size(); i++)
{
if(th >= F1[i] && ph >= F2[i])
{
inFilterRegion = true;
break;
}
}
}
}
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool print)
{
double E1, E2, E3;
double sc, sd;
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
DxL(x, l, zl, gradL);
E1 = gradL.Normlinf();
optProblem->c(x, cx);
E2 = cx.Normlinf();
for(int ii = 0; ii < dimM; ii++)
{
comp(ii) = x(dimU + ii) * zl(ii) - mu;
}
E3 = comp.Normlinf();
double ll1, zl1;
zl1 = zl.Norml1() / double(dimC + dimM);
ll1 = l.Norml1();
sc = max(sMax, zl1 / (double(dimM)) ) / sMax;
sd = max(sMax, (ll1 + zl1) / (double(dimC + dimM))) / sMax;
if(print)
{
mfem::out << "evaluating optimality error for mu = " << mu << endl;
mfem::out << "stationarity measure = " << E1 / sd << endl;
mfem::out << "feasibility measure = " << E2 << endl;
mfem::out << "complimentarity measure = " << E3 / sc << endl;
}
return max(max(E1 / sd, E2), E3 / sc);
}
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool print)
{
return E(x, l, zl, 0.0, print);
}
double InteriorPointSolver::theta(const BlockVector &x)
{
Vector cx(dimC); cx = 0.0;
optProblem->c(x, cx);
return cx.Norml2();
}
// log-barrier objective
double InteriorPointSolver::phi(const BlockVector &x, double mu)
{
double fx = optProblem->CalcObjective(x);
double logBarrierLoc = 0.0;
for(int i = 0; i < dimM; i++)
{
logBarrierLoc += log(x(dimU+i)-ml(i));
}
double logBarrierGlb = 0.0;
logBarrierGlb = logBarrierLoc;
return fx - mu * logBarrierGlb;
}
// gradient of log-barrier objective with respect to x = (u, m)
void InteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
{
optProblem->CalcObjectiveGrad(x, y);
for(int i = 0; i < dimM; i++)
{
y(dimU + i) -= mu / (x(dimU + i));
}
}
// Lagrangian function evaluation
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
double InteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
{
double fx = optProblem->CalcObjective(x);
Vector cx(dimC); optProblem->c(x, cx);
return (fx + InnerProduct(cx, l) - InnerProduct(x.GetBlock(1), zl));
}
void InteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
{
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
optProblem->CalcObjectiveGrad(x, gradxf);
SparseMatrix *Jacu, *Jacm, *JacuT, *JacmT;
Jacu = optProblem->Duc(x); Jacm = optProblem->Dmc(x);
JacuT = Transpose(*Jacu);
JacmT = Transpose(*Jacm);
JacuT->Mult(l, y.GetBlock(0));
JacmT->Mult(l, y.GetBlock(1));
delete JacuT;
delete JacmT;
y.Add(1.0, gradxf);
(y.GetBlock(1)).Add(-1.0, zl);
}
bool InteriorPointSolver::GetConverged() const
{
return converged;
}
void InteriorPointSolver::SetTol(double Tol)
{
rel_tol = Tol;
}
void InteriorPointSolver::SetMaxIter(int max_it)
{
max_iter = max_it;
}
void InteriorPointSolver::SetBarrierParameter(double mu_0)
{
mu_k = mu_0;
}
void InteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
{
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
saveLogBarrierIterates = save;
}
void InteriorPointSolver::SetLinearSolver(int LinSolver)
{
linSolver = LinSolver;
}
InteriorPointSolver::~InteriorPointSolver()
{
F1.DeleteAll();
F2.DeleteAll();
block_offsetsx.DeleteAll();
block_offsetsumlz.DeleteAll();
block_offsetsuml.DeleteAll();
ml.SetSize(0);
}
+80
View File
@@ -0,0 +1,80 @@
#include "mfem.hpp"
#include "Problems.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifndef IPSOLVER
#define IPSOLVER
class InteriorPointSolver
{
protected:
GeneralOptProblem* optProblem;
double rel_tol;
int max_iter;
double mu_k; // \mu_k
Vector lk, zlk;
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
double thetaMax, kSoc, gTheta, gPhi, kEps;
// filter
Array<double> F1, F2;
// quantities computed in lineSearch
double alpha, alphaz;
double thx0, thxtrial;
double phx0, phxtrial;
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
double Dxphi0_xhat;
int dimU, dimM, dimC;
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
Vector ml;
Vector ckSoc;
SparseMatrix * Huu, * Hum, * Hmu, * Hmm, * Wmm, *D, * Ju, * Jm, * JuT, * JmT;
SparseMatrix * Huucl, *HLuu;
int jOpt;
bool converged;
int MyRank;
bool iAmRoot;
bool saveLogBarrierIterates;
int linSolver;
public:
InteriorPointSolver(GeneralOptProblem*);
void Mult(const BlockVector& , BlockVector&); // used when the user wants to be aware of bound-constrained variable m >= ml
void Mult(const Vector&, Vector &); // useful when the user doesn't need to know about bound-constrained variable m >= ml, e.g., when m is a slack variable
double MaxStepSize(Vector& , Vector& , Vector& , double);
double MaxStepSize(Vector& , Vector& , double);
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
void lineSearch(BlockVector& , BlockVector& , double);
void projectZ(const Vector & , Vector &, double);
void filterCheck(double, double);
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
double E(const BlockVector &, const Vector &, const Vector &, bool);
bool GetConverged() const;
// TO DO: include Hessian of Lagrangian
double theta(const BlockVector &);
double phi(const BlockVector &, double);
void Dxphi(const BlockVector &, double, BlockVector &);
double L(const BlockVector &, const Vector &, const Vector &);
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
void SetTol(double);
void SetMaxIter(int);
void SetBarrierParameter(double);
void SaveLogBarrierHessianIterates(bool);
void SetLinearSolver(int);
virtual ~InteriorPointSolver();
};
#endif
+125
View File
@@ -0,0 +1,125 @@
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double dmanufacturedFun(const Vector &);
double fRhs(const Vector &);
double obstacle(const Vector &);
int main(int argc, char *argv[])
{
int FEorder = 1; // order of the finite elements
int linSolver = 0;
int maxIPMiters = 30;
bool iAmRoot = true;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/inline-quad.mesh";
Mesh *mesh = new Mesh(meshFile, 1, 1);
int dim = mesh->Dimension(); // geometric dimension of the domain
{
int ref_levels = 3;
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
FiniteElementSpace *Vh = new FiniteElementSpace(mesh, fec);
ObstacleProblem problem(Vh, &fRhs, &obstacle);
int dimD = problem.GetDimD();
Vector x0(dimD); x0 = 0.0;
Vector xf(dimD); xf = 0.0;
InteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
double Einitial = problem->E(x0);
double Efinal = problem->E(xf);
cout << "Energy objective at initial point = " << Einitial << endl;
cout << "Energy objective at QP optimizer = " << Efinal << endl;
GridFunction d_gf(Vh);
d_gf = xf;
FunctionCoefficient dm_fc(dmanufacturedFun); // pseudo-manufactured solution
GridFunction dm_gf(Vh);
dm_gf.ProjectCoefficient(dm_fc);
ParaViewDataCollection paraview_dc("BarrierProblemSolution", mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("d(x) (numerical)", &d_gf);
paraview_dc.RegisterField("d(x) (pseudo-manufactured)", &dm_gf);
paraview_dc.Save();
delete Vh;
delete fec;
delete mesh;
return 0;
}
double dmanufacturedFun(const Vector &x)
{
return cos(2*M_PI*x(0)) + 0.2 - 2.0*(pow(x(0),3) - 1.5*pow(x(0),2));
}
// f(x) forcing term... which enters the objective energy functional
// E(d) = 0.5 d^T K d - f^T d, where f is a discrete vector representation
// of f(x). f(x) is such that in the absence of bound-constraints then
// the solution of the optimization problem satisfies the PDE
// -div(grad(d)) + d = f + homogeneous Neumann conditions on the unit interval,
// for d(x) = cos(2 \pi x) + a0 + a3 (x^3 - 1.5 x^2), a2 = 0.2, a3 = -2
double fRhs(const Vector &x)
{
double fx = 0.;
fx = 0.2 - 2.0 * (pow(x(0),3)- 1.5*pow(x(0),2.) - 6 * x(0) + 3.) + (1. + pow(2.*M_PI,2))*cos(2.*M_PI*x(0));
return fx;
}
double obstacle(const Vector &x)
{
return 0.0;
}
+834
View File
@@ -0,0 +1,834 @@
#include "mfem.hpp"
#include "ParIPsolver.hpp"
#include "ParProblems.hpp"
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace mfem;
ParInteriorPointSolver::ParInteriorPointSolver(ParGeneralOptProblem * problem_)
: problem(problem_),
block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
Huu(nullptr), Hum(nullptr), Hmu(nullptr),
Hmm(nullptr), Wmm(nullptr), D(nullptr),
Ju(nullptr), Jm(nullptr), JuT(nullptr), JmT(nullptr),
saveLogBarrierIterates(false)
{
OptTol = 1.e-2;
max_iter = 20;
mu_k = 1.0;
sMax = 1.e2;
kSig = 1.e10; // control deviation from primal Hessian
tauMin = 0.8; // control rate at which iterates can approach the boundary
eta = 1.e-4; // backtracking constant
thetaMin = 1.e-4; // allowed violation of the equality constraints
// constants in line-step A-5.4
delta = 1.0;
sTheta = 1.1;
sPhi = 2.3;
// control the rate at which the penalty parameter is decreased
kMu = 0.2;
thetaMu = 1.5;
thetaMax = 1.e6; // maximum constraint violation
// data for the second order correction
kSoc = 0.99;
// equation (18)
gTheta = 1.e-5;
gPhi = 1.e-5;
kEps = 1.e1;
dimU = problem->GetDimU();
dimM = problem->GetDimM();
dimC = problem->GetDimC();
MPI_Allreduce(&dimU, &dimUglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
MPI_Allreduce(&dimM, &dimMglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
MPI_Allreduce(&dimC, &dimCglb, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
ckSoc.SetSize(dimC);
block_offsetsumlz[0] = 0;
block_offsetsumlz[1] = dimU; // u
block_offsetsumlz[2] = dimM; // m
block_offsetsumlz[3] = dimC; // lambda
block_offsetsumlz[4] = dimM; // zl
block_offsetsumlz.PartialSum();
for(int i = 0; i < block_offsetsuml.Size(); i++)
{
block_offsetsuml[i] = block_offsetsumlz[i];
}
for(int i = 0; i < block_offsetsx.Size(); i++)
{
block_offsetsx[i] = block_offsetsuml[i] ;
}
ml = problem->Getml();
lk.SetSize(dimC); lk = 0.0;
zlk.SetSize(dimM); zlk = 0.0;
linSolver = 0;
linSolveTol = 1.e-8;
MyRank = Mpi::WorldRank();
iAmRoot = MyRank == 0 ? true : false;
}
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
{
double alphaMaxloc = 1.0;
double alphaTmp;
for(int i = 0; i < x.Size(); i++)
{
if( xhat(i) < 0. )
{
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
alphaMaxloc = min(alphaMaxloc, alphaTmp);
}
}
// alphaMaxloc is the local maximum step size which is
// distinct on each MPI process. Need to compute
// the global maximum step size
double alphaMaxglb;
MPI_Allreduce(&alphaMaxloc, &alphaMaxglb, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
return alphaMaxglb;
}
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
{
Vector zero(x.Size()); zero = 0.0;
return MaxStepSize(x, zero, xhat, tau);
}
void ParInteriorPointSolver::Mult(const Vector &x0, Vector &xf)
{
BlockVector x0block(block_offsetsx); x0block = 0.0;
x0block.GetBlock(0).Set(1.0, x0);
x0block.GetBlock(1) = 100.;
x0block.GetBlock(1).Add(1.0, ml);
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
Mult(x0block, xfblock);
xf.Set(1.0, xfblock.GetBlock(0));
}
void ParInteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
{
converged = false;
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
Vector zlhat(dimM); zlhat = 0.0;
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
// running estimate of the final values of the Lagrange multipliers
lk = 0.0;
zlk = 0.0;
for(int i = 0; i < dimM; i++)
{
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
}
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
/* set theta0 = theta(x0)
* thetaMin
* thetaMax
* when theta(xk) < thetaMin and the switching condition holds
* then we ask for the Armijo sufficient decrease of the barrier
* objective to be satisfied, in order to accept the trial step length alphakl
*
* thetaMax controls how the filter is initialized for each log-barrier subproblem
* F0 = {(th, phi) s.t. th > thetaMax}
* that is the filter does not allow for iterates where the constraint violation
* is larger than that of thetaMax
*/
double theta0 = theta(xk);
thetaMin = 1.e-4 * max(1.0, theta0);
thetaMax = 1.e8 * thetaMin; // 1.e4 * max(1.0, theta0)
double Eeval, maxBarrierSolves, Eevalmu0;
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
maxBarrierSolves = 10;
for(jOpt = 0; jOpt < max_iter; jOpt++)
{
if(iAmRoot)
{
cout << "interior-point solve step " << jOpt << endl;
}
// A-2. Check convergence of overall optimization problem
printOptimalityError = false;
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
if(Eevalmu0 < OptTol)
{
converged = true;
if(iAmRoot)
{
cout << "solved optimization problem :)\n";
}
break;
}
if(jOpt > 0) { maxBarrierSolves = 1; }
for(int i = 0; i < maxBarrierSolves; i++)
{
// A-3. Check convergence of the barrier subproblem
printOptimalityError = true;
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
if(iAmRoot)
{
cout << "E = " << Eeval << endl;
}
if(Eeval < kEps * mu_k)
{
if(iAmRoot)
{
cout << "solved barrier subproblem :), for mu = " << mu_k << endl;
}
// A-3.1. Recompute the barrier parameter
mu_k = max(OptTol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
// A-3.2. Re-initialize the filter
F1.DeleteAll();
F2.DeleteAll();
}
else
{
break;
}
}
// A-4. Compute the search direction
// solve for (uhat, mhat, lhat)
if(iAmRoot)
{
cout << "\n** A-4. IP-Newton solve **\n";
}
zlhat = 0.0; Xhatuml = 0.0;
// why do we have Xhatuml ....???
// TO DO: remove Xhatuml in favor of passing Xhat
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
// assign data stack, X = (u, m, l, zl)
Xk = 0.0;
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
Xhat = 0.0;
for(int i = 0; i < 3; i++)
{
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
}
Xhat.GetBlock(3).Set(1.0, zlhat);
// A-5. Backtracking line search.
if(iAmRoot)
{
cout << "\n** A-5. Linesearch **\n";
cout << "mu = " << mu_k << endl;
}
lineSearch(Xk, Xhat, mu_k);
if(lineSearchSuccess)
{
if(iAmRoot)
{
cout << "lineSearch successful :)\n";
}
if(!switchCondition || !sufficientDecrease)
{
F1.Append( (1. - gTheta) * thx0);
F2.Append( phx0 - gPhi * thx0);
}
// ----- A-6: Accept the trial point
// print info regarding zl...
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
lk.Add(alpha, Xhat.GetBlock(2));
zlk.Add(alphaz, Xhat.GetBlock(3));
projectZ(xk, zlk, mu_k);
}
else
{
if(iAmRoot)
{
cout << "lineSearch not successful :(\n";
cout << "attempting feasibility restoration with theta = " << thx0 << endl;
cout << "no feasibility restoration implemented, exiting now \n";
}
break;
}
if(jOpt + 1 == max_iter && iAmRoot)
{
cout << "maximum optimization iterations :(\n";
}
}
// done with optimization routine, just reassign data to xf reference so
// that the application code has access to the optimal point
xf = 0.0;
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
}
void ParInteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
{
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
Huu = problem->Duuf(x);
Hum = problem->Dumf(x);
Hmu = problem->Dmuf(x);
Hmm = problem->Dmmf(x);
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
for(int ii = 0; ii < dimM; ii++)
{
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
}
if(saveLogBarrierIterates)
{
std::ofstream diagStream;
char diagString[100];
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
diagStream.open(diagString, ios::out | ios::trunc);
for(int ii = 0; ii < dimM; ii++)
{
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
}
diagStream.close();
}
SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
ParFiniteElementSpace * fes = problem->GetfesM();
D = new HypreParMatrix(fes->GetComm(), fes->GlobalTrueVSize(), fes->GetTrueDofOffsets(), Ds);
HypreStealOwnership(*D,*Ds);
delete Ds;
if(Hmm != nullptr)
{
Wmm = Hmm;
Wmm->Add(1.0, *D);
}
else
{
Wmm = D;
}
Ju = problem->Duc(x); JuT = Ju->Transpose();
Jm = problem->Dmc(x); JmT = Jm->Transpose();
// IP-Newton system matrix
// Ak = [[H_(u,u) H_(u,m) J_u^T]
// [H_(m,u) W_(m,m) J_m^T]
// [ J_u J_m 0 ]]
Ak.SetBlock(0, 0, Huu); Ak.SetBlock(0, 2, JuT);
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
}
// perturbed KKT system solve
// determine the search direction
void ParInteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
{
// solve A x = b, where A is the IP-Newton matrix
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
FormIPNewtonMat(x, l, zl, A);
// [grad_u phi + Ju^T l]
// b = - [grad_m phi + Jm^T l]
// [ c ]
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
BlockVector JTl(block_offsetsx); JTl = 0.0;
Dxphi(x, mu, gradphi);
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
for(int ii = 0; ii < 2; ii++)
{
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
}
if(!socSolve)
{
problem->c(x, b.GetBlock(2));
}
else
{
b.GetBlock(2).Set(1.0, ckSoc);
}
b *= -1.0;
Xhat = 0.0;
// Direct solver (default)
if(linSolver == 0)
{
Array2D<HypreParMatrix *> ABlockMatrix(3,3);
for(int ii = 0; ii < 3; ii++)
{
for(int jj = 0; jj < 3; jj++)
{
if(!A.IsZeroBlock(ii, jj))
{
ABlockMatrix(ii, jj) = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(ii, jj)));
}
else
{
ABlockMatrix(ii, jj) = nullptr;
}
}
}
HypreParMatrix * Ah = HypreParMatrixFromBlocks(ABlockMatrix);
/* direct solve of the 3x3 IP-Newton linear system */
#ifdef MFEM_USE_MUMPS
MUMPSSolver ASolver;
ASolver.SetPrintLevel(0);
ASolver.SetMatrixSymType(MUMPSSolver::MatType::SYMMETRIC_INDEFINITE);
ASolver.SetOperator(*Ah);
ASolver.Mult(b, Xhat);
#else
#ifdef MFEM_USE_MKL_CPARDISO
CPardisoSolver ASolver(MPI_COMM_WORLD);
ASolver.SetOperator(*Ah);
ASolver.Mult(b, Xhat);
#else
MFEM_VERIFY(false, "linSolver 0 will not work unless compiled with MUMPS or MKL");
#endif
#endif
delete Ah;
}
else if(linSolver == 1 || linSolver == 2)
{
// form A = Huu + Ju^T D Ju, Wmm = D for contact
HypreParMatrix * Huuloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(0, 0)));
HypreParMatrix * Wmmloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(1, 1)));
HypreParMatrix * Juloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(2, 0)));
HypreParMatrix * JuTloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(0, 2)));
HypreParMatrix *JuTDJu = RAP(Wmmloc, Juloc); // Ju^T D Ju
HypreParMatrix *Areduced = ParAdd(Huuloc, JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
if(linSolver == 1)
{
// setup the solver for the reduced linear system
#ifdef MFEM_USE_MUMPS
MUMPSSolver AreducedSolver;
AreducedSolver.SetPrintLevel(0);
AreducedSolver.SetMatrixSymType(MUMPSSolver::MatType::SYMMETRIC_INDEFINITE);
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
#else
#ifdef MFEM_USE_MKL_CPARDISO
CPardisoSolver AreducedSolver(MPI_COMM_WORLD);
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
#else
MFEM_VERIFY(false, "linSolver 1 will not work unless compiled with MUMPS or MKL");
#endif
#endif
}
else
{
HyprePCG AreducedSolver(MPI_COMM_WORLD);
AreducedSolver.SetOperator(*Areduced);
HypreBoomerAMG AreducedPrec;
AreducedSolver.SetTol(linSolveTol);
AreducedSolver.SetMaxIter(500);
AreducedSolver.SetPreconditioner(AreducedPrec);
AreducedSolver.SetResidualConvergenceOptions(); // convergence criteria based on residual norm
AreducedSolver.SetPrintLevel(2);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
}
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete JuTDJu;
delete Areduced;
}
/* backsolve to determine zlhat */
for(int ii = 0; ii < dimM; ii++)
{
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
}
// free memory
delete D;
delete JuT;
delete JmT;
if(Hmm != nullptr)
{
delete Wmm;
}
}
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
void ParInteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
{
double tau = max(tauMin, 1.0 - mu);
Vector u0 = X0.GetBlock(0);
Vector m0 = X0.GetBlock(1);
Vector l0 = X0.GetBlock(2);
Vector z0 = X0.GetBlock(3);
Vector uhat = Xhat.GetBlock(0);
Vector mhat = Xhat.GetBlock(1);
Vector lhat = Xhat.GetBlock(2);
Vector zhat = Xhat.GetBlock(3);
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
double alphaMaxz = MaxStepSize(z0, zhat, tau);
alphaz = alphaMaxz;
BlockVector x0(block_offsetsx); x0 = 0.0;
x0.GetBlock(0).Set(1.0, u0);
x0.GetBlock(1).Set(1.0, m0);
BlockVector xhat(block_offsetsx); xhat = 0.0;
xhat.GetBlock(0).Set(1.0, uhat);
xhat.GetBlock(1).Set(1.0, mhat);
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
int maxBacktrack = 20;
alpha = alphaMax;
Vector ck0(dimC); ck0 = 0.0;
Vector zhatsoc(dimM); zhatsoc = 0.0;
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
Vector uhatsoc(dimU); uhatsoc = 0.0;
Vector mhatsoc(dimM); mhatsoc = 0.0;
Dxphi(x0, mu, Dxphi0);
Dxphi0_xhat = InnerProduct(MPI_COMM_WORLD, Dxphi0, xhat);
descentDirection = Dxphi0_xhat < 0. ? true : false;
if(descentDirection)
{
if (iAmRoot)
{
cout << "is a descent direction for the log-barrier objective\n";
}
}
else
{
cout << "is not a descent direction for the log-barrier objective\n";
}
thx0 = theta(x0);
phx0 = phi(x0, mu);
lineSearchSuccess = false;
for(int i = 0; i < maxBacktrack; i++)
{
if (iAmRoot)
{
cout << "\n--------- alpha = " << alpha << " ---------\n";
}
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
xtrial.Set(1.0, x0);
xtrial.Add(alpha, xhat);
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
thxtrial = theta(xtrial);
phxtrial = phi(xtrial, mu);
filterCheck(thxtrial, phxtrial);
if(!inFilterRegion)
{
if (iAmRoot)
{
cout << "not in filter region :)\n";
}
// ------ A.5.4: Check sufficient decrease
if(!descentDirection)
{
switchCondition = false;
}
else
{
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
}
if (iAmRoot)
{
cout << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
cout << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
cout << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
}
// Case I
if(thx0 <= thetaMin && switchCondition)
{
sufficientDecrease = (phxtrial <= phx0 + eta * alpha * Dxphi0_xhat) ? true : false;
if(sufficientDecrease)
{
if(iAmRoot) { cout << "Line search successful: sufficient decrease in log-barrier objective.\n"; }
// accept the trial step
lineSearchSuccess = true;
break;
}
}
else
{
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
{
if(iAmRoot) { cout << "Line search successful: infeasibility or log-barrier objective decreased.\n"; }
// accept the trial step
lineSearchSuccess = true;
break;
}
}
// A-5.5: Initialize the second-order correction
if((!(thx0 < thxtrial)) && i == 0)
{
if (iAmRoot)
{
cout << "second order correction\n";
}
problem->c(xtrial, ckSoc);
problem->c(x0, ck0);
ckSoc.Add(alphaMax, ck0);
// A-5.6 Compute the second-order correction.
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
//WARNING: not complete but currently solver isn't entering this region
}
}
else
{
if (iAmRoot)
{
cout << "in filter region :(\n";
}
}
// include more if needed
alpha *= 0.5;
}
}
void ParInteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
{
double zi;
double mudivmml;
for(int i = 0; i < dimM; i++)
{
zi = z(i);
mudivmml = mu / (x(i + dimU) - ml(i));
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
}
}
void ParInteriorPointSolver::filterCheck(double th, double ph)
{
inFilterRegion = false;
if(th > thetaMax)
{
inFilterRegion = true;
}
else
{
for(int i = 0; i < F1.Size(); i++)
{
if(th >= F1[i] && ph >= F2[i])
{
inFilterRegion = true;
break;
}
}
}
}
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool printEeval)
{
double E1, E2, E3;
double sc, sd;
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
DxL(x, l, zl, gradL);
E1 = GlobalLpNorm(infinity(), gradL.Normlinf(), MPI_COMM_WORLD);
problem->c(x, cx);
E2 = GlobalLpNorm(infinity(), cx.Normlinf(), MPI_COMM_WORLD);
for(int ii = 0; ii < dimM; ii++)
{
comp(ii) = x(dimU + ii) * zl(ii) - mu;
}
E3 = GlobalLpNorm(infinity(), comp.Normlinf(), MPI_COMM_WORLD);
double ll1, zl1;
zl1 = GlobalLpNorm(1, zl.Norml1(), MPI_COMM_WORLD)/ double(dimCglb + dimMglb);
ll1 = GlobalLpNorm(1, l.Norml1(), MPI_COMM_WORLD);
sc = max(sMax, zl1 / (double(dimMglb)) ) / sMax;
sd = max(sMax, (ll1 + zl1) / (double(dimCglb + dimMglb))) / sMax;
if(iAmRoot && printEeval)
{
cout << "evaluating optimality error for mu = " << mu << endl;
cout << "stationarity measure = " << E1 / sd << endl;
cout << "feasibility measure = " << E2 << endl;
cout << "complimentarity measure = " << E3 / sc << endl;
}
return max(max(E1 / sd, E2), E3 / sc);
}
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool printEeval)
{
return E(x, l, zl, 0.0, printEeval);
}
double ParInteriorPointSolver::theta(const BlockVector &x)
{
Vector cx(dimC); cx = 0.0;
problem->c(x, cx);
return sqrt(InnerProduct(MPI_COMM_WORLD,cx, cx));
}
// log-barrier objective
double ParInteriorPointSolver::phi(const BlockVector &x, double mu)
{
double fx = problem->CalcObjective(x);
double logBarrierLoc = 0.0;
for(int i = 0; i < dimM; i++)
{
logBarrierLoc += log(x(dimU+i)-ml(i));
}
double logBarrierGlb;
MPI_Allreduce(&logBarrierLoc, &logBarrierGlb, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
return fx - mu * logBarrierGlb;
}
// gradient of log-barrier objective with respect to x = (u, m)
void ParInteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
{
problem->CalcObjectiveGrad(x, y);
for(int i = 0; i < dimM; i++)
{
y(dimU + i) -= mu / (x(dimU + i));
}
}
// Lagrangian function evaluation
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
double ParInteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
{
double fx = problem->CalcObjective(x);
Vector cx(dimC); problem->c(x, cx);
return (fx + InnerProduct(MPI_COMM_WORLD,cx, l) - InnerProduct(MPI_COMM_WORLD, x.GetBlock(1), zl));
}
void ParInteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
{
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
problem->CalcObjectiveGrad(x, gradxf);
HypreParMatrix *Jacu, *Jacm, *JacuT, *JacmT;
Jacu = problem->Duc(x);
Jacm = problem->Dmc(x);
JacuT = Jacu->Transpose();
JacmT = Jacm->Transpose();
JacuT->Mult(l, y.GetBlock(0));
JacmT->Mult(l, y.GetBlock(1));
delete JacuT;
delete JacmT;
y.Add(1.0, gradxf);
(y.GetBlock(1)).Add(-1.0, zl);
}
bool ParInteriorPointSolver::GetConverged() const
{
return converged;
}
void ParInteriorPointSolver::SetTol(double Tol)
{
OptTol = Tol;
}
void ParInteriorPointSolver::SetMaxIter(int max_it)
{
max_iter = max_it;
}
void ParInteriorPointSolver::SetBarrierParameter(double mu_0)
{
mu_k = mu_0;
}
void ParInteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
{
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
saveLogBarrierIterates = save;
}
void ParInteriorPointSolver::SetLinearSolver(int LinSolver)
{
linSolver = LinSolver;
}
void ParInteriorPointSolver::SetLinearSolveTol(double Tol)
{
linSolveTol = Tol;
}
ParInteriorPointSolver::~ParInteriorPointSolver()
{
F1.DeleteAll();
F2.DeleteAll();
block_offsetsx.DeleteAll();
block_offsetsumlz.DeleteAll();
block_offsetsuml.DeleteAll();
ml.SetSize(0);
}
+82
View File
@@ -0,0 +1,82 @@
#include "mfem.hpp"
#include "ParProblems.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifndef PARIPSOLVER
#define PARIPSOLVER
class ParInteriorPointSolver
{
protected:
ParGeneralOptProblem* problem;
double OptTol;
int max_iter;
double mu_k; // \mu_k
Vector lk, zlk;
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
double thetaMax, kSoc, gTheta, gPhi, kEps;
// filter
Array<double> F1, F2;
// quantities computed in lineSearch
double alpha, alphaz;
double thx0, thxtrial;
double phx0, phxtrial;
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
double Dxphi0_xhat;
int dimU, dimM, dimC;
int dimUglb, dimMglb, dimCglb;
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
Vector ml;
Vector ckSoc;
HypreParMatrix * Huu, * Hum, * Hmu, * Hmm, * Wmm, *D, * Ju, * Jm, * JuT, * JmT;
int jOpt;
bool converged;
int MyRank;
bool iAmRoot;
bool saveLogBarrierIterates;
int linSolver;
double linSolveTol;
public:
ParInteriorPointSolver(ParGeneralOptProblem*);
double MaxStepSize(Vector& , Vector& , Vector& , double);
double MaxStepSize(Vector& , Vector& , double);
void Mult(const BlockVector& , BlockVector&);
void Mult(const Vector&, Vector &);
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
void lineSearch(BlockVector& , BlockVector& , double);
void projectZ(const Vector & , Vector &, double);
void filterCheck(double, double);
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
double E(const BlockVector &, const Vector &, const Vector &, bool);
bool GetConverged() const;
// TO DO: include Hessian of Lagrangian
double theta(const BlockVector &);
double phi(const BlockVector &, double);
void Dxphi(const BlockVector &, double, BlockVector &);
double L(const BlockVector &, const Vector &, const Vector &);
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
void SetTol(double);
void SetMaxIter(int);
void SetBarrierParameter(double);
void SaveLogBarrierHessianIterates(bool);
void SetLinearSolver(int);
void SetLinearSolveTol(double);
virtual ~ParInteriorPointSolver();
};
#endif
+129
View File
@@ -0,0 +1,129 @@
// Obstacle Problem
//
//
// Compile with: make ParObstacleProblem
//
// Sample runs: mpirun -np 4 ./ParObstacleProblem
//
//
// Description: This example code demonstrates the use of MFEM to solve the
// bound-constrained energy minimization problem
//
// minimize (||∇u||² + ||u||²) subject to u ≥ ϕ in H¹.
#include "mfem.hpp"
#include "ParProblems.hpp"
#include "ParIPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double dmanufacturedFun(const Vector &);
double fRhs(const Vector &);
int main(int argc, char *argv[])
{
// Initialize MPI
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
int FEorder = 1; // order of the finite elements
int linSolver = 2;
int maxIPMiters = 30;
int ref_levels = 3;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if(Mpi::Root())
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/inline-quad.mesh";
Mesh mesh(meshFile, 1, 1);
int dim = mesh.Dimension(); // geometric dimension of the meshed domain
{
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
ParFiniteElementSpace *Vh = new ParFiniteElementSpace(&pmesh, fec);
ParObstacleProblem problem(Vh,Vh,&fRhs);
int dimD = problem.GetDimD();
Vector x0(dimD); x0 = 100.0;
Vector xf(dimD); xf = 0.0;
ParInteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-8);
optimizer.SetLinearSolveTol(1.e-10);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
ParGridFunction d_gf(Vh);
d_gf.SetFromTrueDofs(xf);
FunctionCoefficient dm_fc(dmanufacturedFun); // manufactured solution
ParGridFunction dm_gf(Vh);
dm_gf.ProjectCoefficient(dm_fc);
char vishost[] = "localhost";
int visport = 19916;
socketstream exact_sock(vishost, visport);
exact_sock.precision(8);
exact_sock << "parallel " << num_procs << " " << myid << "\n";
exact_sock << "solution\n" << pmesh << dm_gf
<< "window_title 'Manufactured solution'" << flush;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock << "solution\n" << pmesh << d_gf
<< "window_title 'Numerical solution'" << flush;
delete Vh;
delete fec;
return 0;
}
double dmanufacturedFun(const Vector &x)
{
return cos(2*M_PI*x(0)) + 0.2 - 2.0*(pow(x(0),3) - 1.5*pow(x(0),2));
}
double fRhs(const Vector &x)
{
double fx = 0.;
fx = 0.2 - 2.0 * (pow(x(0),3)- 1.5*pow(x(0),2.) - 6 * x(0) + 3.) + (1. + pow(2.*M_PI,2))*cos(2.*M_PI*x(0));
return fx;
}
+212
View File
@@ -0,0 +1,212 @@
#include "mfem.hpp"
#include "ParProblems.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
ParGeneralOptProblem::ParGeneralOptProblem(ParFiniteElementSpace * fesU_, ParFiniteElementSpace * fesM_)
: fesU(fesU_), fesM(fesM_)
{
dimU = fesU->GetTrueVSize();
dimM = fesM->GetTrueVSize();
dimC = fesM->GetTrueVSize();
}
void ParGeneralOptProblem::CalcObjectiveGrad(const BlockVector &x, BlockVector &y) const
{
Duf(x, y.GetBlock(0));
Dmf(x, y.GetBlock(1));
}
ParGeneralOptProblem::~ParGeneralOptProblem()
{
block_offsetsx.DeleteAll();
}
// min E(d) s.t. g(d) >= 0
// min_(d,s) E(d) s.t. c(d,s) := g(d) - s = 0, s >= 0
ParOptProblem::ParOptProblem(ParFiniteElementSpace * fesU_,
ParFiniteElementSpace * fesM_)
: ParGeneralOptProblem(fesU_, fesM_), block_offsetsx(3)
{
block_offsetsx[0] = 0;
block_offsetsx[1] = dimU;
block_offsetsx[2] = dimM;
block_offsetsx.PartialSum();
ml.SetSize(dimM); ml = 0.0;
Vector negIdentDiag(dimM);
negIdentDiag = -1.0;
SparseMatrix * diag = new SparseMatrix(negIdentDiag);
Ih = new HypreParMatrix(fesM->GetComm(), fesM->GlobalTrueVSize(),
fesM->GetTrueDofOffsets(), diag);
HypreStealOwnership(*Ih, *diag);
delete diag;
}
double ParOptProblem::CalcObjective(const BlockVector &x) const { return E(x.GetBlock(0)); }
void ParOptProblem::Duf(const BlockVector &x, Vector &y) const { DdE(x.GetBlock(0), y); }
void ParOptProblem::Dmf(const BlockVector &x, Vector &y) const { y = 0.0; }
HypreParMatrix * ParOptProblem::Duuf(const BlockVector &x)
{
return DddE(x.GetBlock(0));
}
HypreParMatrix * ParOptProblem::Dumf(const BlockVector &x) { return nullptr; }
HypreParMatrix * ParOptProblem::Dmuf(const BlockVector &x) { return nullptr; }
HypreParMatrix * ParOptProblem::Dmmf(const BlockVector &x) { return nullptr; }
void ParOptProblem::c(const BlockVector &x, Vector &y) const // c(u,m) = g(u) - m
{
g(x.GetBlock(0), y);
y.Add(-1.0, x.GetBlock(1));
}
HypreParMatrix * ParOptProblem::Duc(const BlockVector &x)
{
return Ddg(x.GetBlock(0));
}
HypreParMatrix * ParOptProblem::Dmc(const BlockVector &x)
{
return Ih;
}
ParOptProblem::~ParOptProblem()
{
delete Ih;
}
// Obstacle Problem, no essential boundary conditions enforced
// Hessian of energy term is K + M (stiffness + mass)
ParObstacleProblem::ParObstacleProblem(ParFiniteElementSpace *fesU_,
ParFiniteElementSpace *fesM_,
double (*fSource)(const Vector &)) :
ParOptProblem(fesU_,fesM_), f(dimU), psi(dimU), J(nullptr)
{
Kform = new ParBilinearForm(fesU);
Kform->AddDomainIntegrator(new MassIntegrator);
Kform->AddDomainIntegrator(new DiffusionIntegrator);
Kform->Assemble();
Kform->Finalize();
Kform->FormSystemMatrix(ess_tdof_list, K);
FunctionCoefficient fcoeff(fSource);
fform = new ParLinearForm(fesU);
fform->AddDomainIntegrator(new DomainLFIntegrator(fcoeff));
fform->Assemble();
Vector F(dimU);
fform->ParallelAssemble(F);
f.SetSize(dimU);
f.Set(1.0, F);
psi = 0.0;
Vector iDiag(dimU); iDiag = 1.0;
SparseMatrix * Jacg = new SparseMatrix(iDiag);
J = new HypreParMatrix(fesU->GetComm(),fesU->GlobalTrueVSize(),fesU->GetTrueDofOffsets(),Jacg);
HypreStealOwnership(*J, *Jacg);
delete Jacg;
}
// Obstacle Problem, essential boundary conditions enforced
// Hessian of energy term is K (stiffness)
ParObstacleProblem::ParObstacleProblem(ParFiniteElementSpace *fesU_,
ParFiniteElementSpace *fesM_,
double (*fSource)(const Vector &),
double (*obstacleSource)(const Vector &),
Array<int> tdof_list, Vector &xDC) : ParOptProblem(fesU_,fesM_), f(dimU), psi(dimU), J(nullptr)
{
// elastic energy functional terms
ess_tdof_list = tdof_list;
Kform = new ParBilinearForm(fesU);
Kform->AddDomainIntegrator(new DiffusionIntegrator);
Kform->Assemble();
Kform->Finalize();
Kform->FormSystemMatrix(ess_tdof_list, K);
FunctionCoefficient fcoeff(fSource);
fform = new ParLinearForm(fesU);
fform->AddDomainIntegrator(new DomainLFIntegrator(fcoeff));
fform->Assemble();
Vector F(dimU);
fform->ParallelAssemble(F);
f.SetSize(dimU);
f.Set(1.0, F);
Kform->EliminateVDofsInRHS(ess_tdof_list, xDC, f);
// obstacle constraints --
Vector iDiag(dimU); iDiag = 1.0;
for(int i = 0; i < ess_tdof_list.Size(); i++)
{
iDiag(ess_tdof_list[i]) = 0.0;
}
SparseMatrix * Jacg = new SparseMatrix(iDiag);
J = new HypreParMatrix(fesU->GetComm(),fesU->GlobalTrueVSize(),fesU->GetTrueDofOffsets(),Jacg);
HypreStealOwnership(*J, *Jacg);
delete Jacg;
FunctionCoefficient psi_fc(obstacleSource);
ParGridFunction psi_gf(fesU);
psi_gf.ProjectCoefficient(psi_fc);
psi.Set(1.0, (*psi_gf.GetTrueDofs()));
for(int i = 0; i < ess_tdof_list.Size(); i++)
{
psi(ess_tdof_list[i]) -= 1.e-8;
}
}
double ParObstacleProblem::E(const Vector &d) const
{
Vector Kd(K.Height()); Kd = 0.0;
MFEM_VERIFY(d.Size() == K.Width(), "ParObstacleProblem::E - Inconsistent dimensions");
K.Mult(d, Kd);
return 0.5 * InnerProduct(MPI_COMM_WORLD, d, Kd) - InnerProduct(MPI_COMM_WORLD, f, d);
}
void ParObstacleProblem::DdE(const Vector &d, Vector &gradE) const
{
gradE.SetSize(K.Height());
MFEM_VERIFY(d.Size() == K.Width(), "ParObstacleProblem::DdE - Inconsistent dimensions");
K.Mult(d, gradE);
MFEM_VERIFY(f.Size() == K.Height(), "ParObstacleProblem::DdE - Inconsistent dimensions");
gradE.Add(-1.0, f);
}
HypreParMatrix * ParObstacleProblem::DddE(const Vector &d)
{
return &K;
}
// g(d) = d >= \psi
void ParObstacleProblem::g(const Vector &d, Vector &gd) const
{
MFEM_VERIFY(d.Size() == J->Width(), "ParObstacleProblem::g - Inconsistent dimensions");
J->Mult(d, gd);
MFEM_VERIFY(gd.Size() == J->Height(), "ParObstacleProblem::g - Inconsistent dimensions");
gd.Add(-1.0, psi);
}
HypreParMatrix * ParObstacleProblem::Ddg(const Vector &d)
{
return J;
}
ParObstacleProblem::~ParObstacleProblem()
{
delete Kform;
delete fform;
delete J;
}
+107
View File
@@ -0,0 +1,107 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifndef PARPROBLEM_DEFS
#define PARPROBLEM_DEFS
// abstract ParGeneralOptProblem class
// of the form
// min_(u,m) f(u,m) s.t. c(u,m)=0 and m>=ml
// the primal variable (u, m) is represented as a BlockVector
// think about supporting general lower and upper bounds (see HiOP user manual)
class ParGeneralOptProblem
{
protected:
int dimU, dimM, dimC;
ParFiniteElementSpace * fesU = nullptr;
ParFiniteElementSpace * fesM = nullptr;
Array<int> block_offsetsx;
Vector ml;
public:
ParGeneralOptProblem(ParFiniteElementSpace * fesU_, ParFiniteElementSpace * fesM_); // constructor
virtual double CalcObjective(const BlockVector &) const = 0;
virtual void Duf(const BlockVector &, Vector &) const = 0;
virtual void Dmf(const BlockVector &, Vector &) const = 0;
void CalcObjectiveGrad(const BlockVector &, BlockVector &) const;
virtual HypreParMatrix * Duuf(const BlockVector &) = 0;
virtual HypreParMatrix * Dumf(const BlockVector &) = 0;
virtual HypreParMatrix * Dmuf(const BlockVector &) = 0;
virtual HypreParMatrix * Dmmf(const BlockVector &) = 0;
virtual HypreParMatrix * Duc(const BlockVector &) = 0;
virtual HypreParMatrix * Dmc(const BlockVector &) = 0;
// TO DO: include Hessian terms of constraint c
virtual void c(const BlockVector &, Vector &) const = 0;
int GetDimU() const { return dimU; };
int GetDimM() const { return dimM; };
int GetDimC() const { return dimC; };
ParFiniteElementSpace * GetfesU() {return fesU;}
ParFiniteElementSpace * GetfesM() {return fesM;}
Vector Getml() const { return ml; };
~ParGeneralOptProblem(); // destructor
};
// abstract ContactProblem class
// of the form
// min_d e(d) s.t. g(d) >= 0
class ParOptProblem : public ParGeneralOptProblem
{
protected:
Array<int> block_offsetsx;
HypreParMatrix * Ih;
public:
ParOptProblem(ParFiniteElementSpace * fesU_, ParFiniteElementSpace * fesM_); // constructor
double CalcObjective(const BlockVector &) const; // objective e
void Duf(const BlockVector &, Vector &) const;
void Dmf(const BlockVector &, Vector &) const;
HypreParMatrix * Duuf(const BlockVector &);
HypreParMatrix * Dumf(const BlockVector &);
HypreParMatrix * Dmuf(const BlockVector &);
HypreParMatrix * Dmmf(const BlockVector &);
HypreParMatrix * Duc(const BlockVector &);
HypreParMatrix * Dmc(const BlockVector &);
void c(const BlockVector &, Vector &) const;
virtual double E(const Vector &) const = 0; // objective e(d) (energy function)
virtual void DdE(const Vector &, Vector &) const = 0; // gradient of objective De / Dd
virtual HypreParMatrix * DddE(const Vector &) = 0;
// Hessian of objective D^2 e / D d^2
virtual HypreParMatrix * Ddg(const Vector &) = 0;
// Jacobian of inequality constraint Dg / Dd
virtual void g(const Vector &, Vector &) const = 0; // inequality constraint g(d) >= 0 (gap function)
int GetDimD() const { return fesU->GetTrueVSize(); };
int GetDimS() const { return fesM->GetTrueVSize(); };
virtual ~ParOptProblem();
};
class ParObstacleProblem : public ParOptProblem
{
protected:
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d >= \psi
// stiffness matrix used to define objective
ParBilinearForm *Kform;
ParLinearForm *fform;
Array<int> ess_tdof_list; // needed for calls to FormSystemMatrix
HypreParMatrix K;
HypreParMatrix *J;
ParFiniteElementSpace *Vh;
Vector f;
Vector psi;
public :
ParObstacleProblem(ParFiniteElementSpace*, ParFiniteElementSpace*, double (*fSource)(const Vector &));
ParObstacleProblem(ParFiniteElementSpace*, ParFiniteElementSpace*, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &), Array<int> tdof_list, Vector &);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
HypreParMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
HypreParMatrix* Ddg(const Vector &);
virtual ~ParObstacleProblem();
};
#endif
@@ -0,0 +1,173 @@
// Spherical Obstacle Problem
//
//
// Compile with: make ParSphericalObstacleProblem
//
// Sample runs: mpirun -np 4 ./ParSphericalObstacleProblem -linSolver 0
// mpirun -np 4 ./ParSphericalObstacleProblem -linSolver 1
// mpirun -np 4 ./ParSphericalObstacleProblem -linSolver 2
//
//
// Description: This example code demonstrates the use of MFEM to solve the
// bound-constrained energy minimization problem
//
// minimize ||∇u||² subject to u ≥ ϕ in H¹₀.
#include "mfem.hpp"
#include "ParProblems.hpp"
#include "ParIPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double fRhs(const Vector &);
double spherical_obstacle(const Vector &);
double exact_solution_obstacle(const Vector &);
int main(int argc, char *argv[])
{
// Initialize MPI
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
int FEorder = 1; // order of the finite elements
int linSolver = 2;
int maxIPMiters = 30;
int ref_levels = 3;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if(myid == 0)
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/disk.mesh";
Mesh mesh(meshFile, 1, 1);
int dim = mesh.Dimension(); // geometric dimension of the meshed domain
{
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
ParFiniteElementSpace *Vh = new ParFiniteElementSpace(&pmesh, fec);
Array<int> boundary_dofs;
Vh->GetBoundaryTrueDofs(boundary_dofs);
int dimD = Vh->GetTrueVSize();
Vector xDC(dimD); xDC = 0.0;
ParObstacleProblem problem(Vh, Vh, &fRhs, &spherical_obstacle, boundary_dofs, xDC);
Vector x0(dimD); x0.Set(1.0, xDC);
Vector xf(dimD); xf = 0.0;
ParInteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolveTol(1.e-10);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
ParGridFunction d_gf(Vh);
d_gf.SetFromTrueDofs(xf);
FunctionCoefficient dtrue_fc(exact_solution_obstacle); // analytic solution
ParGridFunction dtrue_gf(Vh);
dtrue_gf.ProjectCoefficient(dtrue_fc);
double L2error = d_gf.ComputeL2Error(dtrue_fc);
if (myid == 0)
{
cout << "\n|| u_h - u ||_{L^2} = " << L2error << '\n' << endl;
}
ParaViewDataCollection paraview_dc("SphericalObstacleProblem", &pmesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("u(x,y) (analytic)", &dtrue_gf);
paraview_dc.RegisterField("u(x,y) (numerical)", &d_gf);
paraview_dc.Save();
delete Vh;
delete fec;
return 0;
}
double fRhs(const Vector &x)
{
return 0.;
}
double spherical_obstacle(const Vector &pt)
{
double x = pt(0), y = pt(1);
double r = sqrt(x*x + y*y);
double r0 = 0.5;
double beta = 0.9;
double b = r0*beta;
double tmp = sqrt(r0*r0 - b*b);
double B = tmp + b*b/tmp;
double C = -b/tmp;
if (r > b)
{
return B + r * C;
}
else
{
return sqrt(r0*r0 - r*r);
}
}
double exact_solution_obstacle(const Vector &pt)
{
double x = pt(0), y = pt(1);
double r = sqrt(x*x + y*y);
double r0 = 0.5;
double a = 0.348982574111686;
double A = -0.340129705945858;
if (r > a)
{
return A * log(r);
}
else
{
return sqrt(r0*r0-r*r);
}
}
File diff suppressed because it is too large Load Diff
+300
View File
@@ -0,0 +1,300 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include <set>
using namespace std;
using namespace mfem;
#ifndef PROBLEM_DEFS
#define PROBLEM_DEFS
// abstract GeneralOptProblem class
// for the problem
// min_(u,m) f(u,m)
// such that c(u,m)=0 and m >= ml
class GeneralOptProblem
{
protected:
int dimU, dimM, dimC;
Array<int> block_offsetsx;
Vector ml;
public:
GeneralOptProblem();
virtual double CalcObjective(const BlockVector &) const = 0;
virtual void Duf(const BlockVector &, Vector &) const = 0;
virtual void Dmf(const BlockVector &, Vector &) const = 0;
void CalcObjectiveGrad(const BlockVector &, BlockVector &) const;
virtual SparseMatrix* Duuf(const BlockVector &) = 0;
virtual SparseMatrix* Dumf(const BlockVector &) = 0;
virtual SparseMatrix* Dmuf(const BlockVector &) = 0;
virtual SparseMatrix* Dmmf(const BlockVector &) = 0;
virtual void c(const BlockVector &, Vector &) const = 0;
virtual SparseMatrix* Duc(const BlockVector &) = 0;
virtual SparseMatrix* Dmc(const BlockVector &) = 0;
virtual SparseMatrix* lDuuc(const BlockVector &, const Vector &) = 0;
virtual SparseMatrix* lDumc(const BlockVector &, const Vector &) = 0;
virtual SparseMatrix* lDmuc(const BlockVector &, const Vector &) = 0;
virtual SparseMatrix* lDmmc(const BlockVector &, const Vector &) = 0;
// TO DO: include log-barrier lumped-mass and pass that
// to the optimizer
//virtual SparseMatrix* GetLogBarrierLumpedMass() = 0;
int GetDimU() const { return dimU; };
int GetDimM() const { return dimM; };
int GetDimC() const { return dimC; };
Vector Getml() const { return ml; };
~GeneralOptProblem();
};
// abstract OptProblem class
// of the form
// min_d e(d) s.t. g(d) >= 0
class OptProblem : public GeneralOptProblem
{
protected:
int dimD;
int dimS;
Array<int> block_offsetsx;
SparseMatrix * negIdentity;
SparseMatrix * zeroMatum;
SparseMatrix * zeroMatmu;
SparseMatrix * zeroMatmm;
public:
//OptProblem(int, int); // constructor
OptProblem();
void InitializeParentData(int, int);
double CalcObjective(const BlockVector &) const; // objective e
void Duf(const BlockVector &, Vector &) const;
void Dmf(const BlockVector &, Vector &) const;
SparseMatrix* Duuf(const BlockVector &);
SparseMatrix* Dumf(const BlockVector &);
SparseMatrix* Dmuf(const BlockVector &);
SparseMatrix* Dmmf(const BlockVector &);
void c(const BlockVector &, Vector &) const;
SparseMatrix* Duc(const BlockVector &);
SparseMatrix* Dmc(const BlockVector &);
SparseMatrix* lDuuc(const BlockVector &, const Vector &);
SparseMatrix* lDumc(const BlockVector &, const Vector &);
SparseMatrix* lDmuc(const BlockVector &, const Vector &);
SparseMatrix* lDmmc(const BlockVector &, const Vector &);
virtual double E(const Vector &) const = 0; // objective e(d) (energy function)
virtual void DdE(const Vector &, Vector &) const = 0; // gradient of objective De / Dd
virtual SparseMatrix* DddE(const Vector &) = 0; // Hessian of objective D^2 e / D d^2
virtual void g(const Vector &, Vector &) const = 0; // inequality constraint g(d) >= 0 (gap function)
virtual SparseMatrix* Ddg(const Vector &) = 0; // Jacobian of inequality constraint Dg / Dd
virtual SparseMatrix* lDddg(const Vector &, const Vector &) = 0;
int GetDimD() const { return dimD; };
int GetDimS() const { return dimS; };
virtual ~OptProblem();
};
class ObstacleProblem : public OptProblem
{
protected:
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d + \psi >= 0
// stiffness matrix used to define objective
BilinearForm *Kform;
LinearForm *fform;
Array<int> ess_tdof_list;
SparseMatrix *K;
SparseMatrix *J;
SparseMatrix *Hcl;
FiniteElementSpace *Vh;
Vector f;
Vector psil;
Vector psiu;
bool twoBounds;
Vector xDC;
double Ce;
public :
ObstacleProblem(FiniteElementSpace*, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &));
ObstacleProblem(FiniteElementSpace*, Vector&, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &), Array<int> tdof_list);
ObstacleProblem(FiniteElementSpace*, Vector &, double (*fSource)(const Vector &), double (*obstacleSourcel)(const Vector &), double (*obstacleSourceu)(const Vector &), Array<int> tdof_list);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
SparseMatrix * lDddg(const Vector &, const Vector &);
virtual ~ObstacleProblem();
};
SparseMatrix * GenerateProjector(int n, Array<int> ess_tdof_list);
class ObstacleProblemVariant : public OptProblem
{
protected:
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d + \psi >= 0
// stiffness matrix used to define objective
BilinearForm *Kform;
LinearForm *fform;
Array<int> ess_tdof_list;
Array<int> noness_tdof_list;
SparseMatrix *K;
SparseMatrix *RKP; // R K P = R K R^T
SparseMatrix *J;
SparseMatrix *Hcl;
SparseMatrix *R;
FiniteElementSpace *Vh;
Vector f;
Vector psil;
Vector xDC;
double Ce;
public :
ObstacleProblemVariant(FiniteElementSpace*, Vector&, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &), Array<int> tdof_list);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
SparseMatrix * lDddg(const Vector &, const Vector &);
virtual ~ObstacleProblemVariant();
};
class QPOptProblem : public OptProblem
{
protected:
SparseMatrix *K;
SparseMatrix *J;
SparseMatrix *zeroMatdd;
Vector f;
Vector g0;
public:
QPOptProblem(const SparseMatrix, const SparseMatrix, const Vector, const Vector);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
SparseMatrix * lDddg(const Vector &, const Vector &);
virtual ~QPOptProblem();
};
class ExContactBlockTL : public OptProblem
{
public:
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
SparseMatrix * lDddg(const Vector &, const Vector &);
FiniteElementSpace GetVh1();
FiniteElementSpace GetVh2();
SparseMatrix *zeroMatdd;
public:
/** default constructor */
ExContactBlockTL(Mesh *, Mesh *, int);
/** default destructor */
virtual ~ExContactBlockTL();
private:
void update_g() const;
private:
/**@name Methods to block default compiler methods.
*
* The compiler automatically generates the following three methods.
* Since the default compiler implementation is generally not what
* you want (for all but the most simple classes), we usually
* put the declarations of these methods in the private section
* and never implement them. This prevents the compiler from
* implementing an incorrect "default" behavior without us
* knowing. (See Scott Meyers book, "Effective C++")
*/
ExContactBlockTL(
const ExContactBlockTL&
);
ExContactBlockTL& operator=(
const ExContactBlockTL&
);
Array<int> attr;
Array<int> m_attr;
Array<int> s_conn; // connectivity of the second/slave mesh
std::string mesh_file1;
std::string mesh_file2;
Mesh* mesh1;
Mesh* mesh2;
FiniteElementCollection* fec1;
FiniteElementCollection* fec2;
FiniteElementSpace* fespace1;
FiniteElementSpace* fespace2;
Array<int> ess_tdof_list1;
Array<int> ess_tdof_list2;
GridFunction nodes0;
GridFunction* nodes1;
GridFunction* nodes2;
mutable GridFunction* x1;
mutable GridFunction* x2;
PWConstCoefficient* lambda1_func;
PWConstCoefficient* lambda2_func;
PWConstCoefficient* mu1_func;
PWConstCoefficient* mu2_func;
BilinearForm* a1;
BilinearForm* a2;
mfem::Vector lambda1;
mfem::Vector lambda2;
mfem::Vector mu1;
mfem::Vector mu2;
mutable mfem::Vector xyz;
std::set<int> bdryVerts2;
int dim;
// degrees of freedom of both meshes
int ndof_1;
int ndof_2;
int ndofs;
// number of nodes for each mesh
int nnd_1;
int nnd_2;
int nnd;
int npoints;
SparseMatrix A1;
mfem::Vector B1, X1;
SparseMatrix A2;
mfem::Vector B2, X2;
BlockVector *B;
SparseMatrix* K;
mutable mfem::Vector gapv;
mutable mfem::Vector m_xi;
mutable mfem::Vector xs;
mutable Array<int> m_conn; // only works for linear elements that have 4 vertices!
mutable DenseMatrix* coordsm;
mutable SparseMatrix* M;
mutable std::vector<SparseMatrix>* dM;
Array<int> Dirichlet_dof;
Array<double> Dirichlet_val;
Array<int> block_offsets;
public:
Mesh * GetMesh1() {return mesh1;}
Mesh * GetMesh2() {return mesh2;}
GridFunction & GetMesh1GridFunction() {return *x1;}
GridFunction & GetMesh2GridFunction() {return *x2;}
Array<int> & GetMesh1DirichletDofs() {return ess_tdof_list1;}
Array<int> & GetMesh2DirichletDofs() {return ess_tdof_list2;}
};
#endif
@@ -0,0 +1,175 @@
// Spherical Obstacle Problem
//
//
// Compile with: make SphericalobstacleProblem
//
// Sample runs: ./SphericalobstacleProblem
//
//
// Description: This example code demonstrates the use of MFEM to solve the
// bound-constrained energy minimization problem
//
// minimize ||∇u||² subject to u ≥ ϕ in H¹₀.
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double fRhs(const Vector &);
double spherical_obstacle(const Vector &);
double exact_solution_obstacle(const Vector &);
int main(int argc, char *argv[])
{
int FEorder = 1; // finite element order
int linSolver = 0; // linear solver 0 (direct), 1 (iterative) or 2 (iterative)
int maxIPMiters = 30;
bool iAmRoot = true;
int ref_levels = 3;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/disk.mesh";
Mesh *mesh = new Mesh(meshFile, 1, 1);
int dim = mesh->Dimension(); // geometric dimension of the domain
{
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
double h_min, h_max, kappa_min, kappa_max;
mesh->GetCharacteristics(h_min, h_max, kappa_min, kappa_max);
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
FiniteElementSpace *Vh = new FiniteElementSpace(mesh, fec);
Array<int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
Vh->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
int dimD = Vh->GetTrueVSize();
Vector x0(dimD); x0 = 0.0;
Vector xf(dimD); xf = 0.0;
ObstacleProblem problem(Vh, x0, &fRhs, &spherical_obstacle, ess_tdof_list);
InteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
double Einitial = problem.E(x0);
double Efinal = problem.E(xf);
cout << "Energy objective at initial point = " << Einitial << endl;
cout << "Energy objective at optimizer = " << Efinal << endl;
GridFunction d_gf(Vh);
d_gf = xf;
FunctionCoefficient dtrue_fc(exact_solution_obstacle); // exact solution
GridFunction dtrue_gf(Vh);
dtrue_gf.ProjectCoefficient(dtrue_fc);
ParaViewDataCollection paraview_dc("BarrierProblemSolution", mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("d(x) (numerical)", &d_gf);
paraview_dc.RegisterField("d(x) (true)", &dtrue_gf);
paraview_dc.Save();
FunctionCoefficient exact_coef(exact_solution_obstacle);
double L2_error = d_gf.ComputeL2Error(exact_coef);
cout << "||u - u_true||_L^2(Omega) = " << L2_error << ", hmax = " << h_max << ", hmin = " << h_min << endl;
delete Vh;
delete fec;
delete mesh;
return 0;
}
double fRhs(const Vector &x)
{
return 0.;
}
double spherical_obstacle(const Vector &pt)
{
double x = pt(0), y = pt(1);
double r = sqrt(x*x + y*y);
double r0 = 0.5;
double beta = 0.9;
double b = r0*beta;
double tmp = sqrt(r0*r0 - b*b);
double B = tmp + b*b/tmp;
double C = -b/tmp;
if (r > b)
{
return B + r * C;
}
else
{
return sqrt(r0*r0 - r*r);
}
}
double exact_solution_obstacle(const Vector &pt)
{
double x = pt(0), y = pt(1);
double r = sqrt(x*x + y*y);
double r0 = 0.5;
double a = 0.348982574111686;
double A = -0.340129705945858;
if (r > a)
{
return A * log(r);
}
else
{
return sqrt(r0*r0-r*r);
}
}
@@ -0,0 +1,143 @@
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double fRhs(const Vector &pt);
double obstaclel(const Vector &pt);
double obstacleu(const Vector &pt);
double dmanufacturedFun(const Vector &pt);
int main(int argc, char *argv[])
{
int FEorder = 1; // order of the finite elements
int linSolver = 0;
int maxIPMiters = 30;
bool iAmRoot = true;
int ref_levels = 1;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/inline-quad.mesh";
Mesh *mesh = new Mesh(meshFile, 1, 1);
int dim = mesh->Dimension(); // geometric dimension of the domain
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
FiniteElementSpace *Vh = new FiniteElementSpace(mesh, fec);
Array<int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
Vh->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
double DC_val = 0.0;
int dimD = Vh->GetTrueVSize();
Vector x0(dimD); x0 = DC_val;
Vector xf(dimD); xf = 0.0;
ObstacleProblem problem(Vh, x0, &fRhs, &obstaclel, &obstacleu, ess_tdof_list);
InteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
GridFunction d_gf(Vh);
d_gf = xf;
FunctionCoefficient dm_fc(dmanufacturedFun); // pseudo-manufactured solution
GridFunction dm_gf(Vh);
dm_gf.ProjectCoefficient(dm_fc);
ParaViewDataCollection paraview_dc("BarrierProblemSolution", mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("d(x) (numerical)", &d_gf);
paraview_dc.RegisterField("d(x) (pseudo-manufactured)", &dm_gf);
paraview_dc.Save();
delete Vh;
delete fec;
delete mesh;
return 0;
}
double dmanufacturedFun(const Vector &pt)
{
double alpha = 16.5;
return sin(M_PI * pt(1)) * (sin(M_PI * pt(0)) - alpha * pow(pt(0) * (1. - pt(0)), 2));
}
// f(x) forcing term... which enters the objective energy functional
// E(d) = 0.5 d^T K d - f^T d, where f is a discrete vector representation
// of f(x). f(x) is such that in the absence of bound-constraints then
// the solution of the optimization problem satisfies the PDE
// -div(grad(d)) + d = f + homogeneous Neumann conditions on the unit interval,
// for d(x) = cos(2 \pi x) + a0 + a3 (x^3 - 1.5 x^2), a2 = 0.2, a3 = -2
double fRhs(const Vector &pt)
{
double alpha = 16.5;
double fx;
fx = pow(M_PI, 2) * sin(M_PI * pt(0));
fx += alpha * (2. * pow(pt(0), 2) + 2. * pow(1.-pt(0), 2) - 8. * pt(0) * (1.-pt(0)));
fx += pow(M_PI, 2) * sin(M_PI * pt(0)) * dmanufacturedFun(pt);
fx *= sin(M_PI * pt(1));
return fx;
}
double obstaclel(const Vector &pt)
{
return 0.0;
}
double obstacleu(const Vector &pt)
{
return 0.08;
}
+103
View File
@@ -0,0 +1,103 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
3
elements
9
1 5 0 1 3 2 8 9 11 10
1 5 2 3 5 4 10 11 13 12
1 5 4 5 7 6 12 13 15 14
1 5 8 9 11 10 16 17 19 18
1 5 10 11 13 12 18 19 21 20
1 5 12 13 15 14 20 21 23 22
1 5 16 17 19 18 24 25 27 26
1 5 18 19 21 20 26 27 29 28
1 5 20 21 23 22 28 29 31 30
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
30
1 3 1 0 2 3
1 3 3 2 4 5
1 3 5 4 6 7
1 3 24 25 27 26
1 3 26 27 29 28
1 3 28 29 31 30
2 3 2 0 8 10
2 3 4 2 10 12
2 3 6 4 12 14
2 3 10 8 16 18
2 3 12 10 18 20
2 3 14 12 20 22
2 3 18 16 24 26
2 3 20 18 26 28
2 3 22 20 28 30
3 3 1 3 11 9
3 3 3 5 13 11
3 3 5 7 15 13
3 3 9 11 19 17
3 3 11 13 21 19
3 3 13 15 23 21
3 3 17 19 27 25
3 3 19 21 29 27
3 3 21 23 31 29
1 3 8 0 1 9
1 3 16 8 9 17
1 3 24 16 17 25
1 3 6 14 15 7
1 3 14 22 23 15
1 3 22 30 31 23
vertices
32
3
-1.0000 0 0
0 0 0
-1.0000 0.3000 0
0 0.3000 0
-1.0000 0.6500 0
0 0.6500 0
-1.0000 1.0000 0
0 1.0000 0
-1.0000 0 0.3000
0 0 0.3000
-1.0000 0.3000 0.3500
0 0.3000 0.3500
-1.0000 0.6500 0.3000
0 0.6500 0.3000
-1.0000 1.0000 0.3000
0 1.0000 0.3000
-1.0000 0 0.6500
0 0 0.6500
-1.0000 0.3000 0.6500
0 0.3000 0.6500
-1.0000 0.6500 0.6500
0 0.6500 0.6500
-1.0000 1.0000 0.6500
0 1.0000 0.6500
-1.0000 0 1.0000
0 0 1.0000
-1.0000 0.3000 1.0000
0 0.3000 1.0000
-1.0000 0.6500 1.0000
0 0.6500 1.0000
-1.0000 1.0000 1.0000
0 1.0000 1.0000
+246
View File
@@ -0,0 +1,246 @@
// Quadratic-Programming (QP) Contact example
//
// Compile with: make exQPContactBlockTL
//
// Sample runs: ./exQPContactBlockTL
#include <fstream>
#include <iostream>
#include <array>
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
int linSolver = 0;
int maxIPMiters = 30;
bool iAmRoot = true;
int ref_levels = 0;
OptionsParser args(argc, argv);
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
Mesh * mesh1 = new Mesh("block1.mesh", 1, 1);
Mesh * mesh2 = new Mesh("rotatedblock2.mesh", 1, 1);
for(int i = 0; i < ref_levels; i++)
{
mesh1->UniformRefinement();
mesh2->UniformRefinement();
}
// Create an instance of the nlp
ExContactBlockTL * contact = new ExContactBlockTL(mesh1, mesh2, 1);
int ndofs = contact->GetDimD();
int nconstraints = contact->GetDimS();
// set up a QP-problem
// E(d) = 1 / 2 d^T K d + f^T d
// g(d) = J d + g0
// where K, J, f and g0 are evaluated at d0 (a valid configuration)
// to do: seems more appropriate to evaluate at a valid configuration...
// that is one where the Dirichlet conditions hold... need to pull
// this data from contactBlockTL...
Vector d0(ndofs); d0 = 0.0;
Array<int> ess_tdofs1 = contact->GetMesh1DirichletDofs();
Array<int> ess_tdofs2 = contact->GetMesh2DirichletDofs();
int sz1 = ess_tdofs1.Size();
int sz2 = ess_tdofs2.Size();
Array<int> DirichletDofs(sz1+sz2);
for (int i = 0; i<sz1; i++)
{
DirichletDofs[i] = ess_tdofs1[i];
}
for (int i = 0; i<sz2; i++)
{
DirichletDofs[i+sz1] = ess_tdofs2[i]+contact->GetVh1().GetTrueVSize();
}
GridFunction x1 = contact->GetMesh1GridFunction();
GridFunction x2 = contact->GetMesh2GridFunction();
SparseMatrix *K;
Vector f(ndofs); f = 0.0;
contact->DdE(d0, f); K = contact->DddE(d0);
d0.SetVector(x1,0);
d0.SetVector(x2,x1.Size());
SparseMatrix *J;
Vector g0(nconstraints); g0 = 0.0;
contact->g(d0, g0); J = contact->Ddg(d0);
Vector temp(nconstraints);
J->Mult(d0, temp);
g0.Add(-1.0, temp);
// check which rows of the Jacobian are zero!
Vector ei(nconstraints); ei = 0.0;
Vector JTei(ndofs); JTei = 0.0;
double normJTei;
Array<int> nonZeroRows;
for(int i = 0; i < nconstraints; i++)
{
Array<int> col_tmp;
Vector v_tmp; v_tmp = 0.0;
J->GetRow(i, col_tmp, v_tmp);
normJTei = v_tmp.Norml2();
if (normJTei > 1.e-12)
{
nonZeroRows.Append(i);
}
}
mfem::out << J->Height() << " linearized constraints\n";
mfem::out << nonZeroRows.Size() << " (reduced) linearized constraints\n";
// remove zero rows of the gap function Jacobian and corresponding gap function entries
SparseMatrix * Jreduced = new SparseMatrix(nonZeroRows.Size(), ndofs);
Vector g0reduced(nonZeroRows.Size()); g0reduced = 0.0;
for(int i = 0; i < nonZeroRows.Size(); i++)
{
Array<int> col_tmp;
Vector v_tmp; v_tmp = 0.0;
J->GetRow(nonZeroRows[i], col_tmp, v_tmp);
/* obtain subset of columns of the given nonZero Jacobian row that are not Dirichlet constrained */
bool freeDof;
Array<int> free_col_indicies;
for(int j = 0; j < col_tmp.Size(); j++)
{
freeDof = true;
for(int k = 0; k < DirichletDofs.Size(); k++)
{
if(col_tmp[j] == DirichletDofs[k])
{
freeDof = false;
}
}
if(freeDof)
{
free_col_indicies.Append(j);
}
}
Array<int> col_tmp_reduced(free_col_indicies.Size());
Vector v_tmp_reduced(free_col_indicies.Size());
for(int j = 0; j < free_col_indicies.Size(); j++)
{
col_tmp_reduced[j] = col_tmp[free_col_indicies[j]];
v_tmp_reduced(j) = v_tmp(free_col_indicies[j]);
}
Jreduced->SetRow(i, col_tmp_reduced, v_tmp_reduced);
g0reduced(i) = g0(nonZeroRows[i]);
}
QPOptProblem *QPContact = new QPOptProblem(*K, *Jreduced, f, g0reduced);
InteriorPointSolver * QPContactOptimizer = new InteriorPointSolver(QPContact);
QPContactOptimizer->SetTol(1.e-6);
QPContactOptimizer->SetLinearSolver(linSolver);
Vector x0(ndofs); x0 = 0.0;
x0.SetVector(x1,0);
x0.SetVector(x2,x1.Size());
Vector xf(ndofs); xf = 0.0;
QPContactOptimizer->Mult(x0, xf);
MFEM_VERIFY(QPContactOptimizer->GetConverged(), "Interior point solver did not converge.");
double Einitial = QPContact->E(x0);
double Efinal = QPContact->E(xf);
cout << "Energy objective at initial point = " << Einitial << endl;
cout << "Energy objective at QP optimizer = " << Efinal << endl;
int gdim = mesh1->Dimension();
FiniteElementCollection * fec = new H1_FECollection(1, gdim);
FiniteElementSpace * fespace1 = new FiniteElementSpace(mesh1, fec, gdim, Ordering::byVDIM);
FiniteElementSpace * fespace2 = new FiniteElementSpace(mesh2, fec, gdim, Ordering::byVDIM);
GridFunction x1_gf(fespace1);
GridFunction x2_gf(fespace2);
int ndof1 = fespace1->GetTrueVSize();
int ndof2 = fespace2->GetTrueVSize();
int ndof = ndof1 + ndof2;
for(int i = 0; i < ndof1; i++)
{
x1_gf(i) = xf(i);
}
for(int i = ndof1; i < ndof; i++)
{
x2_gf(i - ndof1) = xf(i);
}
mesh1->SetNodalFESpace(fespace1);
mesh2->SetNodalFESpace(fespace2);
GridFunction *nodes1 = mesh1->GetNodes();
GridFunction *nodes2 = mesh2->GetNodes();
{
*nodes1 += x1_gf;
*nodes2 += x2_gf;
}
ParaViewDataCollection paraview_dc1("QPContactBody1", mesh1);
paraview_dc1.SetPrefixPath("ParaView");
paraview_dc1.SetLevelsOfDetail(1);
paraview_dc1.SetDataFormat(VTKFormat::BINARY);
paraview_dc1.SetHighOrderOutput(true);
paraview_dc1.SetCycle(0);
paraview_dc1.SetTime(0.0);
paraview_dc1.RegisterField("Body1", &x1_gf);
paraview_dc1.Save();
ParaViewDataCollection paraview_dc2("QPContactBody2", mesh2);
paraview_dc2.SetPrefixPath("ParaView");
paraview_dc2.SetLevelsOfDetail(1);
paraview_dc2.SetDataFormat(VTKFormat::BINARY);
paraview_dc2.SetHighOrderOutput(true);
paraview_dc2.SetCycle(0);
paraview_dc2.SetTime(0.0);
paraview_dc2.RegisterField("Body2", &x2_gf);
paraview_dc2.Save();
delete fespace1;
delete fespace2;
delete fec;
delete mesh1;
delete mesh2;
delete QPContact;
delete QPContactOptimizer;
delete Jreduced;
delete contact;
return 0;
}
+125
View File
@@ -0,0 +1,125 @@
# Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../../
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/contact/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ObstacleProblem SphericalObstacleProblem DirichletObstacleProblem exQPContactBlockTL
PAR_EXAMPLES = ParObstacleProblem
EXAMPLES = $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
ifeq ($(MFEM_USE_SUITESPARSE),NO)
$(SEQ_EXAMPLES):
$(error MFEM is not configured with SUITESPARSE)
endif
ifeq ($(MFEM_USE_MUMPS),NO)
ifeq ($(MFEM_USE_MKL_CPARDISO), NO)
$(PAR_EXAMPLES):
$(error MFEM is not configured with MUMPS or CPARDISO)
endif
endif
all: $(EXAMPLES)
ObstacleProblem: ObstacleProblem.o Problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) ObstacleProblem.o Problems.o IPsolver.o -o $@ $(MFEM_LIBS)
SphericalObstacleProblem: SphericalObstacleProblem.o Problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) SphericalObstacleProblem.o Problems.o IPsolver.o -o $@ $(MFEM_LIBS)
DirichletObstacleProblem: DirichletObstacleProblem.o Problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) DirichletObstacleProblem.o Problems.o IPsolver.o -o $@ $(MFEM_LIBS)
DirichletObstacleProblemVariant: DirichletObstacleProblemVariant.o Problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) DirichletObstacleProblemVariant.o Problems.o IPsolver.o -o $@ $(MFEM_LIBS)
TwoSidedDirichletObstacleProblem: TwoSidedDirichletObstacleProblem.o Problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) TwoSidedDirichletObstacleProblem.o Problems.o IPsolver.o -o $@ $(MFEM_LIBS)
exQPContactBlockTL: exQPContactBlockTL.o Problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) exQPContactBlockTL.o Problems.o IPsolver.o -o $@ $(MFEM_LIBS)
ParTest: ParTest.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) ParTest.o -o $@ $(MFEM_LIBS)
ObstacleProblem.o: $(SRC)ObstacleProblem.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
SphericalObstacleProblem.o: $(SRC)SphericalObstacleProblem.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
DirichletObstacleProblem.o: $(SRC)DirichletObstacleProblem.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
DirichletObstacleProblemVariant.o: $(SRC)DirichletObstacleProblemVariant.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
TwoSidedDirichletObstacleProblem.o: $(SRC)TwoSidedDirichletObstacleProblem.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
exQPContactBlockTL.o: $(SRC)exQPContactBlockTL.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
Problems.o: $(SRC)Problems.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
IPsolver.o: $(SRC)IPsolver.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
ParObstacleProblem: ParObstacleProblem.o ParProblems.o ParIPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) ParObstacleProblem.o ParProblems.o ParIPsolver.o -o $@ $(MFEM_LIBS)
ParSphericalObstacleProblem: ParSphericalObstacleProblem.o ParProblems.o ParIPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) ParSphericalObstacleProblem.o ParProblems.o ParIPsolver.o -o $@ $(MFEM_LIBS)
ParObstacleProblem.o: $(SRC)ParObstacleProblem.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
ParSphericalObstacleProblem.o: $(SRC)ParSphericalObstacleProblem.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
ParProblems.o: $(SRC)ParProblems.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
ParIPsolver.o: $(SRC)ParIPsolver.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
ParTest.o: $(SRC)ParTest.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
clean: clean-build
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
# For out-of-source builds, link the data files from the source tree:
ifneq ($(SRC),)
DATA_FILES = block1.mesh rotatedblock2.mesh
$(DATA_FILES): %: $(SRC)%
ln -sf $(<) .
copy-data: | $(DATA_FILES)
# For out-of-source builds, the test and sample runs for 'field-interp' need
# data from the meshing miniapps directory:
exQPContactBlockTL: | mesh-data
.PHONY: mesh-data
mesh-data:
$(MAKE) -C ./ copy-data
endif
+896
View File
@@ -0,0 +1,896 @@
using namespace std;
using namespace mfem;
void BasisEval(const Vector xi, Vector &N, DenseMatrix &dNdxi) // dNdxi is 2*4
{
N[0] = 0.25*(1-xi[0])*(1-xi[1]);
N[1] = 0.25*(1+xi[0])*(1-xi[1]);
N[2] = 0.25*(1+xi[0])*(1+xi[1]);
N[3] = 0.25*(1-xi[0])*(1+xi[1]);
dNdxi(0,0) = 0.25*(-1+xi[1]);
dNdxi(0,1) = 0.25*(1-xi[1]);
dNdxi(0,2) = 0.25*(1+xi[1]);
dNdxi(0,3) = 0.25*(-1-xi[1]);
dNdxi(1,0) = 0.25*(-1+xi[0]);
dNdxi(1,1) = 0.25*(-1-xi[0]);
dNdxi(1,2) = 0.25*(1+xi[0]);
dNdxi(1,3) = 0.25*(1-xi[0]);
}
void BasisEvalDerivs(const Vector xi, Vector& N, DenseMatrix& dNdxi,
DenseMatrix& dN2dxi)
{
N[0] = 0.25*(1-xi[0])*(1-xi[1]);
N[1] = 0.25*(1+xi[0])*(1-xi[1]);
N[2] = 0.25*(1+xi[0])*(1+xi[1]);
N[3] = 0.25*(1-xi[0])*(1+xi[1]);
dNdxi.SetSize(2,4); dNdxi = 0.0;
dN2dxi.SetSize(3,4);
dN2dxi = 0.0; // first row dxi2, second detadxi, third deta2
dNdxi(0,0) = 0.25*(-1+xi[1]); dNdxi(0,1) = 0.25*(1-xi[1]);
dNdxi(0,2) = 0.25*(1+xi[1]); dNdxi(0,3) = 0.25*(-1-xi[1]);
dNdxi(1,0) = 0.25*(-1+xi[0]); dNdxi(1,1) = 0.25*(-1-xi[0]);
dNdxi(1,2) = 0.25*(1+xi[0]); dNdxi(1,3) = 0.25*(1-xi[0]);
dN2dxi(1,0) = 0.25; dN2dxi(1,1) = -0.25; dN2dxi(1,2) = 0.25;
dN2dxi(1,3) = -0.25;
}
// returns the vector and matrix form of the shape functions and its derivative
void BasisVectorDerivs(const Vector xi, DenseMatrix& N, DenseMatrix& dNdxi,
DenseMatrix& ddNdxi)
{
N.SetSize(3,12); N = 0.0;
N(0,0) = 0.25*(1-xi[0])*(1-xi[1]); N(0,3) = 0.25*(1+xi[0])*(1-xi[1]);
N(0,6) = 0.25*(1+xi[0])*(1+xi[1]); N(0,9) = 0.25*(1-xi[0])*(1+xi[1]);
N(1,1) = 0.25*(1-xi[0])*(1-xi[1]); N(1,4) = 0.25*(1+xi[0])*(1-xi[1]);
N(1,7) = 0.25*(1+xi[0])*(1+xi[1]); N(1,10) = 0.25*(1-xi[0])*(1+xi[1]);
N(2,2) = 0.25*(1-xi[0])*(1-xi[1]); N(2,5) = 0.25*(1+xi[0])*(1-xi[1]);
N(2,8) = 0.25*(1+xi[0])*(1+xi[1]); N(2,11) = 0.25*(1-xi[0])*(1+xi[1]);
dNdxi.SetSize(3*2, 3*4); dNdxi = 0.0;
dNdxi(0,0) = 0.25*(-1+xi[1]); dNdxi(0,3) = 0.25*(1-xi[1]);
dNdxi(0,6) = 0.25*(1+xi[1]); dNdxi(0,9) = 0.25*(-1-xi[1]);
dNdxi(1,1) = 0.25*(-1+xi[1]); dNdxi(1,4) = 0.25*(1-xi[1]);
dNdxi(1,7) = 0.25*(1+xi[1]); dNdxi(1,10) = 0.25*(-1-xi[1]);
dNdxi(2,2) = 0.25*(-1+xi[1]); dNdxi(2,5) = 0.25*(1-xi[1]);
dNdxi(2,8) = 0.25*(1+xi[1]); dNdxi(2,11) = 0.25*(-1-xi[1]);
dNdxi(3,0) = 0.25*(-1+xi[0]); dNdxi(3,3) = 0.25*(-1-xi[0]);
dNdxi(3,6) = 0.25*(1+xi[0]); dNdxi(3,9) = 0.25*(1-xi[0]);
dNdxi(4,1) = 0.25*(-1+xi[0]); dNdxi(4,4) = 0.25*(-1-xi[0]);
dNdxi(4,7) = 0.25*(1+xi[0]); dNdxi(4,10) = 0.25*(1-xi[0]);
dNdxi(5,2) = 0.25*(-1+xi[0]); dNdxi(5,5) = 0.25*(-1-xi[0]);
dNdxi(5,8) = 0.25*(1+xi[0]); dNdxi(5,11) = 0.25*(1-xi[0]);
ddNdxi.SetSize(3*4, 3*4); ddNdxi = 0.0;
ddNdxi(3,0) = 0.25; ddNdxi(3,3) = -0.25;
ddNdxi(3,6) = 0.25; ddNdxi(3,9) = -0.25;
ddNdxi(4,1) = 0.25; ddNdxi(4,4) = -0.25;
ddNdxi(4,7) = 0.25; ddNdxi(4,10) = -0.25;
ddNdxi(5,2) = 0.25; ddNdxi(5,5) = -0.25;
ddNdxi(5,8) = 0.25; ddNdxi(5,11) = -0.25;
ddNdxi(6,0) = 0.25; ddNdxi(6,3) = -0.25;
ddNdxi(6,6) = 0.25; ddNdxi(6,9) = -0.25;
ddNdxi(7,1) = 0.25; ddNdxi(7,4) = -0.25;
ddNdxi(7,7) = 0.25; ddNdxi(7,10) = -0.25;
ddNdxi(8,2) = 0.25; ddNdxi(8,5) = -0.25;
ddNdxi(8,8) = 0.25; ddNdxi(8,11) = -0.25;
}
void cross(const Vector a, const Vector b, Vector& c)
{
assert(a.Size()==3);
c.SetSize(3);
c[0] = a[1]*b[2] - a[2]*b[1];
c[1] = -a[0]*b[2] + b[0]*a[2];
c[2] = a[0]*b[1] - a[1]*b[0];
}
// a outer b
void outer(const Vector a, const Vector b, DenseMatrix& c)
{
int m = a.Size();
int n = b.Size();
assert(c.Height()==m);
assert(c.Width() ==n);
for (int i=0; i<m; i++)
{
for (int j=0; j<n; j++)
{
c(i,j) = a[i]*b[j];
}
}
}
// dphidxi 2*4
// coords 4*3
void ComputeNormal(const DenseMatrix& dphidxi, const DenseMatrix& coords,
Vector& normal, double& nnorm)
{
DenseMatrix dxdxi(2,3);
Mult(dphidxi, coords, dxdxi);
Vector dxdxi1(3);
Vector dxdxi2(3);
dxdxi.GetRow(0,dxdxi1);
dxdxi.GetRow(1,dxdxi2);
cross(dxdxi1, dxdxi2, normal); // is there a cross product? no
// VectorCrossProductCoefficient::Eval has hard-coded cross product
nnorm = normal.Norml2( );
normal /= nnorm;
}
void SlaveToMaster(const DenseMatrix& m_coords, const Vector& s_x, Vector& xi)
{
bool converged = false;
bool pt_on_elem = false;
int dim = 3;
xi.SetSize(dim-1);
xi = 0.0;
int max_iter = 15;
double off_el_xi = 1e-2;
double proj_newton_tol = 1e-13;
double proj_max_gap = 0.5;
Vector gap_v(dim);
// warm start from linear solution
for (int it=0; it<max_iter; it++)
{
//cout<<it<<endl;
Vector m_N(4);
m_N = 0.;
DenseMatrix m_dN(2,4);
m_dN = 0.;
DenseMatrix m_dN2(3,4);
m_dN2 = 0.;
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
Vector x_c(dim);
m_coords.MultTranspose(m_N, x_c);
gap_v = s_x;
gap_v -= x_c;
DenseMatrix m_dx(2,3);
m_dx = 0.;
Mult(m_dN, m_coords, m_dx);
Vector r(dim-1);
r = 0.0;
m_dx.Mult(gap_v, r);
if (r.Normlinf() < proj_newton_tol)
{
converged = true;
break;
}
DenseMatrix drdxi(dim-1,dim-1);
drdxi = 0.;
MultABt(m_dx, m_dx, drdxi); // m_dx * m_dx.T
drdxi *= -1.0;
DenseMatrix m_dx2(3,3); m_dx2 = 0.0;
Mult(m_dN2,m_coords, m_dx2);
//m_d2x = m_dN(:,:,2) * m_elem_coords(1:4,:); //m_dN(:,:,2) is 3*4
for (int d=0; d<3; d++)
{
DenseMatrix Mtemp(2,2); Mtemp = 0.0;
Mtemp(0,0) = m_dx2(0,d); Mtemp(0,1) = m_dx2(1,d);
Mtemp(1,0) = m_dx2(1,d); Mtemp(1,1) = m_dx2(2,d);
drdxi.Add(gap_v[d], Mtemp);
}
//cond_num = rcond(drdxi); condition number?
//drdxi.TestInversion();
DenseMatrixInverse drdxi_inv(drdxi);
Vector xi_tmp(dim-1);
drdxi_inv.Mult(r,xi_tmp);
xi -= xi_tmp;
}
if (!converged)
{
xi = 0.0;
}
off_el_xi += 1 ; // tolerance of offset of xi outside [-1,1]
//cout<<gap_v.Norml2()<<" " <<xi.Normlinf()<<endl;
//
// Discuss with Frank... what is happening here
if (gap_v.Norml2() < proj_max_gap && xi.Normlinf() <= off_el_xi)
{
pt_on_elem = true;
}
if (pt_on_elem)
{
//cout << "convergence of node to segment projection? " << converged << endl;
//for(int i = 0; i < 2; i++)
//{
// cout << "xi_" << i << " = " << xi(i) << endl;
//}
}
MFEM_VERIFY(pt_on_elem == true, "xi went out of bounds");
MFEM_VERIFY(converged == true, "projection didn't converge");
}
// m_coords is expected to be 4 * 3
void ComputeGapJacobian(const Vector x_s, const Vector xi,
const DenseMatrix m_coords,
double& gap, Vector& normal, Vector& dgdxm, Vector& dgdxs)
{
Vector m_N(4);
DenseMatrix m_dN(2,4);
DenseMatrix m_dN2(3,4);
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
Vector x_c(3);
m_coords.MultTranspose(m_N, x_c);
Vector gap_v(3); gap_v = 0.0;
gap_v = x_s;
gap_v -= x_c;
DenseMatrix m_dx(2,3);
Mult(m_dN, m_coords, m_dx);
double nnorm = 0;
ComputeNormal(m_dN, m_coords, normal, nnorm);
gap = gap_v * normal; // gap function value, dot product between vectors
//dr_dx = zeros(2,4,3); % nsegment, nodes in quad, ndim
DenseMatrix dr_dx_res1(4,3); dr_dx_res1 = 0.;
DenseMatrix dr_dx_res2(4,3); dr_dx_res2 = 0.;
Vector m_dxrow1(3);
m_dx.GetRow(0, m_dxrow1);
outer(m_N, m_dxrow1, dr_dx_res1);// 4*1 times 1*3
dr_dx_res1 *= -1.0;
Vector m_dxrow2(3);
m_dx.GetRow(1, m_dxrow2);
outer(m_N, m_dxrow2, dr_dx_res2);// 4*1 times 1*3
dr_dx_res2 *= -1.0;
Vector m_dNrow1(4); m_dN.GetRow(0, m_dNrow1);
Vector m_dNrow2(4); m_dN.GetRow(1, m_dNrow2);
DenseMatrix dr_dx_res1_tmp(4,3); dr_dx_res1_tmp = 0.;
DenseMatrix dr_dx_res2_tmp(4,3); dr_dx_res2_tmp = 0.;
outer(m_dNrow1, gap_v, dr_dx_res1_tmp);// 4*1 times 1*3
outer(m_dNrow2, gap_v, dr_dx_res2_tmp);// 4*1 times 1*3
dr_dx_res1 += dr_dx_res1_tmp; // outer product in vector?
dr_dx_res2 += dr_dx_res2_tmp;
DenseMatrix K_dxidx1(2,2); // 2*2
K_dxidx1 = 0.;
MultABt(m_dx, m_dx, K_dxidx1); // m_dx * m_dx.T
Vector v_dxidx2(4);
m_coords.Mult(gap_v, v_dxidx2); // m_coords * gap_v; // 4*3 * 3 = 4
DenseMatrix K_dxidx2(2,2); K_dxidx2 = 0.0;
Vector m_dN2row1(4); m_dN2.GetRow(0, m_dN2row1);
Vector m_dN2row2(4); m_dN2.GetRow(1, m_dN2row2);
Vector m_dN2row3(4); m_dN2.GetRow(2, m_dN2row3);
// how to get 2nd order? multidimensional matrix?
K_dxidx2(0,0) = m_dN2row1 * v_dxidx2; // how would 4*1 * 1*4 be computed?
K_dxidx2(0,1) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,0) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,1) = m_dN2row3 * v_dxidx2;
DenseMatrix K_dxidx(2,2);
K_dxidx -= K_dxidx1;
K_dxidx += K_dxidx2;
// resize the vectors and matrices
Vector dxidx(24); dxidx = 0.0;
Vector drdx_r(24); drdx_r = 0.0;
for (int i=0; i<4; i++)
{
for (int j=0; j<3; j++)
{
drdx_r[4*j+i] = dr_dx_res1(i,j);
drdx_r[4*j+i+12] = dr_dx_res2(i,j);
}
}
//drdx_r(1:4*3,1) = reshape(dr_dx_res(:,:,1),4*3,1);
//drdx_r(4*3+1:2*4*3,1) = reshape(dr_dx_res(:,:,2),4*3,1);
DenseMatrix drdx_K(24,24); drdx_K = 0.;
for (int i =0; i<12; i++)
{
drdx_K(i,i) = K_dxidx(0,0);
drdx_K(i,12+i) = K_dxidx(0,1);
drdx_K(12+i,i) = K_dxidx(1,0);
drdx_K(12+i,12+i) = K_dxidx(1,1);
}
DenseMatrixInverse drdxK_inv(drdx_K);
drdxK_inv.Mult(drdx_r,dxidx);
// LinearSolve (drdx_K,drdx_r, dxidx) ; //???
dxidx *= -1.0;
Vector drdxs_r(6);
drdxs_r[0] = m_dx(0,0); drdxs_r[1] = m_dx(0,1); drdxs_r[2] = m_dx(0,2);
drdxs_r[3] = m_dx(1,0); drdxs_r[4] = m_dx(1,1); drdxs_r[5] = m_dx(1,2);
DenseMatrix drdxs_K(6,6); drdxs_K = 0.;
for (int i=0; i<3; i++)
{
drdxs_K(i,i) = K_dxidx(0,0);
drdxs_K(i,3+i) = K_dxidx(0,1);
drdxs_K(i+3,i) = K_dxidx(1,0);
drdxs_K(i+3,i+3) = K_dxidx(1,1);
}
Vector dxidxs(6); dxidxs = 0.0;
DenseMatrixInverse drdxsK_inv(drdxs_K);
drdxsK_inv.Mult(drdxs_r,dxidxs);
dxidxs *= -1.0;
//dxidxs = -drdxs_K\drdxs_r;
//dxidx = reshape(dxidx, 4,3,2); dxidxs = reshape(dxidxs, 1,3,2);
dgdxm.SetSize(12); dgdxm = 0.;
DenseMatrix dgdxm_tmp(4,3);
outer(m_N, normal,dgdxm_tmp);
for (int i=0; i<4; i++)
{
for (int j=0; j<3; j++)
{
dgdxm[3*i+j] = -dgdxm_tmp(i,j);
}
}
//dxidx_M = -m_dN(1:2,:,1) * (m_coords(1:4,:)*normal'); % this turns out to be 0
dgdxs.SetSize(3);
dgdxs += normal;
//dgdxs = dgdxs + dxidx_M(1) * dxidxs(:,:,1) + dxidx_M(2) * dxidxs(:,:,2);
};
void ComputeGapHessian(const Vector x_s, const Vector xi,
const DenseMatrix m_coords,
DenseMatrix& dg2dx)
{
Vector m_N(4);
DenseMatrix m_dN(2,4);
DenseMatrix m_dN2(3,4);
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
int dim = 3;
int num_dofs1 = dim;
int num_dofs2 = 4*dim;
int num_dofs = num_dofs1 + num_dofs2;
dg2dx.SetSize(num_dofs,num_dofs); dg2dx = 0.0;
Vector x_c(3);
m_coords.MultTranspose(m_N,x_c);
Vector gap_v(3); gap_v = 0.0;
gap_v = x_s;
gap_v -= x_c;
DenseMatrix m_dx(2,3);
Mult(m_dN, m_coords, m_dx);
DenseMatrix m_dx2(3,3); m_dx2 = 0.0;
Mult(m_dN2,m_coords, m_dx2);
double nnorm = 0.0;
Vector normal(3); normal = 0.0;
ComputeNormal(m_dN, m_coords, normal, nnorm);
double gap = gap_v * normal; // gap function value, dot product between vectors
DenseMatrix M(2,2); M = 0.0;
MultABt(m_dx, m_dx, M);
DenseMatrix f(2, num_dofs2); f = 0.0;
for (int d=0; d<3; d++)
{
DenseMatrix Mtemp(2,2); Mtemp = 0.0;
Mtemp(0,0) = m_dx2(0,d); Mtemp(0,1) = m_dx2(1,d);
Mtemp(1,0) = m_dx2(1,d); Mtemp(1,1) = m_dx2(2,d);
M.Add(-gap_v[d], Mtemp);
Vector m_dxcol(2); m_dx.GetColumn(d, m_dxcol);
DenseMatrix ftmp(2,4);
outer(m_dxcol, m_N, ftmp);
ftmp *= -1;
ftmp.Add( gap_v[d], m_dN); // 2*4
for (int j=0; j<4; j++)
{
assert(d+3*j<num_dofs2);
f(0,d+j*3) = ftmp(0,j);
f(1,d+j*3) = ftmp(1,j);
}
}
//fprintf('hess dxidxm\n');
DenseMatrixInverse Minv(M);
DenseMatrix dxidxm(2,num_dofs2); dxidxm = 0.0;
Minv.Mult(f, dxidxm);
//LinearSolve??
//dxidxm = M\f;
DenseMatrix nde2(2,2); nde2 = 0.0;
DenseMatrix Nndx2(2,num_dofs2); Nndx2 = 0.0;
for (int d=0; d<3; d++)
{
DenseMatrix ndetmp(2,2); ndetmp = 0.0;
ndetmp(0,0) = normal(d)*m_dx2(0,d); ndetmp(0,1) = normal(d)*m_dx2(1,d);
ndetmp(1,0) = normal(d)*m_dx2(1,d); ndetmp(1,1) = normal(d)*m_dx2(2,d);
nde2 += ndetmp;
for (int j=0; j<4; j++)
{
assert(d+3*j<num_dofs2);
Nndx2(0,d+j*3) = normal[d]*m_dN(0,j);
Nndx2(1,d+j*3) = normal[d]*m_dN(1,j);
}
}
DenseMatrix Ndn(2,num_dofs2); Ndn = 0.0;
Ndn += Nndx2;
AddMult(nde2, dxidxm, Ndn);
DenseMatrix M2(2,2); M2 = 0.0;
MultABt(m_dx, m_dx, M2);
DenseMatrixInverse M2inv(M2);
DenseMatrix diag2(2,2); diag2(0,0) = 1.0; diag2(1,1) = 1.0;
DenseMatrix m_con(2,2); m_con = 0.0;
M2inv.Mult(diag2, m_con);
DenseMatrix dg2dxm(num_dofs2, num_dofs2); dg2dxm = 0.0;
DenseMatrix dg2dxm_tmp(num_dofs2,2); dg2dxm_tmp = 0.0;
MultAtB(Ndn, m_con, dg2dxm_tmp);
Mult(dg2dxm_tmp, Ndn, dg2dxm);
dg2dxm *= gap;
DenseMatrix dg2dxm_tmp2(num_dofs2,num_dofs2); dg2dxm_tmp2 = 0.0;
MultAtB(Nndx2, dxidxm, dg2dxm_tmp2);
dg2dxm.Add(-1.0, dg2dxm_tmp2);
dg2dxm_tmp = 0.0;
MultAtB(dxidxm, nde2, dg2dxm_tmp);
AddMult_a(-1.0, dg2dxm_tmp, dxidxm, dg2dxm);
dg2dxm_tmp2 = 0.0;
MultAtB(dxidxm, Nndx2, dg2dxm_tmp2);
dg2dxm.Add(-1.0, dg2dxm_tmp2);
Vector v_dxidx2(4);
m_coords.Mult(gap_v, v_dxidx2); // m_coords * gap_v; // 4*3 * 3 = 4
DenseMatrix K_dxidx2(2,2); K_dxidx2 = 0.0;
Vector m_dN2row1(4); m_dN2.GetRow(0, m_dN2row1);
Vector m_dN2row2(4); m_dN2.GetRow(1, m_dN2row2);
Vector m_dN2row3(4); m_dN2.GetRow(2, m_dN2row3);
K_dxidx2(0,0) = m_dN2row1 * v_dxidx2; // how would 4*1 * 1*4 be computed?
K_dxidx2(0,1) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,0) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,1) = m_dN2row3 * v_dxidx2;
DenseMatrix K_dxidx(2,2);
K_dxidx -= M2;
K_dxidx += K_dxidx2;
Vector drdxs_r(6);
drdxs_r[0] = m_dx(0,0); drdxs_r[1] = m_dx(0,1); drdxs_r[2] = m_dx(0,2);
drdxs_r[3] = m_dx(1,0); drdxs_r[4] = m_dx(1,1); drdxs_r[5] = m_dx(1,2);
DenseMatrix drdxs_K(6,6); drdxs_K = 0.;
for (int i=0; i<3; i++)
{
drdxs_K(i,i) = K_dxidx(0,0);
drdxs_K(i,3+i) = K_dxidx(0,1);
drdxs_K(i+3,i) = K_dxidx(1,0);
drdxs_K(i+3,i+3) = K_dxidx(1,1);
}
Vector dxidxs(6);
DenseMatrixInverse drdxsK_inv(drdxs_K);
drdxsK_inv.Mult(drdxs_r,dxidxs);
dxidxs *= -1.0;
//dxidxs = -drdxs_K\drdxs_r;
DenseMatrix dxidxs_m(2,3); dxidxs_m = 0.0;
dxidxs_m(0,0) = dxidxs[0]; dxidxs_m(0,1) = dxidxs[1]; dxidxs_m(0,2) = dxidxs[2];
dxidxs_m(1,0) = dxidxs[3]; dxidxs_m(1,1) = dxidxs[4]; dxidxs_m(1,2) = dxidxs[5];
DenseMatrix dtao1dxs(3,3); dtao1dxs = 0.0;
DenseMatrix dtao2dxs(3,3); dtao2dxs = 0.0;
Vector dxidxs_row1(3); dxidxs_row1 = 0.0; Vector dxidxs_row2(3);
dxidxs_row2 = 0.0;
Vector mdx2_row1(3); mdx2_row1 = 0.0; Vector mdx2_row2(3); mdx2_row2 = 0.0;
Vector mdx2_row3(3); mdx2_row3 = 0.0;
dxidxs_m.GetRow(0,dxidxs_row1);
dxidxs_m.GetRow(1,dxidxs_row2);
m_dx2.GetRow(0,mdx2_row1);
m_dx2.GetRow(1,mdx2_row2);
m_dx2.GetRow(2,mdx2_row3);
DenseMatrix dtaotmp(3,3); dtaotmp = 0.0;
outer(mdx2_row1, dxidxs_row1,dtaotmp);
dtao1dxs += dtaotmp; dtaotmp = 0.0;
outer(mdx2_row2, dxidxs_row1,dtaotmp);
dtao1dxs += dtaotmp; dtaotmp = 0.0;
outer(mdx2_row2, dxidxs_row2, dtaotmp);
dtao2dxs += dtaotmp; dtaotmp = 0.0;
outer(mdx2_row3, dxidxs_row2, dtaotmp);
dtao2dxs += dtaotmp; dtaotmp = 0.0;
DenseMatrix dtaodxs(3,3); dtaodxs = 0.0; //tao = tao1 cross tao2
for (int d=0; d<3; d++)
{
Vector dtao1dxs_tmp(3); dtao1dxs_tmp = 0.0;
dtao1dxs.GetColumn(d,dtao1dxs_tmp);
Vector m_dxrow(3); m_dx.GetRow(1, m_dxrow);
Vector dtaodxs_tmp(3); dtaodxs_tmp = 0.0;
cross(dtao1dxs_tmp, m_dxrow, dtaodxs_tmp);
Vector dtaodxs_tmp2(3); dtaodxs_tmp2 = 0.0;
m_dx.GetRow(0, m_dxrow);
dtao1dxs_tmp = 0.0; // reuse the same vector for dtao2
dtao2dxs.GetColumn(d,dtao1dxs_tmp);
cross(m_dxrow, dtao1dxs_tmp, dtaodxs_tmp2);
dtaodxs_tmp2 += dtaodxs_tmp;
dtaodxs.SetCol(d, dtaodxs_tmp2);
}
DenseMatrix dndxs(3,3); dndxs = 0.0; dndxs += dtaodxs; dndxs *= 1.0/nnorm;
DenseMatrix dndxs_tmp(3,3); dndxs_tmp = 0.0;
outer(normal, normal, dndxs_tmp);
AddMult_a(-1/nnorm, dndxs_tmp, dtaodxs, dndxs);
DenseMatrix dgvdxs(3,3); dgvdxs = 0.0;
MultAtB(m_dx, dxidxs_m, dgvdxs);
dgvdxs *= -1;
for (int d=0; d<3; d++)
{
dgvdxs(d,d) += 1.0;
}
//dxidxs: 2*3
DenseMatrix dg2dxs(3,3); dg2dxs = 0.0;
DenseMatrix dg2dxs_tmp(3,2); dg2dxs_tmp = 0.0;
MultAtB(dxidxs_m, nde2, dg2dxs_tmp);
AddMult_a(-1.0, dg2dxs_tmp, dxidxs_m, dg2dxs);
DenseMatrix dg2dxs_tmp2(3,3); dg2dxs_tmp2 = 0.0;
MultAtB(dgvdxs, dndxs, dg2dxs_tmp2);
dg2dxs += dg2dxs_tmp2;
dg2dxs_tmp2 = 0.0;
MultAtB(dndxs, dndxs_tmp, dg2dxs_tmp2);
AddMult(dg2dxs_tmp2, dgvdxs, dg2dxs);
DenseMatrix Ne(3,12), Be(6,12), dBe(12,12);
BasisVectorDerivs(xi, Ne, Be, dBe);
DenseMatrix dtao1dxm(3,12); dtao1dxm.CopyRows(Be, 0, 2);
DenseMatrix dtao2dxm(3,12); dtao2dxm.CopyRows(Be, 3, 5);
Vector m_coords_v(12);
for (int i=0; i<4; i++)
{
for (int j=0; j<3; j++)
{
m_coords_v[i*3+j] = m_coords(i,j);
}
}
for (int i=0; i<2; i++)
{
Vector dxidxm_tmp(num_dofs2); dxidxm_tmp = 0.0;
dxidxm.GetRow(i,dxidxm_tmp);
DenseMatrix dBe_tmp(3,12);
dBe_tmp.CopyRows(dBe,i*3,(i+1)*3-1);
DenseMatrix dtaodxm_tmp(12,12); dtaodxm_tmp = 0.0;
outer(m_coords_v, dxidxm_tmp, dtaodxm_tmp);
AddMult(dBe_tmp, dtaodxm_tmp, dtao1dxm);
//dtao1dxm += dBe(:,:,i)*reshape(m_coords(1:4,:)',12,1)*reshape(dxidxm(i,:),1,12); % 3*12
dBe_tmp = 0.0;
dBe_tmp.CopyRows(dBe,(i+2)*3,(i+3)*3-1);
AddMult(dBe_tmp, dtaodxm_tmp, dtao2dxm);
}
DenseMatrix dtaodxm(3,12); dtaodxm = 0.0;//tao = tao1 cross tao2
for (int d=0; d<12; d++)
{
Vector dtaodxm_tmp(3); dtaodxm_tmp = 0.0;
Vector dtaodxm_tmp2(3); dtaodxm_tmp2 = 0.0;
Vector tmp1(3); tmp1 = 0.0; dtao1dxm.GetColumn(d,tmp1);
Vector m_dxrow2(3); m_dx.GetRow(1, m_dxrow2);
Vector m_dxrow1(3); m_dx.GetRow(0, m_dxrow1);
Vector tmp2(3); tmp2 = 0.0; dtao2dxm.GetColumn(d,tmp2);
cross(tmp1, m_dxrow2, dtaodxm_tmp);
cross(m_dxrow1,tmp2, dtaodxm_tmp2);
dtaodxm_tmp += dtaodxm_tmp2;
dtaodxm.SetCol(d, dtaodxm_tmp);
}
DenseMatrix dndxm(3,12); dndxm = 0.0;
dndxm += dtaodxm;
dndxm *= 1.0/nnorm;
AddMult_a(-1/nnorm, dndxs_tmp, dtaodxm, dndxm); //dndxs_tmp = normal'*normal
DenseMatrix dgvdxm(3,12); dgvdxm = 0.0;
dgvdxm -= Ne;
for (int i=0; i<2; i++)
{
Vector dxidxm_tmp(num_dofs2); dxidxm_tmp = 0.0;
dxidxm.GetRow(i,dxidxm_tmp);
DenseMatrix Be_tmp(3,12);
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
DenseMatrix dgvdxm_tmp(12,12); dgvdxm_tmp = 0.0;
outer(m_coords_v, dxidxm_tmp, dgvdxm_tmp);
AddMult_a(-1.0, Be_tmp, dgvdxm_tmp, dgvdxm);
}
DenseMatrix dg2dxsxm(3,12); dg2dxsxm = 0.0;
DenseMatrix dg2dxsxm_tmp(3,3); dg2dxsxm_tmp = 0.0;
MultAtB(dgvdxs, dndxm, dg2dxsxm);
MultAtB(dndxs, dndxs_tmp, dg2dxsxm_tmp);
AddMult(dg2dxsxm_tmp, dgvdxm, dg2dxsxm); // += dndxs'*normal'*normal*dgvdxm;
DenseMatrix dgvdxsxmn(3,12); dgvdxsxmn = 0.0;
DenseMatrix dgvdxsxmn_tmp(3,2); dgvdxsxmn_tmp = 0.0;
MultAtB(dxidxs_m, nde2, dgvdxsxmn_tmp); //dxidxs_m: 2*3
AddMult_a(-1.0, dgvdxsxmn_tmp, dxidxm, dgvdxsxmn);
for (int i =0; i<2; i++)
{
DenseMatrix Be_tmp(3,12);
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
Vector dxidxs_row(3); dxidxs_row = 0.0; dxidxs_m.GetRow(i,dxidxs_row);
DenseMatrix dgvdxsxmn_tmp2(3,3); dgvdxsxmn_tmp2 = 0.0;
outer(dxidxs_row, normal, dgvdxsxmn_tmp2);
AddMult_a(-1.0, dgvdxsxmn_tmp2, Be_tmp, dgvdxsxmn);
}
dg2dxsxm += dgvdxsxmn;
DenseMatrix dg2dxmxs(12,3); dg2dxmxs = 0.0;
DenseMatrix dg2dxmxs_tmp(12,3); dg2dxmxs_tmp = 0.0;
MultAtB(dgvdxm, dndxs, dg2dxmxs);
MultAtB(dndxm, dndxs_tmp, dg2dxmxs_tmp);
AddMult(dg2dxmxs_tmp, dgvdxs, dg2dxmxs);
DenseMatrix dgvdxmxsn(12,3); dgvdxmxsn = 0.0;
DenseMatrix dgvdxmxsn_tmp(12,2); dgvdxmxsn_tmp = 0.0;
MultAtB(dxidxm, nde2, dgvdxmxsn_tmp);
dgvdxmxsn_tmp *= -1.0;
AddMult(dgvdxmxsn_tmp, dxidxs_m, dgvdxmxsn);
for (int i =0; i<2; i++)
{
DenseMatrix Be_tmp(3,12);
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
Be_tmp.Transpose(); // Be is now 12*3
Vector dxidxs_row(3); dxidxs_row = 0.0; dxidxs_m.GetRow(i,dxidxs_row);
DenseMatrix dgvdxmxsn_tmp2(3,3); dgvdxmxsn_tmp2 = 0.0;
outer(normal, dxidxs_row, dgvdxmxsn_tmp2);
AddMult_a(-1.0, Be_tmp, dgvdxmxsn_tmp2, dgvdxmxsn);
}
dg2dxmxs += dgvdxmxsn;
dg2dx.CopyMN(dg2dxs, 0, 0);
dg2dx.CopyMN(dg2dxm, 3, 3);
dg2dx.CopyMN(dg2dxsxm, 0, 3);
dg2dx.CopyMN(dg2dxmxs, 3, 0);
};
void NodeSegConPairs(const Vector x1, const Vector xi2,
const DenseMatrix coords2,
double& node_g, Vector& node_dg, DenseMatrix& node_dg2)
{
double gap = 0.0;
Vector normal(3); normal = 0.0;
Vector dgdxm(12); dgdxm = 0.0;
Vector dgdxs(3); dgdxs = 0.0;
ComputeGapJacobian(x1, xi2, coords2, gap, normal, dgdxm, dgdxs);
node_g = gap;
node_dg.SetSize(12+3);
for (int i=0; i<3; i++) { node_dg[i] = dgdxs[i]; }
for (int i=0; i<12; i++) { node_dg[i+3] = dgdxm[i]; }
DenseMatrix dg2dx(15,15); dg2dx = 0.0;
DenseMatrix dgvdxmxsn(12,3); dgvdxmxsn = 0.0;
ComputeGapHessian(x1, xi2, coords2, dg2dx);
node_dg2.SetSize(15,15);
node_dg2 = dg2dx;
/*
if(obj.space1.conns{e1}(i)==150) % for debugging purpose
v1 = 1:3;
v2 = 1:12;
%v1 = ones(1,3)
%v2 = ones(1,12)
v2 = reshape(v2,4,3);
x1n1 = x1 + 0.01*v1;
coords2n1 = coords2 + 0.001*v2;
[xi2n1, gapv1, ~, ~] = SlaveToMaster(obj, coords2n1, x1n1);
[gapn1, n1,dgdxmn1, dgdxsn1] = ComputeGapJacobian(obj, x1n1, xi2n1, coords2n1);
x1n2 = x1 - 0.01*v1;
coords2n2 = coords2 - 0.001*v2;
[xi2n2, gapv2, ~, ~] = SlaveToMaster(obj, coords2n2, x1n2);
[gapn2, n2,dgdxmn2, dgdxsn2] = ComputeGapJacobian(obj, x1n2, xi2n2, coords2n2);
fprintf('fd\n');
%gapv1-gapv2
[dgdxsn1(:)',dgdxmn1(:)'] - [dgdxsn2(:)',dgdxmn2(:)']
%dgdxsn1-dgdxsn2
fprintf('code\n');
v2n = v2';
%dg2dx(1:3,1:3)*0.04*ones(3,1)
temp = zeros(12,3);
for i = 1:4
temp1 = dg2dx(3+(i-1)*3+1:3+i*3,1:3);
temp((i-1)*3+1:i*3,:) = temp1';
end
temp2 = zeros(3,12);
for i = 1:4
temp3 = dg2dx(1:3,3+(i-1)*3+1:3+i*3);
temp2(:,(i-1)*3+1:i*3) = temp3';
end
%dg2dx
%dg2dx(4:end,1:3) = temp;
%dg2dx(1:3,4:end) = temp2;
%dgvdxm * 0.002*v2n(:)
(dg2dx*[0.02*v1(:)',0.002*v2n(:)']')'
%dg2dx(4:end,1:3)
end*/
};
// coordsm : (npoints*4, 3) use what class?
// m_conn: (npoints*4)
void Assemble_Contact(const int m, const int npoints, const int ndofs,
const Vector x_s,
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
const Array<int> m_conn, Vector& g, SparseMatrix& M,
std::vector<SparseMatrix>& dM)
{
int ndim = 3;
g.SetSize(m);
g = 0.0;
//SparseMatrix M(m, n); // M needs to be the correct size
//dM.resize(m); // needs to clear?
double g_tmp = 0.;
Vector dg(4*ndim+ndim);
dg = 0.;
DenseMatrix dg2(4*ndim+ndim,4*ndim+ndim);
dg2 = 0.;
for (int i=0; i<npoints; i++)
{
Vector x1(ndim);
x1[0] = x_s[i*ndim];
x1[1] = x_s[i*ndim+1];
x1[2] = x_s[i*ndim+2];
Vector xi2(ndim-1);
xi2[0] = xi[i*(ndim-1)];
xi2[1] = xi[i*(ndim-1)+1];
DenseMatrix coords2(4,3);
coords2.CopyRows(coordsm, i*4,(i+1)*4-1);
//how to get coords2?
dg = 0.0;
dg2 = 0.;
NodeSegConPairs(x1, xi2, coords2, g_tmp, dg, dg2);
//x1.Print();
//xi2.Print();
//coords2.Print();
g[s_conn[i]] = g_tmp; // should be unique
Array<int> m_conn_i(4);
m_conn.GetSubArray(4*i, 4, m_conn_i);
Array<int> node_conn(5);
node_conn[0] = s_conn[i];
for (int j=0; j<4; j++)
{
node_conn[j+1] = m_conn_i[j];
}
Array<int> M_i_tmp(1);
M_i_tmp[0] = s_conn[i];
//j_idx = (node_conn-1)*obj.disp_field.num_components +repmat((1:obj.disp_field.num_components)', 1, length(node_conn{i}));
Array<int> j_idx(5*ndim); j_idx = 0;
for (int j=0; j< 5; j++)
{
for (int k=0; k<ndim; k++)
{
j_idx[j*ndim+k] = node_conn[j]*ndim+k;
}
}
DenseMatrix M_v_tmp(1, ndim*(4+1)); // SetData now?
M_v_tmp.SetRow(0, dg);
M.AddSubMatrix(M_i_tmp, j_idx, M_v_tmp);
Array<int> dM_i(ndim*(4+1));
Array<int> dM_j(ndim*(4+1));
for (int j=0; j< ndim*(4+1); j++)
{
dM_i[j] = j_idx[j];
dM_j[j] = j_idx[j];
}
//dg2.Print();
//dM[s_conn[i]].Print();
dM[s_conn[i]].AddSubMatrix(dM_i,dM_j, dg2);
}
};
+119
View File
@@ -0,0 +1,119 @@
#include "mfem.hpp"
#include "Problems.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double dmanufacturedFun(const Vector &);
double fRhs(const Vector &);
double obstacle(const Vector &);
int main(int argc, char *argv[])
{
int FEorder = 1; // order of the finite elements
int linSolver = 0;
int maxIPMiters = 30;
bool iAmRoot = true;
OptionsParser args(argc, argv);
args.AddOption(&FEorder, "-o", "--order",\
"Order of the finite elements.");
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
const char *meshFile = "../../data/inline-quad.mesh";
Mesh *mesh = new Mesh(meshFile, 1, 1);
int dim = mesh->Dimension(); // geometric dimension of the domain
{
int ref_levels = 3;
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
FiniteElementCollection *fec = new H1_FECollection(FEorder, dim);
FiniteElementSpace *Vh = new FiniteElementSpace(mesh, fec);
ObstacleProblem problem(Vh, &fRhs, &obstacle);
int dimD = problem.GetDimD();
Vector x0(dimD); x0 = 0.0;
Vector xf(dimD); xf = 0.0;
InteriorPointSolver optimizer(&problem);
optimizer.SetTol(1.e-7);
optimizer.SetLinearSolver(linSolver);
optimizer.SetMaxIter(maxIPMiters);
optimizer.Mult(x0, xf);
GridFunction d_gf(Vh);
d_gf = xf;
FunctionCoefficient dm_fc(dmanufacturedFun); // pseudo-manufactured solution
GridFunction dm_gf(Vh);
dm_gf.ProjectCoefficient(dm_fc);
ParaViewDataCollection paraview_dc("BarrierProblemSolution", mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(FEorder);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("d(x) (numerical)", &d_gf);
paraview_dc.RegisterField("d(x) (pseudo-manufactured)", &dm_gf);
paraview_dc.Save();
delete Vh;
delete fec;
delete mesh;
return 0;
}
double dmanufacturedFun(const Vector &x)
{
return cos(2*M_PI*x(0)) + 0.2 - 2.0*(pow(x(0),3) - 1.5*pow(x(0),2));
}
// f(x) forcing term... which enters the objective energy functional
// E(d) = 0.5 d^T K d - f^T d, where f is a discrete vector representation
// of f(x). f(x) is such that in the absence of bound-constraints then
// the solution of the optimization problem satisfies the PDE
// -div(grad(d)) + d = f + homogeneous Neumann conditions on the unit interval,
// for d(x) = cos(2 \pi x) + a0 + a3 (x^3 - 1.5 x^2), a2 = 0.2, a3 = -2
double fRhs(const Vector &x)
{
double fx = 0.;
fx = 0.2 - 2.0 * (pow(x(0),3)- 1.5*pow(x(0),2.) - 6 * x(0) + 3.) + (1. + pow(2.*M_PI,2))*cos(2.*M_PI*x(0));
return fx;
}
double obstacle(const Vector &x)
{
return 0.0;
}
+70
View File
@@ -0,0 +1,70 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
3
# 1 nothing
elements
4
1 5 0 1 3 2 6 7 9 8
1 5 2 3 5 4 8 9 11 10
1 5 6 7 9 8 12 13 15 14
1 5 8 9 11 10 14 15 17 16
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
16
1 3 1 0 2 3
1 3 3 2 4 5
1 3 12 13 15 14
1 3 14 15 17 16
3 3 2 0 6 8
3 3 4 2 8 10
3 3 8 6 12 14
3 3 10 8 14 16
2 3 1 3 9 7
2 3 3 5 11 9
2 3 7 9 15 13
2 3 9 11 17 15
1 3 6 0 1 7
1 3 12 6 7 13
1 3 4 10 11 5
1 3 10 16 17 11
vertices
18
3
0.000000000000 0.145770950245 0.443895630208
0.507100000000 0.145770950245 0.443895630208
0.000000000000 0.350937660019 0.294833290227
0.507100000000 0.350937660019 0.294833290227
0.000000000000 0.556104369792 0.145770950245
0.507100000000 0.556104369792 0.145770950245
0.000000000000 0.294833290227 0.649062339981
0.507100000000 0.294833290227 0.649062339981
0.000000000000 0.500000000000 0.500000000000
0.507100000000 0.500000000000 0.500000000000
0.000000000000 0.705166709773 0.350937660019
0.507100000000 0.705166709773 0.350937660019
0.000000000000 0.443895630208 0.854229049755
0.507100000000 0.443895630208 0.854229049755
0.000000000000 0.649062339981 0.705166709773
0.507100000000 0.649062339981 0.705166709773
0.000000000000 0.854229049755 0.556104369792
0.507100000000 0.854229049755 0.556104369792
+36 -33
View File
@@ -32,7 +32,6 @@
// We recommend viewing Example 22 before viewing this example.
#include "mfem.hpp"
#include <memory>
#include <fstream>
#include <iostream>
@@ -45,7 +44,7 @@ using namespace std;
using namespace mfem;
// Class for setting up a simple Cartesian PML region
class PML
class CartesianPML
{
private:
Mesh *mesh;
@@ -70,7 +69,7 @@ private:
public:
// Constructor
PML(Mesh *mesh_,Array2D<double> length_);
CartesianPML(Mesh *mesh_,Array2D<double> length_);
// Return Computational Domain Boundary
Array2D<double> GetCompDomainBdr() {return comp_dom_bdr;}
@@ -92,12 +91,12 @@ public:
class PMLDiagMatrixCoefficient : public VectorCoefficient
{
private:
PML * pml = nullptr;
void (*Function)(const Vector &, PML *, Vector &);
CartesianPML * pml = nullptr;
void (*Function)(const Vector &, CartesianPML *, Vector &);
public:
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, PML *,
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
Vector &),
PML * pml_)
CartesianPML * pml_)
: VectorCoefficient(dim), pml(pml_), Function(F)
{}
@@ -126,13 +125,13 @@ void source(const Vector &x, Vector & f);
// Functions for computing the necessary coefficients after PML stretching.
// J is the Jacobian matrix of the stretching function
void detJ_JT_J_inv_Re(const Vector &x, PML * pml, Vector &D);
void detJ_JT_J_inv_Im(const Vector &x, PML * pml, Vector &D);
void detJ_JT_J_inv_abs(const Vector &x, PML * pml, Vector &D);
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector &D);
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector &D);
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector &D);
void detJ_inv_JT_J_Re(const Vector &x, PML * pml, Vector &D);
void detJ_inv_JT_J_Im(const Vector &x, PML * pml, Vector &D);
void detJ_inv_JT_J_abs(const Vector &x, PML * pml, Vector &D);
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector &D);
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector &D);
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector &D);
Array2D<double> comp_domain_bdr;
Array2D<double> domain_bdr;
@@ -268,7 +267,7 @@ int main(int argc, char *argv[])
length = 0.25;
break;
}
PML * pml = new PML(mesh,length);
CartesianPML * pml = new CartesianPML(mesh,length);
comp_domain_bdr = pml->GetCompDomainBdr();
domain_bdr = pml->GetDomainBdr();
@@ -468,14 +467,16 @@ int main(int argc, char *argv[])
offsets[2] = fespace->GetTrueVSize();
offsets.PartialSum();
std::unique_ptr<Operator> pc_r;
std::unique_ptr<Operator> pc_i;
Operator *pc_r = nullptr;
Operator *pc_i = nullptr;
double s = (conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0;
if (pa)
{
// Jacobi Smoother
pc_r.reset(new OperatorJacobiSmoother(prec, ess_tdof_list));
pc_i.reset(new ScaledOperator(pc_r.get(), s));
OperatorJacobiSmoother *d00 = new OperatorJacobiSmoother(prec, ess_tdof_list);
ScaledOperator *d11 = new ScaledOperator(d00, s);
pc_r = d00;
pc_i = d11;
}
else
{
@@ -484,13 +485,15 @@ int main(int argc, char *argv[])
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
// Gauss-Seidel Smoother
pc_r.reset(new GSSmoother(*PCOpAh.As<SparseMatrix>()));
pc_i.reset(new ScaledOperator(pc_r.get(), s));
GSSmoother *gs00 = new GSSmoother(*PCOpAh.As<SparseMatrix>());
ScaledOperator *gs11 = new ScaledOperator(gs00, s);
pc_r = gs00;
pc_i = gs11;
}
BlockDiagonalPreconditioner BlockDP(offsets);
BlockDP.SetDiagonalBlock(0, pc_r.get());
BlockDP.SetDiagonalBlock(1, pc_i.get());
BlockDP.SetDiagonalBlock(0, pc_r);
BlockDP.SetDiagonalBlock(1, pc_i);
GMRESSolver gmres;
gmres.SetPrintLevel(1);
@@ -804,7 +807,7 @@ void E_bdr_data_Im(const Vector &x, Vector &E)
}
}
void detJ_JT_J_inv_Re(const Vector &x, PML * pml, Vector &D)
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector &D)
{
vector<complex<double>> dxs(dim);
complex<double> det(1.0, 0.0);
@@ -821,7 +824,7 @@ void detJ_JT_J_inv_Re(const Vector &x, PML * pml, Vector &D)
}
}
void detJ_JT_J_inv_Im(const Vector &x, PML * pml, Vector &D)
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector &D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -838,7 +841,7 @@ void detJ_JT_J_inv_Im(const Vector &x, PML * pml, Vector &D)
}
}
void detJ_JT_J_inv_abs(const Vector &x, PML * pml, Vector &D)
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector &D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -855,7 +858,7 @@ void detJ_JT_J_inv_abs(const Vector &x, PML * pml, Vector &D)
}
}
void detJ_inv_JT_J_Re(const Vector &x, PML * pml, Vector &D)
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector &D)
{
vector<complex<double>> dxs(dim);
complex<double> det(1.0, 0.0);
@@ -880,7 +883,7 @@ void detJ_inv_JT_J_Re(const Vector &x, PML * pml, Vector &D)
}
}
void detJ_inv_JT_J_Im(const Vector &x, PML * pml, Vector &D)
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector &D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -904,7 +907,7 @@ void detJ_inv_JT_J_Im(const Vector &x, PML * pml, Vector &D)
}
}
void detJ_inv_JT_J_abs(const Vector &x, PML * pml, Vector &D)
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector &D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -928,14 +931,14 @@ void detJ_inv_JT_J_abs(const Vector &x, PML * pml, Vector &D)
}
}
PML::PML(Mesh *mesh_, Array2D<double> length_)
CartesianPML::CartesianPML(Mesh *mesh_, Array2D<double> length_)
: mesh(mesh_), length(length_)
{
dim = mesh->Dimension();
SetBoundaries();
}
void PML::SetBoundaries()
void CartesianPML::SetBoundaries()
{
comp_dom_bdr.SetSize(dim, 2);
dom_bdr.SetSize(dim, 2);
@@ -950,7 +953,7 @@ void PML::SetBoundaries()
}
}
void PML::SetAttributes(Mesh *mesh_)
void CartesianPML::SetAttributes(Mesh *mesh_)
{
// Initialize bdr attributes
for (int i = 0; i < mesh_->GetNBE(); ++i)
@@ -999,8 +1002,8 @@ void PML::SetAttributes(Mesh *mesh_)
mesh_->SetAttributes();
}
void PML::StretchFunction(const Vector &x,
vector<complex<double>> &dxs)
void CartesianPML::StretchFunction(const Vector &x,
vector<complex<double>> &dxs)
{
complex<double> zi = complex<double>(0., 1.);
+38 -34
View File
@@ -44,7 +44,7 @@ using namespace std;
using namespace mfem;
// Class for setting up a simple Cartesian PML region
class PML
class CartesianPML
{
private:
Mesh *mesh;
@@ -69,7 +69,7 @@ private:
public:
// Constructor
PML(Mesh *mesh_,Array2D<double> length_);
CartesianPML(Mesh *mesh_,Array2D<double> length_);
// Return Computational Domain Boundary
Array2D<double> GetCompDomainBdr() {return comp_dom_bdr;}
@@ -91,12 +91,12 @@ public:
class PMLDiagMatrixCoefficient : public VectorCoefficient
{
private:
PML * pml = nullptr;
void (*Function)(const Vector &, PML *, Vector &);
CartesianPML * pml = nullptr;
void (*Function)(const Vector &, CartesianPML *, Vector &);
public:
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, PML *,
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
Vector &),
PML * pml_)
CartesianPML * pml_)
: VectorCoefficient(dim), pml(pml_), Function(F)
{}
@@ -125,13 +125,13 @@ void source(const Vector &x, Vector & f);
// Functions for computing the necessary coefficients after PML stretching.
// J is the Jacobian matrix of the stretching function
void detJ_JT_J_inv_Re(const Vector &x, PML * pml, Vector & D);
void detJ_JT_J_inv_Im(const Vector &x, PML * pml, Vector & D);
void detJ_JT_J_inv_abs(const Vector &x, PML * pml, Vector & D);
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector & D);
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector & D);
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector & D);
void detJ_inv_JT_J_Re(const Vector &x, PML * pml, Vector & D);
void detJ_inv_JT_J_Im(const Vector &x, PML * pml, Vector & D);
void detJ_inv_JT_J_abs(const Vector &x, PML * pml, Vector & D);
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector & D);
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector & D);
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector & D);
Array2D<double> comp_domain_bdr;
Array2D<double> domain_bdr;
@@ -295,7 +295,7 @@ int main(int argc, char *argv[])
length = 0.25;
break;
}
PML * pml = new PML(mesh,length);
CartesianPML * pml = new CartesianPML(mesh,length);
comp_domain_bdr = pml->GetCompDomainBdr();
domain_bdr = pml->GetDomainBdr();
@@ -478,11 +478,11 @@ int main(int argc, char *argv[])
if (!pa && mumps_solver)
{
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
MUMPSSolver mumps(A->GetComm());
MUMPSSolver mumps;
mumps.SetPrintLevel(0);
mumps.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
mumps.SetOperator(*A);
mumps.Mult(B, X);
mumps.Mult(B,X);
delete A;
}
#endif
@@ -524,14 +524,16 @@ int main(int argc, char *argv[])
offsets[2] = fespace->GetTrueVSize();
offsets.PartialSum();
std::unique_ptr<Operator> pc_r;
std::unique_ptr<Operator> pc_i;
Operator *pc_r = nullptr;
Operator *pc_i = nullptr;
int s = (conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0;
if (pa)
{
// Jacobi Smoother
pc_r.reset(new OperatorJacobiSmoother(prec, ess_tdof_list));
pc_i.reset(new ScaledOperator(pc_r.get(), s));
OperatorJacobiSmoother *d00 = new OperatorJacobiSmoother(prec, ess_tdof_list);
ScaledOperator *d11 = new ScaledOperator(d00, s);
pc_r = d00;
pc_i = d11;
}
else
{
@@ -539,13 +541,15 @@ int main(int argc, char *argv[])
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
// Hypre AMS
pc_r.reset(new HypreAMS(*PCOpAh.As<HypreParMatrix>(), fespace));
pc_i.reset(new ScaledOperator(pc_r.get(), s));
HypreAMS *ams00 = new HypreAMS(*PCOpAh.As<HypreParMatrix>(), fespace);
ScaledOperator *ams11 = new ScaledOperator(ams00, s);
pc_r = ams00;
pc_i = ams11;
}
BlockDiagonalPreconditioner BlockDP(offsets);
BlockDP.SetDiagonalBlock(0, pc_r.get());
BlockDP.SetDiagonalBlock(1, pc_i.get());
BlockDP.SetDiagonalBlock(0, pc_r);
BlockDP.SetDiagonalBlock(1, pc_i);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPrintLevel(1);
@@ -880,7 +884,7 @@ void E_bdr_data_Im(const Vector &x, Vector &E)
}
}
void detJ_JT_J_inv_Re(const Vector &x, PML * pml, Vector & D)
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector & D)
{
vector<complex<double>> dxs(dim);
complex<double> det(1.0, 0.0);
@@ -897,7 +901,7 @@ void detJ_JT_J_inv_Re(const Vector &x, PML * pml, Vector & D)
}
}
void detJ_JT_J_inv_Im(const Vector &x, PML * pml, Vector & D)
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector & D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -914,7 +918,7 @@ void detJ_JT_J_inv_Im(const Vector &x, PML * pml, Vector & D)
}
}
void detJ_JT_J_inv_abs(const Vector &x, PML * pml, Vector & D)
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector & D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -931,7 +935,7 @@ void detJ_JT_J_inv_abs(const Vector &x, PML * pml, Vector & D)
}
}
void detJ_inv_JT_J_Re(const Vector &x, PML * pml, Vector & D)
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector & D)
{
vector<complex<double>> dxs(dim);
complex<double> det(1.0, 0.0);
@@ -956,7 +960,7 @@ void detJ_inv_JT_J_Re(const Vector &x, PML * pml, Vector & D)
}
}
void detJ_inv_JT_J_Im(const Vector &x, PML * pml, Vector & D)
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector & D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -980,7 +984,7 @@ void detJ_inv_JT_J_Im(const Vector &x, PML * pml, Vector & D)
}
}
void detJ_inv_JT_J_abs(const Vector &x, PML * pml, Vector & D)
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector & D)
{
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
@@ -1004,14 +1008,14 @@ void detJ_inv_JT_J_abs(const Vector &x, PML * pml, Vector & D)
}
}
PML::PML(Mesh *mesh_, Array2D<double> length_)
CartesianPML::CartesianPML(Mesh *mesh_, Array2D<double> length_)
: mesh(mesh_), length(length_)
{
dim = mesh->Dimension();
SetBoundaries();
}
void PML::SetBoundaries()
void CartesianPML::SetBoundaries()
{
comp_dom_bdr.SetSize(dim, 2);
dom_bdr.SetSize(dim, 2);
@@ -1026,7 +1030,7 @@ void PML::SetBoundaries()
}
}
void PML::SetAttributes(ParMesh *pmesh)
void CartesianPML::SetAttributes(ParMesh *pmesh)
{
// Initialize bdr attributes
for (int i = 0; i < pmesh->GetNBE(); ++i)
@@ -1076,8 +1080,8 @@ void PML::SetAttributes(ParMesh *pmesh)
pmesh->SetAttributes();
}
void PML::StretchFunction(const Vector &x,
vector<complex<double>> &dxs)
void CartesianPML::StretchFunction(const Vector &x,
vector<complex<double>> &dxs)
{
complex<double> zi = complex<double>(0., 1.);
-412
View File
@@ -1,412 +0,0 @@
// MFEM Example 34
//
// Compile with: make ex34
//
// Sample runs: ex34
//
// Description: This example code demonstrates the use of MFEM to define a
// discontinuous Galerkin (DG) finite element discretization of
// the Laplace problem -Delta u = f with Dirichlet boundary
// conditions. Finite element spaces of any order, including zero
// on regular grids, are supported. The example highlights the
// use of coupling solution domains though custom physics defined
// on internal boundaries.
//
// We recommend viewing examples 1 and 14 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class InteriorLFIntegrator : public LinearFormIntegrator
{
public:
InteriorLFIntegrator(Coefficient &Q)
: Q(Q)
{}
void AssembleRHSElementVect(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &trans,
Vector &mesh_coords_bar) override;
void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &trans,
Vector &elvect) override
{
mfem_error("AssembleRHSElementVect(...)");
}
private:
Coefficient &Q;
#ifndef MFEM_THREAD_SAFE
Vector shape1;
Vector shape2;
#endif
};
Mesh generate_mesh(int ref, int internal_bdr_attr = 5);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int ref_levels = 0;
int order = 1;
int sol_order = 3;
double jump = -2;
double sigma = -1.0;
double kappa = -1.0;
double eta = 0.0;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly, -1 for auto.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) >= 0.");
args.AddOption(&sigma, "-s", "--sigma",
"One of the three DG penalty parameters, typically +1/-1."
" See the documentation of class DGDiffusionIntegrator.");
args.AddOption(&kappa, "-k", "--kappa",
"One of the three DG penalty parameters, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
args.AddOption(&sol_order, "-so", "--solution_order",
"Polynomial order of the exact solution >= 0.");
args.AddOption(&jump, "-j", "--jump",
"Value of the discontinuity between the material regions.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
if (kappa < 0)
{
kappa = (order+1)*(order+1);
}
if (sol_order < 0)
{
sol_order = 1;
}
args.PrintOptions(cout);
// 2. Construct the (serial) mesh and refine it if requested.
auto mesh = generate_mesh(ref_levels);
int dim = mesh.Dimension();
if (mesh.NURBSext)
{
mesh.SetCurvature(max(order, 1));
}
// 3. Define a finite element space on the mesh. Here we use discontinuous
// finite elements of the specified order >= 0.
DG_FECollection fec(order, dim);
FiniteElementSpace fespace(&mesh, &fec);
cout << "Number of unknowns: " << fespace.GetVSize() << endl;
// 4. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system.
LinearForm b(&fespace);
Array<int> p1_attr_marker(mesh.attributes.Max());
p1_attr_marker = 0;
p1_attr_marker[0] = 1;
FunctionCoefficient p1_source([sol_order](const Vector &p)
{
const double x = p(0);
const double val = -(sol_order - 1)*sol_order*pow(x, sol_order-2);
return val;
});
b.AddDomainIntegrator(new DomainLFIntegrator(p1_source), p1_attr_marker);
Array<int> p2_attr_marker(mesh.attributes.Max());
p2_attr_marker = 0;
p2_attr_marker[1] = 1;
FunctionCoefficient p2_source([sol_order](const Vector &p)
{
const double x = p(0);
double val = -(sol_order - 1)*sol_order*pow(x - 2, sol_order-2);
if (sol_order % 2 == 0)
{
val *= -1.0;
}
return val;
});
b.AddDomainIntegrator(new DomainLFIntegrator(p2_source), p2_attr_marker);
ConstantCoefficient one(1.0);
Array<int> p1_bdr_attr_marker(mesh.bdr_attributes.Max());
p1_bdr_attr_marker = 0;
p1_bdr_attr_marker[0] = 1;
ConstantCoefficient left_bc_val(0.0);
b.AddBdrFaceIntegrator(
new DGDirichletLFIntegrator(left_bc_val, one, sigma, kappa),
p1_bdr_attr_marker);
Array<int> p2_bdr_attr_marker(mesh.bdr_attributes.Max());
p2_bdr_attr_marker = 0;
p2_bdr_attr_marker[1] = 1;
ConstantCoefficient right_bc_val(2.0 + jump);
b.AddBdrFaceIntegrator(
new DGDirichletLFIntegrator(right_bc_val, one, sigma, kappa),
p2_bdr_attr_marker);
Array<int> internal_bdr_attr_marker(mesh.bdr_attributes.Max());
internal_bdr_attr_marker = 0;
internal_bdr_attr_marker[4] = 1;
ConstantCoefficient interface_flux(sol_order);
b.AddInternalBoundaryFaceIntegrator(
new InteriorLFIntegrator(interface_flux),
internal_bdr_attr_marker);
b.Assemble();
// 5. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero.
GridFunction x(&fespace);
x = 0.0;
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator and the interior and boundary DG face integrators.
// Note that boundary conditions are imposed weakly in the form, so there
// is no need for dof elimination. After assembly and finalizing we
// extract the corresponding sparse matrix A.
BilinearForm a(&fespace);
a.AddDomainIntegrator(new DiffusionIntegrator(one));
a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa),
p1_bdr_attr_marker);
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa),
p2_bdr_attr_marker);
if (eta > 0)
{
a.AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a.AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
}
// 7. Negate the DG interface terms along the internal boundary so that the
// only coupling between domains is from the chosen model (constant flux
// in this case).
ProductCoefficient neg_one(-1.0, one);
a.AddInternalBoundaryFaceIntegrator(new DGDiffusionIntegrator(neg_one, sigma,
kappa),
internal_bdr_attr_marker);
if (eta > 0)
{
a.AddInternalBoundaryFaceIntegrator(new DGDiffusionBR2Integrator(fespace,
neg_one, eta),
internal_bdr_attr_marker);
}
a.Assemble();
a.Finalize();
const SparseMatrix &A = a.SpMat();
#ifndef MFEM_USE_SUITESPARSE
// 8. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system Ax=b with PCG in the symmetric case, and GMRES in the
// non-symmetric one.
GSSmoother M(A);
if (sigma == -1.0)
{
PCG(A, M, b, x, 1, 500, 1e-12, 0.0);
}
else
{
GMRES(A, M, b, x, 1, 500, 500, 1e-24, 0.0);
}
#else
// 8. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(b, x);
#endif
// 9. Save the refined mesh and the solution. This output can be viewed later
// using GLVis: "glvis -m refined.mesh -g sol.gf".
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh.Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
x.Save(sol_ofs);
// 10. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << mesh << x << flush;
}
return 0;
}
void InteriorLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &trans,
Vector &elvect)
{
int ndof1 = el1.GetDof();
int ndof2 = el2.GetDof();
int ndof = ndof1 + ndof2;
#ifdef MFEM_THREAD_SAFE
Vector shape1;
Vector shape2;
#endif
shape1.SetSize(ndof1);
shape2.SetSize(ndof2);
const auto *ir = IntRule;
if (ir == NULL)
{
int order = 2 * max(el1.GetOrder(), el2.GetOrder());
ir = &IntRules.Get(trans.GetGeometryType(), order);
}
elvect.SetSize(ndof);
Vector elvect1(elvect.GetData(), ndof1);
Vector elvect2(elvect.GetData() + ndof1, ndof2);
elvect = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const auto &ip = ir->IntPoint(i);
// Set the integration point in the face and the neighboring element
trans.SetAllIntPoints(&ip);
const double w = ip.weight * trans.Weight();
// Access the neighboring element's integration point
const auto &eip1 = trans.GetElement1IntPoint();
const auto &eip2 = trans.GetElement2IntPoint();
double Q_val = Q.Eval(trans, ip);
el1.CalcShape(eip1, shape1);
el2.CalcShape(eip2, shape2);
elvect1.Add(Q_val * w, shape1);
elvect2.Add(-Q_val * w, shape2);
}
}
Mesh generate_mesh(int ref, int internal_bdr_attr)
{
int nxy = 4 * (ref+1);
auto mesh = Mesh::MakeCartesian2D(nxy, nxy, Element::TRIANGLE, true, 2.0, 1.0);
// auto mesh = Mesh::MakeCartesian2D(nxy, nxy, Element::QUADRILATERAL, true, 2.0, 1.0);
// assign element attributes to left and right sides
for (int i = 0; i < mesh.GetNE(); ++i)
{
auto *elem = mesh.GetElement(i);
Array<int> verts;
elem->GetVertices(verts);
bool left = true;
for (int j = 0; j < verts.Size(); ++j)
{
auto *vtx = mesh.GetVertex(verts[j]);
if (vtx[0] <= 1.0)
{
continue;
}
else
{
left = false;
}
}
if (left)
{
elem->SetAttribute(1);
}
else
{
elem->SetAttribute(2);
}
}
// assign boundary element attributes to left and right sides
for (int i = 0; i < mesh.GetNBE(); ++i)
{
auto *elem = mesh.GetBdrElement(i);
Array<int> verts;
elem->GetVertices(verts);
bool left = true;
bool right = true;
bool top = true;
bool bottom = true;
for (int j = 0; j < verts.Size(); ++j)
{
auto *vtx = mesh.GetVertex(verts[j]);
left = left && abs(vtx[0] - 0.0) < 1e-12;
right = right && abs(vtx[0] - 2.0) < 1e-12;
top = top && abs(vtx[1] - 1.0) < 1e-12;
bottom = bottom && abs(vtx[1] - 0.0) < 1e-12;
}
if (left)
{
elem->SetAttribute(1);
}
else if (right)
{
elem->SetAttribute(2);
}
else if (top)
{
elem->SetAttribute(3);
}
else if (bottom)
{
elem->SetAttribute(4);
}
}
// add internal boundary elements
for (int i = 0; i < mesh.GetNumFaces(); ++i)
{
int e1, e2;
mesh.GetFaceElements(i, &e1, &e2);
if (e1 >= 0 && e2 >= 0 && mesh.GetAttribute(e1) != mesh.GetAttribute(e2))
{
// This is the internal face between attributes.
auto *new_elem = mesh.GetFace(i)->Duplicate(&mesh);
new_elem->SetAttribute(internal_bdr_attr);
mesh.AddBdrElement(new_elem);
}
}
mesh.FinalizeTopology(); // Finalize to build relevant tables
mesh.Finalize();
mesh.SetAttributes();
return mesh;
}
-451
View File
@@ -1,451 +0,0 @@
// MFEM Example 36
//
// Compile with: make ex36
//
// Sample runs: ex36
//
// Description: This example code demonstrates the use of MFEM to define a
// discontinuous Galerkin (DG) finite element discretization of
// the Laplace problem -Delta u = f with Dirichlet boundary
// conditions. Finite element spaces of any order, including zero
// on regular grids, are supported. The example highlights the
// use of coupling solution domains though custom physics defined
// on internal boundaries.
//
// We recommend viewing examples 1, 14, and 34 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class InteriorMassIntegrator : public BilinearFormIntegrator
{
public:
InteriorMassIntegrator(Coefficient &Q)
: Q(Q)
{}
void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &trans,
DenseMatrix &elmat) override;
using BilinearFormIntegrator::AssembleFaceMatrix;
private:
Coefficient &Q;
#ifndef MFEM_THREAD_SAFE
Vector shape1;
Vector shape2;
DenseMatrix elmat11;
DenseMatrix elmat12;
DenseMatrix elmat21;
DenseMatrix elmat22;
#endif
};
Mesh generate_mesh(int ref, int internal_bdr_attr = 5);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int ref_levels = 0;
int order = 1;
int sol_order = 3;
double jump = -2;
double sigma = -1.0;
double kappa = -1.0;
double eta = 0.0;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly, -1 for auto.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) >= 0.");
args.AddOption(&sigma, "-s", "--sigma",
"One of the three DG penalty parameters, typically +1/-1."
" See the documentation of class DGDiffusionIntegrator.");
args.AddOption(&kappa, "-k", "--kappa",
"One of the three DG penalty parameters, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
args.AddOption(&sol_order, "-so", "--solution_order",
"Polynomial order of the exact solution >= 0.");
args.AddOption(&jump, "-j", "--jump",
"Value of the discontinuity between the material regions.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
if (kappa < 0)
{
kappa = (order+1)*(order+1);
}
if (sol_order < 0)
{
sol_order = 1;
}
args.PrintOptions(cout);
// 2. Construct the (serial) mesh and refine it if requested.
auto mesh = generate_mesh(ref_levels);
int dim = mesh.Dimension();
if (mesh.NURBSext)
{
mesh.SetCurvature(max(order, 1));
}
// 3. Define a finite element space on the mesh. Here we use discontinuous
// finite elements of the specified order >= 0.
DG_FECollection fec(order, dim);
FiniteElementSpace fespace(&mesh, &fec);
cout << "Number of unknowns: " << fespace.GetVSize() << endl;
// 4. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system.
LinearForm b(&fespace);
Array<int> p1_attr_marker(mesh.attributes.Max());
p1_attr_marker = 0;
p1_attr_marker[0] = 1;
FunctionCoefficient p1_source([sol_order](const Vector &p)
{
const double x = p(0);
const double val = -(sol_order - 1)*sol_order*pow(x, sol_order-2);
return val;
});
b.AddDomainIntegrator(new DomainLFIntegrator(p1_source), p1_attr_marker);
Array<int> p2_attr_marker(mesh.attributes.Max());
p2_attr_marker = 0;
p2_attr_marker[1] = 1;
FunctionCoefficient p2_source([sol_order](const Vector &p)
{
const double x = p(0);
double val = -(sol_order - 1)*sol_order*pow(x - 2, sol_order-2);
if (sol_order % 2 == 0)
{
val *= -1.0;
}
return val;
});
b.AddDomainIntegrator(new DomainLFIntegrator(p2_source), p2_attr_marker);
ConstantCoefficient one(1.0);
Array<int> p1_bdr_attr_marker(mesh.bdr_attributes.Max());
p1_bdr_attr_marker = 0;
p1_bdr_attr_marker[0] = 1;
ConstantCoefficient left_bc_val(0.0);
b.AddBdrFaceIntegrator(
new DGDirichletLFIntegrator(left_bc_val, one, sigma, kappa),
p1_bdr_attr_marker);
Array<int> p2_bdr_attr_marker(mesh.bdr_attributes.Max());
p2_bdr_attr_marker = 0;
p2_bdr_attr_marker[1] = 1;
ConstantCoefficient right_bc_val(2.0 + jump);
b.AddBdrFaceIntegrator(
new DGDirichletLFIntegrator(right_bc_val, one, sigma, kappa),
p2_bdr_attr_marker);
b.Assemble();
// 5. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero.
GridFunction x(&fespace);
x = 0.0;
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator and the interior and boundary DG face integrators.
// Note that boundary conditions are imposed weakly in the form, so there
// is no need for dof elimination. After assembly and finalizing we
// extract the corresponding sparse matrix A.
BilinearForm a(&fespace);
a.AddDomainIntegrator(new DiffusionIntegrator(one));
a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa),
p1_bdr_attr_marker);
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa),
p2_bdr_attr_marker);
if (eta > 0)
{
a.AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a.AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
}
// 7. Negate the DG interface terms along the internal boundary so that the
// only coupling between domains is from the chosen model (constant flux
// in this case).
Array<int> internal_bdr_attr_marker(mesh.bdr_attributes.Max());
internal_bdr_attr_marker = 0;
internal_bdr_attr_marker[4] = 1;
ProductCoefficient neg_one(-1.0, one);
a.AddInternalBoundaryFaceIntegrator(new DGDiffusionIntegrator(neg_one, sigma,
kappa),
internal_bdr_attr_marker);
if (eta > 0)
{
a.AddInternalBoundaryFaceIntegrator(new DGDiffusionBR2Integrator(fespace,
neg_one, eta),
internal_bdr_attr_marker);
}
ConstantCoefficient mass_coeff(sol_order / jump);
a.AddInternalBoundaryFaceIntegrator(new InteriorMassIntegrator(mass_coeff),
internal_bdr_attr_marker);
a.Assemble();
a.Finalize();
const SparseMatrix &A = a.SpMat();
#ifndef MFEM_USE_SUITESPARSE
// 8. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system Ax=b with PCG in the symmetric case, and GMRES in the
// non-symmetric one.
GSSmoother M(A);
if (sigma == -1.0 && !(jump < 0))
{
PCG(A, M, b, x, 1, 500, 1e-12, 0.0);
}
else
{
GMRES(A, M, b, x, 1, 500, 500, 1e-24, 0.0);
}
#else
// 8. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(b, x);
#endif
// 9. Save the refined mesh and the solution. This output can be viewed later
// using GLVis: "glvis -m refined.mesh -g sol.gf".
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh.Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
x.Save(sol_ofs);
// 10. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << mesh << x << flush;
}
return 0;
}
void InteriorMassIntegrator::AssembleFaceMatrix(
const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &trans,
DenseMatrix &elmat)
{
int ndof1 = el1.GetDof();
int ndof2 = el2.GetDof();
int ndof = ndof1 + ndof2;
#ifdef MFEM_THREAD_SAFE
Vector shape1;
Vector shape2;
DenseMatrix elmat11;
DenseMatrix elmat12;
DenseMatrix elmat21;
DenseMatrix elmat22;
#endif
shape1.SetSize(ndof1);
shape2.SetSize(ndof2);
elmat11.SetSize(ndof1);
elmat12.SetSize(ndof1, ndof2);
elmat21.SetSize(ndof2, ndof1);
elmat22.SetSize(ndof2);
const auto *ir = IntRule;
if (ir == NULL)
{
int order = 2 * max(el1.GetOrder(), el2.GetOrder());
ir = &IntRules.Get(trans.GetGeometryType(), order);
}
elmat.SetSize(ndof);
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const auto &ip = ir->IntPoint(i);
// Set the integration point in the face and the neighboring element
trans.SetAllIntPoints(&ip);
const double w = ip.weight * trans.Weight();
// Access the neighboring element's integration point
const auto &eip1 = trans.GetElement1IntPoint();
const auto &eip2 = trans.GetElement2IntPoint();
el1.CalcShape(eip1, shape1);
el2.CalcShape(eip2, shape2);
const double Q_val = Q.Eval(trans, ip);
elmat11 = 0.0;
AddMult_a_VVt(Q_val * w, shape1, elmat11);
elmat12 = 0.0;
AddMult_a_VWt(-Q_val * w, shape2, shape1, elmat12);
elmat21 = 0.0;
AddMult_a_VWt(-Q_val * w, shape1, shape2, elmat21);
elmat22 = 0.0;
AddMult_a_VVt(Q_val * w, shape2, elmat22);
for (int j = 0; j < ndof1; ++j)
{
for (int k = 0; k < ndof1; ++k)
{
elmat(j, k) += elmat11(j, k);
}
}
for (int j = 0; j < ndof1; ++j)
{
for (int k = 0; k < ndof2; ++k)
{
elmat(j, k + ndof1) += elmat12(j, k);
elmat(k + ndof1, j) += elmat21(k, j);
}
}
for (int j = 0; j < ndof2; ++j)
{
for (int k = 0; k < ndof2; ++k)
{
elmat(j + ndof1, k + ndof1) += elmat22(j, k);
}
}
}
}
Mesh generate_mesh(int ref, int internal_bdr_attr)
{
int nxy = 4 * (ref+1);
auto mesh = Mesh::MakeCartesian2D(nxy, nxy, Element::TRIANGLE, true, 2.0, 1.0);
// auto mesh = Mesh::MakeCartesian2D(nxy, nxy, Element::QUADRILATERAL, true, 2.0, 1.0);
// assign element attributes to left and right sides
for (int i = 0; i < mesh.GetNE(); ++i)
{
auto *elem = mesh.GetElement(i);
Array<int> verts;
elem->GetVertices(verts);
bool left = true;
for (int j = 0; j < verts.Size(); ++j)
{
auto *vtx = mesh.GetVertex(verts[j]);
if (vtx[0] <= 1.0)
{
continue;
}
else
{
left = false;
}
}
if (left)
{
elem->SetAttribute(1);
}
else
{
elem->SetAttribute(2);
}
}
// assign boundary element attributes to left and right sides
for (int i = 0; i < mesh.GetNBE(); ++i)
{
auto *elem = mesh.GetBdrElement(i);
Array<int> verts;
elem->GetVertices(verts);
bool left = true;
bool right = true;
bool top = true;
bool bottom = true;
for (int j = 0; j < verts.Size(); ++j)
{
auto *vtx = mesh.GetVertex(verts[j]);
left = left && abs(vtx[0] - 0.0) < 1e-12;
right = right && abs(vtx[0] - 2.0) < 1e-12;
top = top && abs(vtx[1] - 1.0) < 1e-12;
bottom = bottom && abs(vtx[1] - 0.0) < 1e-12;
}
if (left)
{
elem->SetAttribute(1);
}
else if (right)
{
elem->SetAttribute(2);
}
else if (top)
{
elem->SetAttribute(3);
}
else if (bottom)
{
elem->SetAttribute(4);
}
}
// add internal boundary elements
for (int i = 0; i < mesh.GetNumFaces(); ++i)
{
int e1, e2;
mesh.GetFaceElements(i, &e1, &e2);
if (e1 >= 0 && e2 >= 0 && mesh.GetAttribute(e1) != mesh.GetAttribute(e2))
{
// This is the internal face between attributes.
auto *new_elem = mesh.GetFace(i)->Duplicate(&mesh);
new_elem->SetAttribute(internal_bdr_attr);
mesh.AddBdrElement(new_elem);
}
}
mesh.FinalizeTopology(); // Finalize to build relevant tables
mesh.Finalize();
mesh.SetAttributes();
return mesh;
}
+2 -2
View File
@@ -23,7 +23,7 @@ MFEM_LIB_FILE = mfem_is_not_built
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
ex31 ex33 ex34 ex35
ex31 ex33
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p
@@ -36,7 +36,7 @@ ifeq ($(MFEM_USE_MPI),NO)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
SUBDIRS =
SUBDIRS = contact
ifeq ($(MFEM_USE_AMGX),YES)
SUBDIRS += amgx
endif
+4 -6
View File
@@ -67,7 +67,6 @@ int main(int argc, char *argv[])
int slu_colperm = 4;
int slu_rowperm = 1;
int slu_iterref = 2;
int slu_npdep = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -86,11 +85,9 @@ int main(int argc, char *argv[])
"6-ZOLTAN");
args.AddOption(&slu_rowperm, "-rp", "--rowperm",
"SuperLU Row Permutation Method: 0-NOROWPERM, 1-LargeDiag");
args.AddOption(&slu_iterref, "-ir", "--iterref",
args.AddOption(&slu_iterref, "-rp", "--rowperm",
"SuperLU Iterative Refinement: 0-NOREFINE, 1-Single, "
"2-Double, 3-Extra");
args.AddOption(&slu_npdep, "-npdep", "--npdepth",
"Depth of 3D parition for SuperLU (>= 7.2.0)");
args.Parse();
if (!args.Good())
@@ -217,7 +214,7 @@ int main(int argc, char *argv[])
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
// 13. Solve the linear system A X = B utilizing SuperLU.
SuperLUSolver *superlu = new SuperLUSolver(MPI_COMM_WORLD, slu_npdep);
SuperLUSolver *superlu = new SuperLUSolver(MPI_COMM_WORLD);
Operator *SLU_A = new SuperLURowLocMatrix(*A.As<HypreParMatrix>());
superlu->SetPrintStatistics(true);
superlu->SetSymmetricPattern(false);
@@ -284,9 +281,10 @@ int main(int argc, char *argv[])
superlu->SetOperator(*SLU_A);
superlu->SetPrintStatistics(true);
superlu->Mult(B, X);
superlu->DismantleGrid();
delete superlu;
delete SLU_A;
delete superlu;
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
+30 -43
View File
@@ -13,44 +13,28 @@ set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
integ/bilininteg_br2.cpp
integ/bilininteg_convection_mf.cpp
integ/bilininteg_convection_pa.cpp
integ/bilininteg_convection_ea.cpp
integ/bilininteg_curlcurl_pa.cpp
integ/bilininteg_dgtrace_pa.cpp
integ/bilininteg_dgtrace_ea.cpp
integ/bilininteg_diffusion_mf.cpp
integ/bilininteg_diffusion_pa.cpp
integ/bilininteg_diffusion_ea.cpp
integ/bilininteg_divdiv_pa.cpp
integ/bilininteg_gradient_pa.cpp
integ/bilininteg_interp_pa.cpp
integ/bilininteg_mass_mf.cpp
integ/bilininteg_mass_pa.cpp
integ/bilininteg_mass_ea.cpp
integ/bilininteg_mixedcurl_pa.cpp
integ/bilininteg_mixedvecgrad_pa.cpp
integ/bilininteg_transpose_ea.cpp
integ/bilininteg_vecdiffusion_mf.cpp
integ/bilininteg_vecdiffusion_pa.cpp
integ/bilininteg_vecdiv_pa.cpp
integ/bilininteg_vecmass_mf.cpp
integ/bilininteg_vecmass_pa.cpp
integ/bilininteg_vectorfediv_pa.cpp
integ/bilininteg_vectorfemass_pa.cpp
integ/bilininteg_diffusion_kernels.cpp
integ/bilininteg_hcurl_kernels.cpp
integ/bilininteg_hdiv_kernels.cpp
integ/bilininteg_hcurlhdiv_kernels.cpp
integ/bilininteg_mass_kernels.cpp
integ/lininteg_boundary.cpp
integ/lininteg_boundary_flux.cpp
integ/lininteg_domain.cpp
integ/lininteg_domain_grad.cpp
integ/lininteg_domain_vectorfe.cpp
integ/nonlininteg_vecconvection_pa.cpp
integ/nonlininteg_vecconvection_mf.cpp
bilininteg_br2.cpp
bilininteg_convection_mf.cpp
bilininteg_convection_pa.cpp
bilininteg_convection_ea.cpp
bilininteg_dgtrace_pa.cpp
bilininteg_dgtrace_ea.cpp
bilininteg_diffusion_mf.cpp
bilininteg_diffusion_pa.cpp
bilininteg_diffusion_ea.cpp
bilininteg_divergence.cpp
bilininteg_hcurl.cpp
bilininteg_hdiv.cpp
bilininteg_vectorfe.cpp
bilininteg_gradient.cpp
bilininteg_mass_mf.cpp
bilininteg_mass_pa.cpp
bilininteg_mass_ea.cpp
bilininteg_transpose_ea.cpp
bilininteg_vecdiffusion.cpp
bilininteg_vecdiffusion_mf.cpp
bilininteg_vecmass.cpp
bilininteg_vecmass_mf.cpp
coefficient.cpp
complex_fem.cpp
convergence.cpp
@@ -90,6 +74,11 @@ set(SRCS
linearform.cpp
linearform_ext.cpp
lininteg.cpp
lininteg_boundary.cpp
lininteg_boundary_flux.cpp
lininteg_domain.cpp
lininteg_domain_grad.cpp
lininteg_vectorfe_domain.cpp
lor/lor.cpp
lor/lor_ads.cpp
lor/lor_ams.cpp
@@ -102,6 +91,8 @@ set(SRCS
nonlinearform_ext.cpp
nonlininteg.cpp
fespacehierarchy.cpp
nonlininteg_vectorconvection.cpp
nonlininteg_vectorconvection_mf.cpp
qfunction.cpp
qinterp/det.cpp
qinterp/eval_by_nodes.cpp
@@ -152,11 +143,7 @@ set(HDRS
bilinearform.hpp
bilinearform_ext.hpp
bilininteg.hpp
integ/bilininteg_diffusion_kernels.hpp
integ/bilininteg_hcurl_kernels.hpp
integ/bilininteg_hdiv_kernels.hpp
integ/bilininteg_hcurlhdiv_kernels.hpp
integ/bilininteg_mass_kernels.hpp
bilininteg_mass_pa.hpp
coefficient.hpp
complex_fem.hpp
convergence.hpp
-77
View File
@@ -14,7 +14,6 @@
#include "fem.hpp"
#include "../general/device.hpp"
#include <cmath>
#include <cstddef>
namespace mfem
{
@@ -110,9 +109,6 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
boundary_face_integs = bf->boundary_face_integs;
boundary_face_integs_marker = bf->boundary_face_integs_marker;
internal_boundary_face_integs = bf->internal_boundary_face_integs;
internal_boundary_face_integs_marker = bf->internal_boundary_face_integs_marker;
AllocMat();
}
@@ -282,22 +278,6 @@ void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
boundary_face_integs_marker.Append(&bdr_marker);
}
void BilinearForm::AddInternalBoundaryFaceIntegrator(BilinearFormIntegrator
*bfi)
{
internal_boundary_face_integs.Append(bfi);
// nullptr -> all attributes are active
internal_boundary_face_integs_marker.Append(nullptr);
}
void BilinearForm::AddInternalBoundaryFaceIntegrator(BilinearFormIntegrator
*bfi,
Array<int> &internal_bdr_attr_marker)
{
internal_boundary_face_integs.Append(bfi);
internal_boundary_face_integs_marker.Append(&internal_bdr_attr_marker);
}
void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
{
if (element_matrices)
@@ -650,59 +630,6 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
if (internal_boundary_face_integs.Size())
{
// Which internal boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < internal_boundary_face_integs.Size(); k++)
{
if (internal_boundary_face_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
auto &bdr_marker = *internal_boundary_face_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for internal boundary face "
"integrator #" << k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
Array<int> vdofs2;
for (int i = 0; i < mesh->GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
auto *tr = mesh->GetInternalBdrFaceTransformations(i);
if (tr != nullptr)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
fes->GetElementVDofs(tr->Elem2No, vdofs2);
vdofs.Append(vdofs2);
const auto *fe1 = fes->GetFE(tr->Elem1No);
const auto *fe2 = fes->GetFE(tr->Elem2No);
for (int k = 0; k < internal_boundary_face_integs.Size(); k++)
{
if (internal_boundary_face_integs_marker[k] &&
(*internal_boundary_face_integs_marker[k])[bdr_attr - 1] == 0)
{
continue;
}
internal_boundary_face_integs[k]->AssembleFaceMatrix(
*fe1, *fe2, *tr, elemmat);
mat->AddSubMatrix(vdofs, vdofs, elemmat, skip_zeros);
}
}
}
}
#ifdef MFEM_USE_LEGACY_OPENMP
if (free_element_matrices)
{
@@ -1216,10 +1143,6 @@ BilinearForm::~BilinearForm()
{ delete interior_face_integs[k]; }
for (k=0; k < boundary_face_integs.Size(); k++)
{ delete boundary_face_integs[k]; }
for (int i = 0; i < internal_boundary_face_integs.Size(); i++)
{
delete internal_boundary_face_integs[i];
}
}
delete ext;
-16
View File
@@ -113,10 +113,6 @@ protected:
Array<BilinearFormIntegrator*> boundary_face_integs;
Array<Array<int>*> boundary_face_integs_marker; ///< Entries are not owned.
/// Set of internal boundary face integrators to be applied.
Array<BilinearFormIntegrator*> internal_boundary_face_integs;
Array<Array<int>*> internal_boundary_face_integs_marker; ///< Entries not owned.
DenseMatrix elemmat;
Array<int> vdofs;
@@ -420,18 +416,6 @@ public:
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker);
/// @brief Add new internal boundary face integrator. Assumes ownership of
/// @a bfi.
void AddInternalBoundaryFaceIntegrator(BilinearFormIntegrator *bfi);
/** @brief Add new internal boundary face integrator, restricted to the given
boundary attributes.
Assumes ownership of @a bfi. The array @a internal_bdr_attr_marker is
stored internally as a pointer to the given Array<int> object. */
void AddInternalBoundaryFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &internal_bdr_attr_marker);
/// Sets all sparse values of \f$ M \f$ and \f$ M_e \f$ to 'a'.
void operator=(const double a)
{
+48 -86
View File
@@ -56,9 +56,6 @@ void MFBilinearFormExtension::Assemble()
{
integrators[i]->AssembleMF(*a->FESpace());
}
MFEM_VERIFY(a->GetBBFI()->Size() == 0, "AddBoundaryIntegrator is not "
"currently supported in MFBilinearFormExtension");
}
void MFBilinearFormExtension::AssembleDiagonal(Vector &y) const
@@ -278,9 +275,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
int_face_Y.UseDevice(true); // ensure 'int_face_Y = 0.0' is done on device
}
const bool has_bdr_integs = (a->GetBFBFI()->Size() > 0 ||
a->GetBBFI()->Size() > 0);
if (bdr_face_restrict_lex == NULL && has_bdr_integs)
if (bdr_face_restrict_lex == NULL && a->GetBFBFI()->Size() > 0)
{
bdr_face_restrict_lex = trial_fes->GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
@@ -297,27 +292,27 @@ void PABilinearFormExtension::Assemble()
SetupRestrictionOperators(L2FaceValues::DoubleValued);
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
for (BilinearFormIntegrator *integ : integrators)
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integ->AssemblePA(*a->FESpace());
integrators[i]->AssemblePA(*a->FESpace());
}
Array<BilinearFormIntegrator*> &bdr_integrators = *a->GetBBFI();
for (BilinearFormIntegrator *integ : bdr_integrators)
{
integ->AssemblePABoundary(*a->FESpace());
}
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
"Partial assembly does not support AddBoundaryIntegrator yet.");
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
for (BilinearFormIntegrator *integ : intFaceIntegrators)
const int intFaceIntegratorCount = intFaceIntegrators.Size();
for (int i = 0; i < intFaceIntegratorCount; ++i)
{
integ->AssemblePAInteriorFaces(*a->FESpace());
intFaceIntegrators[i]->AssemblePAInteriorFaces(*a->FESpace());
}
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
for (BilinearFormIntegrator *integ : bdrFaceIntegrators)
const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
for (int i = 0; i < boundFaceIntegratorCount; ++i)
{
integ->AssemblePABoundaryFaces(*a->FESpace());
bdrFaceIntegrators[i]->AssemblePABoundaryFaces(*a->FESpace());
}
}
@@ -328,27 +323,20 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
const int iSz = integrators.Size();
if (elem_restrict && !DeviceCanUseCeed())
{
if (iSz > 0)
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AssembleDiagonalPA(localY);
}
const ElementRestriction* H1elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict);
if (H1elem_restrict)
{
H1elem_restrict->MultTransposeUnsigned(localY, y);
}
else
{
elem_restrict->MultTranspose(localY, y);
}
integrators[i]->AssembleDiagonalPA(localY);
}
const ElementRestriction* H1elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict);
if (H1elem_restrict)
{
H1elem_restrict->MultTransposeUnsigned(localY, y);
}
else
{
y = 0.0;
elem_restrict->MultTranspose(localY, y);
}
}
else
@@ -360,18 +348,6 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
integrators[i]->AssembleDiagonalPA(y);
}
}
Array<BilinearFormIntegrator*> &bdr_integs = *a->GetBBFI();
const int n_bdr_integs = bdr_integs.Size();
if (bdr_face_restrict_lex && n_bdr_integs > 0)
{
bdr_face_Y = 0.0;
for (int i = 0; i < n_bdr_integs; ++i)
{
bdr_integs[i]->AssembleDiagonalPA(bdr_face_Y);
}
bdr_face_restrict_lex->AddMultTransposeUnsigned(bdr_face_Y, y);
}
}
void PABilinearFormExtension::Update()
@@ -421,20 +397,13 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
}
else
{
if (iSz)
elem_restrict->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
elem_restrict->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(localX, localY);
}
elem_restrict->MultTranspose(localY, y);
}
else
{
y = 0.0;
integrators[i]->AddMultPA(localX, localY);
}
elem_restrict->MultTranspose(localY, y);
}
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
@@ -453,24 +422,17 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
}
}
Array<BilinearFormIntegrator*> &bdr_integs = *a->GetBBFI();
Array<BilinearFormIntegrator*> &bdr_face_integs = *a->GetBFBFI();
const int n_bdr_integs = bdr_integs.Size();
const int n_bdr_face_integs = bdr_face_integs.Size();
const bool has_bdr_integs = (n_bdr_face_integs > 0 || n_bdr_integs > 0);
if (bdr_face_restrict_lex && has_bdr_integs)
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (bdr_face_restrict_lex && bFISz>0)
{
bdr_face_restrict_lex->Mult(x, bdr_face_X);
if (bdr_face_X.Size()>0)
{
bdr_face_Y = 0.0;
for (int i = 0; i < n_bdr_integs; ++i)
for (int i = 0; i < bFISz; ++i)
{
bdr_integs[i]->AddMultPA(bdr_face_X, bdr_face_Y);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
bdr_face_integs[i]->AddMultPA(bdr_face_X, bdr_face_Y);
bdrFaceIntegrators[i]->AddMultPA(bdr_face_X, bdr_face_Y);
}
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
@@ -634,7 +596,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, ne*NDOFS,
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -669,7 +631,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
if (!factorize_face_terms)
{
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -688,7 +650,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
});
}
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -725,7 +687,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, nf_bdr*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -762,7 +724,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
mfem::forall(ne*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, ne*NDOFS,
{
const int e = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -797,7 +759,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
if (!factorize_face_terms)
{
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -816,7 +778,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
});
}
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
mfem::forall(nf_int*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, nf_int*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -853,7 +815,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
MFEM_FORALL(glob_j, nf_bdr*NDOFS,
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
@@ -1068,13 +1030,13 @@ void FABilinearFormExtension::DGMult(const Vector &x, Vector &y) const
const int local_size = a->FESpace()->GetVSize();
auto dg_x_ptr = dg_x.Write();
auto x_ptr = x.Read();
mfem::forall(local_size, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i,local_size,
{
dg_x_ptr[i] = x_ptr[i];
});
const int shared_size = shared_x.Size();
auto shared_x_ptr = shared_x.Read();
mfem::forall(shared_size, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i,shared_size,
{
dg_x_ptr[local_size+i] = shared_x_ptr[i];
});
@@ -1085,7 +1047,7 @@ void FABilinearFormExtension::DGMult(const Vector &x, Vector &y) const
// DG Restriction
auto dg_y_ptr = dg_y.Read();
auto y_ptr = y.ReadWrite();
mfem::forall(local_size, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i,local_size,
{
y_ptr[i] += dg_y_ptr[i];
});
@@ -1129,13 +1091,13 @@ void FABilinearFormExtension::DGMultTranspose(const Vector &x, Vector &y) const
const int local_size = a->FESpace()->GetVSize();
auto dg_x_ptr = dg_x.Write();
auto x_ptr = x.Read();
mfem::forall(local_size, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i,local_size,
{
dg_x_ptr[i] = x_ptr[i];
});
const int shared_size = shared_x.Size();
auto shared_x_ptr = shared_x.Read();
mfem::forall(shared_size, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i,shared_size,
{
dg_x_ptr[local_size+i] = shared_x_ptr[i];
});
@@ -1146,7 +1108,7 @@ void FABilinearFormExtension::DGMultTranspose(const Vector &x, Vector &y) const
// DG Restriction
auto dg_y_ptr = dg_y.Read();
auto y_ptr = y.ReadWrite();
mfem::forall(local_size, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i,local_size,
{
y_ptr[i] += dg_y_ptr[i];
});
@@ -1484,7 +1446,7 @@ void PADiscreteLinearOperatorExtension::Assemble()
}
auto tm = test_multiplicity.ReadWrite();
mfem::forall(test_multiplicity.Size(), [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, test_multiplicity.Size(),
{
tm[i] = 1.0 / tm[i];
});
@@ -1536,7 +1498,7 @@ void PADiscreteLinearOperatorExtension::AddMultTranspose(
MFEM_VERIFY(x.Size() == test_multiplicity.Size(), "Input vector of wrong size");
auto xs = xscaled.ReadWrite();
auto tm = test_multiplicity.Read();
mfem::forall(x.Size(), [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, x.Size(),
{
xs[i] *= tm[i];
});
+35 -261
View File
@@ -22,47 +22,41 @@ namespace mfem
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
{
MFEM_ABORT("BilinearFormIntegrator::AssemblePA(fes)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssemblePA(fes)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
const FiniteElementSpace&)
{
MFEM_ABORT("BilinearFormIntegrator::AssemblePA(fes, fes)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePABoundary(const FiniteElementSpace&)
{
MFEM_ABORT("BilinearFormIntegrator::AssemblePABoundary(fes)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssemblePA(fes, fes)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePAInteriorFaces(const FiniteElementSpace&)
{
MFEM_ABORT("BilinearFormIntegrator::AssemblePAInteriorFaces(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssemblePAInteriorFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePABoundaryFaces(const FiniteElementSpace&)
{
MFEM_ABORT("BilinearFormIntegrator::AssemblePABoundaryFaces(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssemblePABoundaryFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleDiagonalPA(Vector &)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
Vector &emat,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEA(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleEA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
@@ -71,8 +65,8 @@ void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
Vector &ea_data_ext,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
@@ -80,8 +74,8 @@ void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
Vector &ea_data_bdr,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEABoundaryFaces(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleEABoundaryFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
@@ -92,62 +86,62 @@ void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::MultAssembled(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::MultAssembled(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::AddMultTransposePA(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AddMultTransposePA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleMF(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultMF(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::AddMultMF(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AddMultMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultTransposeMF(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::AddMultTransposeMF(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AddMultTransposeMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleDiagonalMF(Vector &)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalMF(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleDiagonalMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleElementMatrix (
const FiniteElement &el, ElementTransformation &Trans,
DenseMatrix &elmat )
{
MFEM_ABORT("BilinearFormIntegrator::AssembleElementMatrix(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleElementMatrix(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleElementMatrix2 (
const FiniteElement &el1, const FiniteElement &el2,
ElementTransformation &Trans, DenseMatrix &elmat )
{
MFEM_ABORT("BilinearFormIntegrator::AssembleElementMatrix2(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleElementMatrix2(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix (
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleFaceMatrix(...)\n"
" is not implemented for this class.");
mfem_error ("BilinearFormIntegrator::AssembleFaceMatrix(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix(
@@ -159,16 +153,6 @@ void BilinearFormIntegrator::AssembleFaceMatrix(
" Integrator class.");
}
void BilinearFormIntegrator::AssembleTraceFaceMatrix (int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe1,
FaceElementTransformations &Trans,
DenseMatrix &elmat)
{
MFEM_ABORT("AssembleTraceFaceMatrix (DPG form) is not implemented for this"
" Integrator class.");
}
void BilinearFormIntegrator::AssembleElementVector(
const FiniteElement &el, ElementTransformation &Tr, const Vector &elfun,
Vector &elvect)
@@ -2649,7 +2633,7 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
}
else
{
MFEM_ABORT("VectorFEMassIntegrator::AssembleElementMatrix2(...)\n"
mfem_error("VectorFEMassIntegrator::AssembleElementMatrix2(...)\n"
" is not implemented for given trial and test bases.");
}
}
@@ -4013,216 +3997,6 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
}
}
void TraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations & Trans,
DenseMatrix &elmat)
{
MFEM_VERIFY(test_fe.GetMapType() == FiniteElement::VALUE,
"TraceIntegrator::AssembleTraceFaceMatrix: Test space should be H1");
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::INTEGRAL,
"TraceIntegrator::AssembleTraceFaceMatrix: Trial space should be RT trace");
int i, j, face_ndof, ndof;
int order;
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
face_shape.SetSize(face_ndof);
shape.SetSize(ndof);
elmat.SetSize(ndof, face_ndof);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
order = test_fe.GetOrder();
order += trial_face_fe.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
int iel = Trans.Elem1->ElementNo;
if (iel != elem)
{
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
}
double scale = 1.0;
if (iel != elem) { scale = -1.; }
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
// Set the integration point in the face and the neighboring elements
Trans.SetAllIntPoints(&ip);
// Trace finite element shape function
trial_face_fe.CalcPhysShape(Trans,face_shape);
// Finite element shape function
ElementTransformation * eltrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
test_fe.CalcPhysShape(*eltrans, shape);
face_shape *= Trans.Weight()*ip.weight*scale;
for (i = 0; i < ndof; i++)
{
for (j = 0; j < face_ndof; j++)
{
elmat(i, j) += shape(i) * face_shape(j);
}
}
}
}
void NormalTraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat)
{
int i, j, face_ndof, ndof, dim;
int order;
MFEM_VERIFY(test_fe.GetMapType() == FiniteElement::H_DIV,
"NormalTraceIntegrator::AssembleTraceFaceMatrix: Test space should be RT");
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::VALUE,
"NormalTraceIntegrator::AssembleTraceFaceMatrix: Trial space should be H1 (trace)");
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
dim = test_fe.GetDim();
face_shape.SetSize(face_ndof);
normal.SetSize(dim);
shape.SetSize(ndof,dim);
shape_n.SetSize(ndof);
elmat.SetSize(ndof, face_ndof);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
order = test_fe.GetOrder();
order += trial_face_fe.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
int iel = Trans.Elem1->ElementNo;
if (iel != elem)
{
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
}
double scale = 1.0;
if (iel != elem) { scale = -1.; }
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
Trans.SetAllIntPoints(&ip);
trial_face_fe.CalcPhysShape(Trans, face_shape);
CalcOrtho(Trans.Jacobian(),normal);
ElementTransformation * etrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
test_fe.CalcVShape(*etrans, shape);
shape.Mult(normal, shape_n);
face_shape *= ip.weight*scale;
for (i = 0; i < ndof; i++)
{
for (j = 0; j < face_ndof; j++)
{
elmat(i, j) += shape_n(i) * face_shape(j);
}
}
}
}
void TangentTraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations & Trans,
DenseMatrix &elmat)
{
MFEM_VERIFY(test_fe.GetMapType() == FiniteElement::H_CURL,
"TangentTraceIntegrator::AssembleTraceFaceMatrix: Test space should be ND");
int face_ndof, ndof, dim;
int order;
dim = test_fe.GetDim();
if (dim == 3)
{
std::string msg =
"Trial space should be ND face trace and test space should be a ND vector field in 3D ";
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::H_CURL &&
trial_face_fe.GetDim() == 2 && test_fe.GetDim() == 3, msg);
}
else
{
std::string msg =
"Trial space should be H1 edge trace and test space should be a ND vector field in 2D";
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::VALUE &&
trial_face_fe.GetDim() == 1 && test_fe.GetDim() == 2, msg);
}
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
int dimc = (dim == 3) ? 3 : 1;
face_shape.SetSize(face_ndof,dimc);
shape_n.SetSize(ndof,dimc);
shape.SetSize(ndof,dim);
normal.SetSize(dim);
DenseMatrix face_shape_n(face_ndof,dimc);
elmat.SetSize(ndof, face_ndof);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
order = test_fe.GetOrder();
order += trial_face_fe.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
int iel = Trans.Elem1->ElementNo;
if (iel != elem)
{
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
}
double scale = 1.0;
if (iel != elem) { scale = -1.; }
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
// Set the integration point in the face and the neighboring elements
Trans.SetAllIntPoints(&ip);
// Trace finite element shape function
if (dim == 3)
{
trial_face_fe.CalcVShape(Trans,face_shape);
}
else
{
face_shape.GetColumnReference(0,temp);
trial_face_fe.CalcPhysShape(Trans,temp);
}
CalcOrtho(Trans.Jacobian(),normal);
ElementTransformation * eltrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
test_fe.CalcVShape(*eltrans, shape);
// rotate
cross_product(normal, shape, shape_n);
const double w = scale*ip.weight;
AddMult_a_ABt(w,shape_n, face_shape, elmat);
}
}
void NormalInterpolator::AssembleElementMatrix2(
const FiniteElement &dom_fe, const FiniteElement &ran_fe,
+16 -109
View File
@@ -61,8 +61,6 @@ public:
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AssemblePABoundary(const FiniteElementSpace &fes);
virtual void AssemblePAInteriorFaces(const FiniteElementSpace &fes);
virtual void AssemblePABoundaryFaces(const FiniteElementSpace &fes);
@@ -161,15 +159,6 @@ public:
FaceElementTransformations &Trans,
DenseMatrix &elmat);
/** Abstract method used for assembling TraceFaceIntegrators for
DPG weak formulations. */
virtual void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
/// @brief Perform the local action of the BilinearFormIntegrator.
/// Note that the default implementation in the base class is general but not
/// efficient.
@@ -303,12 +292,6 @@ public:
bfi->AssemblePA(fes);
}
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes)
{
bfi->AssemblePA(test_fes, trial_fes); // Reverse test and trial
}
virtual void AssemblePAInteriorFaces(const FiniteElementSpace &fes)
{
bfi->AssemblePAInteriorFaces(fes);
@@ -2200,9 +2183,8 @@ protected:
// PA extension
const FiniteElementSpace *fespace;
Vector pa_data;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
const FaceGeometricFactors *face_geom; ///< Not owned
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
public:
@@ -2229,8 +2211,6 @@ public:
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssemblePABoundary(const FiniteElementSpace &fes);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add);
@@ -3321,87 +3301,6 @@ public:
DenseMatrix &elmat);
};
/** Integrator for the DPG form: < v, w > over a face (the interface) where
the trial variable v is defined on the interface
(H^-1/2 i.e., v:=un normal trace of H(div))
and the test variable w is in an H1-conforming space. */
class TraceIntegrator : public BilinearFormIntegrator
{
private:
Vector face_shape, shape;
public:
TraceIntegrator() { }
void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** Integrator for the form: < v, w.n > over a face (the interface) where
the trial variable v is defined on the interface (H^1/2, i.e., trace of H1)
and the test variable w is in an H(div)-conforming space. */
class NormalTraceIntegrator : public BilinearFormIntegrator
{
private:
Vector face_shape, normal, shape_n;
DenseMatrix shape;
public:
NormalTraceIntegrator() { }
virtual void AssembleTraceFaceMatrix(int ielem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** Integrator for the form: < v, w × n > over a face (the interface)
* In 3D the trial variable v is defined on the interface (H^-1/2(curl), trace of H(curl))
* In 2D it's defined on the interface (H^1/2, trace of H1)
* The test variable w is in an H(curl)-conforming space. */
class TangentTraceIntegrator : public BilinearFormIntegrator
{
private:
DenseMatrix face_shape, shape, shape_n;
Vector normal;
Vector temp;
void cross_product(const Vector & x, const DenseMatrix & Y, DenseMatrix & Z)
{
int dim = x.Size();
MFEM_VERIFY(Y.Width() == dim, "Size missmatch");
int dimc = dim == 3 ? dim : 1;
int h = Y.Height();
Z.SetSize(h,dimc);
if (dim == 3)
{
for (int i = 0; i<h; i++)
{
Z(i,0) = x(2) * Y(i,1) - x(1) * Y(i,2);
Z(i,1) = x(0) * Y(i,2) - x(2) * Y(i,0);
Z(i,2) = x(1) * Y(i,0) - x(0) * Y(i,1);
}
}
else
{
for (int i = 0; i<h; i++)
{
Z(i,0) = x(1) * Y(i,0) - x(0) * Y(i,1);
}
}
}
public:
TangentTraceIntegrator() { }
void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** Abstract class to serve as a base for local interpolators to be used in the
DiscreteLinearOperator class. */
class DiscreteInterpolator : public BilinearFormIntegrator { };
@@ -3437,7 +3336,7 @@ public:
private:
/// 1D finite element that generates and owns the 1D DofToQuad maps below
FiniteElement *dofquad_fe;
FiniteElement * dofquad_fe;
bool B_id; // is the B basis operator (maps_C_C) the identity?
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
@@ -3452,8 +3351,6 @@ private:
class IdentityInterpolator : public DiscreteInterpolator
{
public:
IdentityInterpolator(): dofquad_fe(NULL) { }
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
const FiniteElement &ran_fe,
ElementTransformation &Trans,
@@ -3468,11 +3365,9 @@ public:
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
virtual ~IdentityInterpolator() { delete dofquad_fe; }
private:
/// 1D finite element that generates and owns the 1D DofToQuad maps below
FiniteElement *dofquad_fe;
FiniteElement * dofquad_fe;
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
@@ -3627,5 +3522,17 @@ protected:
VectorCoefficient *VQ;
};
// PA Diffusion Assemble 2D kernel
template<const int T_SDIM>
void PADiffusionSetup2D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
}
#endif
@@ -9,8 +9,8 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../pfespace.hpp"
#include "bilininteg.hpp"
#include "pfespace.hpp"
#include <algorithm>
namespace mfem
@@ -9,9 +9,9 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
@@ -34,7 +34,7 @@ static void EAConvectionAssemble1D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -86,7 +86,7 @@ static void EAConvectionAssemble2D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -163,7 +163,7 @@ static void EAConvectionAssemble3D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 3, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -9,9 +9,12 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../ceed/integrators/convection/convection.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "ceed/integrators/convection/convection.hpp"
using namespace std;
namespace mfem
{
@@ -9,15 +9,18 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../ceed/integrators/convection/convection.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "qfunction.hpp"
#include "ceed/integrators/convection/convection.hpp"
#include "quadinterpolator.hpp"
namespace mfem
{
// PA Convection Integrator
// PA Convection Assemble 2D kernel
static void PAConvectionSetup2D(const int NQ,
const int NE,
@@ -38,7 +41,7 @@ static void PAConvectionSetup2D(const int NQ,
Reshape(vel.Read(), DIM,NQ,NE);
auto y = Reshape(op.Write(), NQ,DIM,NE);
mfem::forall(NE*NQ, [=] MFEM_HOST_DEVICE (int q_global)
MFEM_FORALL(q_global, NE*NQ,
{
const int e = q_global / NQ;
const int q = q_global % NQ;
@@ -75,7 +78,7 @@ static void PAConvectionSetup3D(const int NQ,
Reshape(vel.Read(), 3,1,1) :
Reshape(vel.Read(), 3,NQ,NE);
auto y = Reshape(op.Write(), NQ,3,NE);
mfem::forall(NE*NQ, [=] MFEM_HOST_DEVICE (int q_global)
MFEM_FORALL(q_global, NE*NQ,
{
const int e = q_global / NQ;
const int q = q_global % NQ;
@@ -132,61 +135,6 @@ static void PAConvectionSetup(const int dim,
}
}
void ConvectionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation &Trans = *fes.GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, Trans);
if (DeviceCanUseCeed())
{
delete ceedOp;
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPAConvectionIntegrator(*this, fes, Q, alpha);
}
else
{
ceedOp = new ceed::PAConvectionIntegrator(fes, *ir, Q, alpha);
}
return;
}
const int dims = el.GetDim();
const int symmDims = dims;
nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, mt);
QuadratureSpace qs(*mesh, *ir);
CoefficientVector vel(*Q, qs, CoefficientStorage::COMPRESSED);
PAConvectionSetup(dim, nq, ne, ir->GetWeights(), geom->J,
vel, alpha, pa_data);
}
void ConvectionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
MFEM_ABORT("AssembleDiagonalPA not yet implemented for"
" ConvectionIntegrator.");
}
}
// PA Convection Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0> static
void PAConvectionApply2D(const int ne,
@@ -211,7 +159,7 @@ void PAConvectionApply2D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, 2, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -331,7 +279,7 @@ void SmemPAConvectionApply2D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, 2, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -458,7 +406,7 @@ void PAConvectionApply3D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -639,7 +587,7 @@ void SmemPAConvectionApply3D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -843,7 +791,7 @@ void PAConvectionApplyT2D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, 2, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -959,7 +907,7 @@ void SmemPAConvectionApplyT2D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, 2, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -1081,7 +1029,7 @@ void PAConvectionApplyT3D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1257,7 +1205,7 @@ void SmemPAConvectionApplyT3D(const int ne,
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1427,6 +1375,48 @@ void SmemPAConvectionApplyT3D(const int ne,
});
}
void ConvectionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation &Trans = *fes.GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, Trans);
if (DeviceCanUseCeed())
{
delete ceedOp;
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPAConvectionIntegrator(*this, fes, Q, alpha);
}
else
{
ceedOp = new ceed::PAConvectionIntegrator(fes, *ir, Q, alpha);
}
return;
}
const int dims = el.GetDim();
const int symmDims = dims;
nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, mt);
QuadratureSpace qs(*mesh, *ir);
CoefficientVector vel(*Q, qs, CoefficientStorage::COMPRESSED);
PAConvectionSetup(dim, nq, ne, ir->GetWeights(), geom->J,
vel, alpha, pa_data);
}
static void PAConvectionApply(const int dim,
const int D1D,
const int Q1D,
@@ -1531,6 +1521,7 @@ static void PAConvectionApplyT(const int dim,
MFEM_ABORT("Unknown kernel.");
}
// PA Convection Apply kernel
void ConvectionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
@@ -1545,6 +1536,7 @@ void ConvectionIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
}
// PA Convection Apply transpose kernel
void ConvectionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
@@ -1560,4 +1552,17 @@ void ConvectionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
}
}
void ConvectionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
MFEM_ABORT("AssembleDiagonalPA not yet implemented for"
" ConvectionIntegrator.");
}
}
} // namespace mfem
@@ -9,9 +9,9 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
@@ -26,7 +26,7 @@ static void EADGTraceAssemble1DInt(const int NF,
auto D = Reshape(padata.Read(), 2, 2, NF);
auto A_int = Reshape(eadata_int.ReadWrite(), 2, NF);
auto A_ext = Reshape(eadata_ext.ReadWrite(), 2, NF);
mfem::forall(NF, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, NF,
{
double val_int0, val_int1, val_ext01, val_ext10;
val_int0 = D(0, 0, f);
@@ -58,7 +58,7 @@ static void EADGTraceAssemble1DBdr(const int NF,
{
auto D = Reshape(padata.Read(), 2, 2, NF);
auto A_bdr = Reshape(eadata_bdr.ReadWrite(), NF);
mfem::forall(NF, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, NF,
{
if (add)
{
@@ -89,7 +89,7 @@ static void EADGTraceAssemble2DInt(const int NF,
auto D = Reshape(padata.Read(), Q1D, 2, 2, NF);
auto A_int = Reshape(eadata_int.ReadWrite(), D1D, D1D, 2, NF);
auto A_ext = Reshape(eadata_ext.ReadWrite(), D1D, D1D, 2, NF);
mfem::forall_2D(NF, D1D, D1D, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -143,7 +143,7 @@ static void EADGTraceAssemble2DBdr(const int NF,
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, 2, 2, NF);
auto A_bdr = Reshape(eadata_bdr.ReadWrite(), D1D, D1D, NF);
mfem::forall_2D(NF, D1D, D1D, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -187,7 +187,7 @@ static void EADGTraceAssemble3DInt(const int NF,
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, 2, NF);
auto A_int = Reshape(eadata_int.ReadWrite(), D1D, D1D, D1D, D1D, 2, NF);
auto A_ext = Reshape(eadata_ext.ReadWrite(), D1D, D1D, D1D, D1D, 2, NF);
mfem::forall_2D(NF, D1D, D1D, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -283,7 +283,7 @@ static void EADGTraceAssemble3DBdr(const int NF,
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, 2, NF);
auto A_bdr = Reshape(eadata_bdr.ReadWrite(), D1D, D1D, D1D, D1D, NF);
mfem::forall_2D(NF, D1D, D1D, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -9,15 +9,16 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../restriction.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "qfunction.hpp"
#include "restriction.hpp"
using namespace std;
namespace mfem
{
// PA DG Trace Integrator
static void PADGTraceSetup2D(const int Q1D,
const int NF,
@@ -43,7 +44,7 @@ static void PADGTraceSetup2D(const int Q1D,
auto W = w.Read();
auto qd = Reshape(op.Write(), Q1D, 2, 2, NF);
mfem::forall(Q1D*NF, [=] MFEM_HOST_DEVICE (int tid)
MFEM_FORALL(tid, Q1D*NF,
{
const int f = tid / Q1D;
const int q = tid % Q1D;
@@ -86,7 +87,7 @@ static void PADGTraceSetup3D(const int Q1D,
auto W = w.Read();
auto qd = Reshape(op.Write(), Q1D, Q1D, 2, 2, NF);
mfem::forall(Q1D*Q1D*NF, [=] MFEM_HOST_DEVICE (int tid)
MFEM_FORALL(tid, Q1D*Q1D*NF,
{
int f = tid / (Q1D * Q1D);
int q2 = (tid / Q1D) % Q1D;
@@ -98,7 +99,7 @@ static void PADGTraceSetup3D(const int Q1D,
const double v1 = const_v ? V(1,0,0,0) : V(1,q1,q2,f);
const double v2 = const_v ? V(2,0,0,0) : V(2,q1,q2,f);
const double dot = n(q1,q2,0,f) * v0 + n(q1,q2,1,f) * v1 +
n(q1,q2,2,f) * v2;
/* */ n(q1,q2,2,f) * v2;
const double abs = dot > 0.0 ? dot : -dot;
const double w = W[q1+q2*Q1D]*r*d(q1,q2,f);
qd(q1,q2,0,0,f) = w*( alpha/2 * dot + beta * abs );
@@ -266,7 +267,7 @@ void PADGTraceApply2D(const int NF,
auto x = Reshape(x_.Read(), D1D, VDIM, 2, NF);
auto y = Reshape(y_.ReadWrite(), D1D, VDIM, 2, NF);
mfem::forall(NF, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, NF,
{
const int VDIM = 1;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -357,7 +358,7 @@ void PADGTraceApply3D(const int NF,
auto x = Reshape(x_.Read(), D1D, D1D, VDIM, 2, NF);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, VDIM, 2, NF);
mfem::forall(NF, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, NF,
{
const int VDIM = 1;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -502,7 +503,7 @@ void SmemPADGTraceApply3D(const int NF,
auto x = Reshape(x_.Read(), D1D, D1D, 2, NF);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, 2, NF);
mfem::forall_2D_batch(NF, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL_2D(f, NF, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -667,7 +668,7 @@ void PADGTraceApplyTranspose2D(const int NF,
auto x = Reshape(x_.Read(), D1D, VDIM, 2, NF);
auto y = Reshape(y_.ReadWrite(), D1D, VDIM, 2, NF);
mfem::forall(NF, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, NF,
{
const int VDIM = 1;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -763,7 +764,7 @@ void PADGTraceApplyTranspose3D(const int NF,
auto x = Reshape(x_.Read(), D1D, D1D, VDIM, 2, NF);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, VDIM, 2, NF);
mfem::forall(NF, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, NF,
{
const int VDIM = 1;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -919,7 +920,7 @@ void SmemPADGTraceApplyTranspose3D(const int NF,
auto x = Reshape(x_.Read(), D1D, D1D, 2, NF);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, 2, NF);
mfem::forall_2D_batch(NF, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL_2D(f, NF, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -9,9 +9,9 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
@@ -33,7 +33,7 @@ static void EADiffusionAssemble1D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -85,7 +85,7 @@ static void EADiffusionAssemble2D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, 3, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -162,7 +162,7 @@ static void EADiffusionAssemble3D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 6, NE);
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -9,9 +9,12 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "ceed/integrators/diffusion/diffusion.hpp"
using namespace std;
namespace mfem
{
@@ -9,42 +9,189 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
#define MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "qfunction.hpp"
#include "ceed/integrators/diffusion/diffusion.hpp"
#include "../../config/config.hpp"
#include "../../general/array.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
using namespace std;
namespace mfem
{
namespace internal
// PA Diffusion Integrator
// OCCA 2D Assemble kernel
#ifdef MFEM_USE_OCCA
static void OccaPADiffusionSetup2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &op)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup2D_ker;
if (OccaDiffSetup2D_ker.find(id) == OccaDiffSetup2D_ker.end())
{
const occa::kernel DiffusionSetup2D =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionSetup2D", props);
OccaDiffSetup2D_ker.emplace(id, DiffusionSetup2D);
}
OccaDiffSetup2D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
}
void PADiffusionSetup(const int dim,
const int sdim,
const int D1D,
const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &D);
static void OccaPADiffusionSetup3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &op)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup3D_ker;
if (OccaDiffSetup3D_ker.find(id) == OccaDiffSetup3D_ker.end())
{
const occa::kernel DiffusionSetup3D =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionSetup3D", props);
OccaDiffSetup3D_ker.emplace(id, DiffusionSetup3D);
}
OccaDiffSetup3D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
}
#endif // MFEM_USE_OCCA
// PA Diffusion Assemble 2D kernel
template<int T_SDIM>
void PADiffusionSetup2D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
template<>
void PADiffusionSetup2D<2>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
const bool symmetric = (coeffDim != 4);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 3 ||
!const_c, "Constant matrix coefficient not supported");
const auto W = Reshape(w.Read(), Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,2,2,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, symmetric ? 3 : 4, NE);
MFEM_FORALL_2D(e, NE, Q1D,Q1D,1,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
const double J11 = J(qx,qy,0,0,e);
const double J21 = J(qx,qy,1,0,e);
const double J12 = J(qx,qy,0,1,e);
const double J22 = J(qx,qy,1,1,e);
const double w_detJ = W(qx,qy) / ((J11*J22)-(J21*J12));
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient
{
// First compute entries of R = MJ^{-T}, without det J factor.
const double M11 = C(0,qx,qy,e);
const double M12 = C(1,qx,qy,e);
const double M21 = symmetric ? M12 : C(2,qx,qy,e);
const double M22 = symmetric ? C(2,qx,qy,e) : C(3,qx,qy,e);
const double R11 = M11*J22 - M12*J12;
const double R21 = M21*J22 - M22*J12;
const double R12 = -M11*J21 + M12*J11;
const double R22 = -M21*J21 + M22*J11;
// Now set y to J^{-1}R.
D(qx,qy,0,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
D(qx,qy,1,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
D(qx,qy,2,e) = w_detJ * (symmetric ? (-J21*R12 + J11*R22) :
(J22*R12 - J12*R22)); // 2,2 or 1,2
if (!symmetric)
{
D(qx,qy,3,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
}
else // Vector or scalar coefficient
{
const double C1 = const_c ? C(0,0,0,0) : C(0,qx,qy,e);
const double C2 = const_c ? C(0,0,0,0) :
(coeffDim == 2 ? C(1,qx,qy,e) : C(0,qx,qy,e));
D(qx,qy,0,e) = w_detJ * (C2*J12*J12 + C1*J22*J22); // 1,1
D(qx,qy,1,e) = -w_detJ * (C2*J12*J11 + C1*J22*J21); // 1,2
D(qx,qy,2,e) = w_detJ * (C2*J11*J11 + C1*J21*J21); // 2,2
}
}
}
});
}
// PA Diffusion Assemble 2D kernel with 3D node coords
template<>
void PADiffusionSetup2D<3>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
MFEM_VERIFY(coeffDim == 1, "Matrix and vector coefficients not supported");
constexpr int DIM = 2;
constexpr int SDIM = 3;
const bool const_c = c.Size() == 1;
const auto W = Reshape(w.Read(), Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,SDIM,DIM,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1) :
Reshape(c.Read(), Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, 3, NE);
MFEM_FORALL_2D(e, NE, Q1D,Q1D,1,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
const double wq = W(qx,qy);
const double J11 = J(qx,qy,0,0,e);
const double J21 = J(qx,qy,1,0,e);
const double J31 = J(qx,qy,2,0,e);
const double J12 = J(qx,qy,0,1,e);
const double J22 = J(qx,qy,1,1,e);
const double J32 = J(qx,qy,2,1,e);
const double E = J11*J11 + J21*J21 + J31*J31;
const double G = J12*J12 + J22*J22 + J32*J32;
const double F = J11*J12 + J21*J22 + J31*J32;
const double iw = 1.0 / sqrt(E*G - F*F);
const double coeff = const_c ? C(0,0,0) : C(qx,qy,e);
const double alpha = wq * coeff * iw;
D(qx,qy,0,e) = alpha * G; // 1,1
D(qx,qy,1,e) = -alpha * F; // 1,2
D(qx,qy,2,e) = alpha * E; // 2,2
}
}
});
}
// PA Diffusion Assemble 3D kernel
void PADiffusionSetup3D(const int Q1D,
@@ -53,41 +200,217 @@ void PADiffusionSetup3D(const int Q1D,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
Vector &d)
{
const bool symmetric = (coeffDim != 9);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 6 ||
!const_c, "Constant matrix coefficient not supported");
const auto W = Reshape(w.Read(), Q1D,Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,Q1D,3,3,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D,Q1D, symmetric ? 6 : 9, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
const double J11 = J(qx,qy,qz,0,0,e);
const double J21 = J(qx,qy,qz,1,0,e);
const double J31 = J(qx,qy,qz,2,0,e);
const double J12 = J(qx,qy,qz,0,1,e);
const double J22 = J(qx,qy,qz,1,1,e);
const double J32 = J(qx,qy,qz,2,1,e);
const double J13 = J(qx,qy,qz,0,2,e);
const double J23 = J(qx,qy,qz,1,2,e);
const double J33 = J(qx,qy,qz,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double w_detJ = W(qx,qy,qz) / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
const double A13 = (J12 * J23) - (J22 * J13);
const double A21 = (J31 * J23) - (J21 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J21 * J13) - (J11 * J23);
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
{
// Compute entries of R = MJ^{-T} = M adj(J)^T, without det J.
const double M11 = C(0, qx,qy,qz, e);
const double M12 = C(1, qx,qy,qz, e);
const double M13 = C(2, qx,qy,qz, e);
const double M21 = (!symmetric) ? C(3, qx,qy,qz, e) : M12;
const double M22 = (!symmetric) ? C(4, qx,qy,qz, e) : C(3, qx,qy,qz, e);
const double M23 = (!symmetric) ? C(5, qx,qy,qz, e) : C(4, qx,qy,qz, e);
const double M31 = (!symmetric) ? C(6, qx,qy,qz, e) : M13;
const double M32 = (!symmetric) ? C(7, qx,qy,qz, e) : M23;
const double M33 = (!symmetric) ? C(8, qx,qy,qz, e) : C(5, qx,qy,qz, e);
const double R11 = M11*A11 + M12*A12 + M13*A13;
const double R12 = M11*A21 + M12*A22 + M13*A23;
const double R13 = M11*A31 + M12*A32 + M13*A33;
const double R21 = M21*A11 + M22*A12 + M23*A13;
const double R22 = M21*A21 + M22*A22 + M23*A23;
const double R23 = M21*A31 + M22*A32 + M23*A33;
const double R31 = M31*A11 + M32*A12 + M33*A13;
const double R32 = M31*A21 + M32*A22 + M33*A23;
const double R33 = M31*A31 + M32*A32 + M33*A33;
// Now set D to J^{-1} R = adj(J) R
D(qx,qy,qz,0,e) = w_detJ * (A11*R11 + A12*R21 + A13*R31); // 1,1
const double D12 = w_detJ * (A11*R12 + A12*R22 + A13*R32);
D(qx,qy,qz,1,e) = D12; // 1,2
D(qx,qy,qz,2,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
const double D22 = w_detJ * (A21*R12 + A22*R22 + A23*R32);
const double D23 = w_detJ * (A21*R13 + A22*R23 + A23*R33);
const double D33 = w_detJ * (A31*R13 + A32*R23 + A33*R33);
D(qx,qy,qz,4,e) = symmetric ? D23 : D22; // 2,3 or 2,2
D(qx,qy,qz,5,e) = symmetric ? D33 : D23; // 3,3 or 2,3
if (symmetric)
{
D(qx,qy,qz,3,e) = D22; // 2,2
}
else
{
D(qx,qy,qz,3,e) = w_detJ * (A21*R11 + A22*R21 + A23*R31); // 2,1
D(qx,qy,qz,6,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
D(qx,qy,qz,7,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
D(qx,qy,qz,8,e) = D33; // 3,3
}
}
else // Vector or scalar coefficient version
{
const double C1 = const_c ? C(0,0,0,0,0) : C(0,qx,qy,qz,e);
const double C2 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(1,qx,qy,qz,e) : C(0,qx,qy,qz,e));
const double C3 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(2,qx,qy,qz,e) : C(0,qx,qy,qz,e));
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
D(qx,qy,qz,0,e) = w_detJ * (C1*A11*A11 + C2*A12*A12 + C3*A13*A13); // 1,1
D(qx,qy,qz,1,e) = w_detJ * (C1*A11*A21 + C2*A12*A22 + C3*A13*A23); // 2,1
D(qx,qy,qz,2,e) = w_detJ * (C1*A11*A31 + C2*A12*A32 + C3*A13*A33); // 3,1
D(qx,qy,qz,3,e) = w_detJ * (C1*A21*A21 + C2*A22*A22 + C3*A23*A23); // 2,2
D(qx,qy,qz,4,e) = w_detJ * (C1*A21*A31 + C2*A22*A32 + C3*A23*A33); // 3,2
D(qx,qy,qz,5,e) = w_detJ * (C1*A31*A31 + C2*A32*A32 + C3*A33*A33); // 3,3
}
}
}
}
});
}
static void PADiffusionSetup(const int dim,
const int sdim,
const int D1D,
const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &D)
{
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PADiffusionSetup"); }
if (dim == 2)
{
#ifdef MFEM_USE_OCCA
// OCCA 2D Assemble kernel
void OccaPADiffusionSetup2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &op);
// OCCA 3D Assemble kernel
void OccaPADiffusionSetup3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &op);
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, C, D);
return;
}
#else
MFEM_CONTRACT_VAR(D1D);
#endif // MFEM_USE_OCCA
if (sdim == 2) { PADiffusionSetup2D<2>(Q1D, coeffDim, NE, W, J, C, D); }
if (sdim == 3) { PADiffusionSetup2D<3>(Q1D, coeffDim, NE, W, J, C, D); }
}
if (dim == 3)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup3D(D1D, Q1D, NE, W, J, C, D);
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup3D(Q1D, coeffDim, NE, W, J, C, D);
}
}
void PADiffusionAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Vector &D,
Vector &Y);
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
// Assuming the same element type
fespace = &fes;
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
if (DeviceCanUseCeed())
{
delete ceedOp;
MFEM_VERIFY(!VQ && !MQ,
"Only scalar coefficient supported for DiffusionIntegrator"
" with libCEED");
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
}
else
{
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
}
return;
}
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
const int sdim = mesh->SpaceDimension();
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(qs, CoefficientStorage::COMPRESSED);
if (MQ) { coeff.ProjectTranspose(*MQ); }
else if (VQ) { coeff.Project(*VQ); }
else if (Q) { coeff.Project(*Q); }
else { coeff.SetConstant(1.0); }
const int coeff_dim = coeff.GetVDim();
symmetric = (coeff_dim != dims*dims);
const int pa_size = symmetric ? symmDims : dims*dims;
pa_data.SetSize(pa_size * nq * ne, mt);
PADiffusionSetup(dim, sdim, dofs1D, quad1D, coeff_dim, ne, ir->GetWeights(),
geom->J, coeff, pa_data);
}
// PA Diffusion Diagonal 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PADiffusionDiagonal2D(const int NE,
static void PADiffusionDiagonal2D(const int NE,
const bool symmetric,
const Array<double> &b,
const Array<double> &g,
@@ -106,7 +429,7 @@ inline void PADiffusionDiagonal2D(const int NE,
// store necessary entries
auto D = Reshape(d.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -153,7 +476,7 @@ inline void PADiffusionDiagonal2D(const int NE,
// Shared memory PA Diffusion Diagonal 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
inline void SmemPADiffusionDiagonal2D(const int NE,
static void SmemPADiffusionDiagonal2D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
@@ -173,7 +496,7 @@ inline void SmemPADiffusionDiagonal2D(const int NE,
auto g = Reshape(g_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -246,9 +569,8 @@ inline void SmemPADiffusionDiagonal2D(const int NE,
});
}
// PA Diffusion Diagonal 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PADiffusionDiagonal3D(const int NE,
static void PADiffusionDiagonal3D(const int NE,
const bool symmetric,
const Array<double> &b,
const Array<double> &g,
@@ -268,7 +590,7 @@ inline void PADiffusionDiagonal3D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, symmetric ? 6 : 9, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -292,8 +614,8 @@ inline void PADiffusionDiagonal3D(const int NE,
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const int ksym = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const int k = symmetric ? ksym : (i*DIM) + j;
const double O = Q(q,k,e);
const double Bz = B(qz,dz);
@@ -349,7 +671,7 @@ inline void PADiffusionDiagonal3D(const int NE,
// Shared memory PA Diffusion Diagonal 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPADiffusionDiagonal3D(const int NE,
static void SmemPADiffusionDiagonal3D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
@@ -369,7 +691,7 @@ inline void SmemPADiffusionDiagonal3D(const int NE,
auto g = Reshape(g_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, symmetric ? 6 : 9, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -466,48 +788,169 @@ inline void SmemPADiffusionDiagonal3D(const int NE,
});
}
void PADiffusionApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y);
static void PADiffusionAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Vector &D,
Vector &Y)
{
if (dim == 2)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionDiagonal2D<2,2,8>(NE,symm,B,G,D,Y);
case 0x33: return SmemPADiffusionDiagonal2D<3,3,8>(NE,symm,B,G,D,Y);
case 0x44: return SmemPADiffusionDiagonal2D<4,4,4>(NE,symm,B,G,D,Y);
case 0x55: return SmemPADiffusionDiagonal2D<5,5,4>(NE,symm,B,G,D,Y);
case 0x66: return SmemPADiffusionDiagonal2D<6,6,2>(NE,symm,B,G,D,Y);
case 0x77: return SmemPADiffusionDiagonal2D<7,7,2>(NE,symm,B,G,D,Y);
case 0x88: return SmemPADiffusionDiagonal2D<8,8,1>(NE,symm,B,G,D,Y);
case 0x99: return SmemPADiffusionDiagonal2D<9,9,1>(NE,symm,B,G,D,Y);
default: return PADiffusionDiagonal2D(NE,symm,B,G,D,Y,D1D,Q1D);
}
}
else if (dim == 3)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionDiagonal3D<2,2>(NE,symm,B,G,D,Y);
case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,symm,B,G,D,Y);
case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,symm,B,G,D,Y);
case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,symm,B,G,D,Y);
case 0x46: return SmemPADiffusionDiagonal3D<4,6>(NE,symm,B,G,D,Y);
case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,symm,B,G,D,Y);
case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,symm,B,G,D,Y);
case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,symm,B,G,D,Y);
case 0x89: return SmemPADiffusionDiagonal3D<8,9>(NE,symm,B,G,D,Y);
case 0x9A: return SmemPADiffusionDiagonal3D<9,10>(NE,symm,B,G,D,Y);
default: return PADiffusionDiagonal3D(NE,symm,B,G,D,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
if (pa_data.Size()==0) { AssemblePA(*fespace); }
PADiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne, symmetric,
maps->B, maps->G, pa_data, diag);
}
}
#ifdef MFEM_USE_OCCA
// OCCA PA Diffusion Apply 2D kernel
void OccaPADiffusionApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y);
static void OccaPADiffusionApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_G = OccaMemoryRead(G.GetMemory(), G.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_Gt = OccaMemoryRead(Gt.GetMemory(), Gt.Size());
const occa::memory o_D = OccaMemoryRead(D.GetMemory(), D.Size());
const occa::memory o_X = OccaMemoryRead(X.GetMemory(), X.Size());
occa::memory o_Y = OccaMemoryReadWrite(Y.GetMemory(), Y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaDiffApply2D_cpu;
if (OccaDiffApply2D_cpu.find(id) == OccaDiffApply2D_cpu.end())
{
const occa::kernel DiffusionApply2D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply2D_CPU", props);
OccaDiffApply2D_cpu.emplace(id, DiffusionApply2D_CPU);
}
OccaDiffApply2D_cpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
else
{
static occa_kernel_t OccaDiffApply2D_gpu;
if (OccaDiffApply2D_gpu.find(id) == OccaDiffApply2D_gpu.end())
{
const occa::kernel DiffusionApply2D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply2D_GPU", props);
OccaDiffApply2D_gpu.emplace(id, DiffusionApply2D_GPU);
}
OccaDiffApply2D_gpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
}
// OCCA PA Diffusion Apply 3D kernel
void OccaPADiffusionApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y);
static void OccaPADiffusionApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_G = OccaMemoryRead(G.GetMemory(), G.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_Gt = OccaMemoryRead(Gt.GetMemory(), Gt.Size());
const occa::memory o_D = OccaMemoryRead(D.GetMemory(), D.Size());
const occa::memory o_X = OccaMemoryRead(X.GetMemory(), X.Size());
occa::memory o_Y = OccaMemoryReadWrite(Y.GetMemory(), Y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaDiffApply3D_cpu;
if (OccaDiffApply3D_cpu.find(id) == OccaDiffApply3D_cpu.end())
{
const occa::kernel DiffusionApply3D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply3D_CPU", props);
OccaDiffApply3D_cpu.emplace(id, DiffusionApply3D_CPU);
}
OccaDiffApply3D_cpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
else
{
static occa_kernel_t OccaDiffApply3D_gpu;
if (OccaDiffApply3D_gpu.find(id) == OccaDiffApply3D_gpu.end())
{
const occa::kernel DiffusionApply3D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply3D_GPU", props);
OccaDiffApply3D_gpu.emplace(id, DiffusionApply3D_GPU);
}
OccaDiffApply3D_gpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
}
#endif // MFEM_USE_OCCA
// PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PADiffusionApply2D(const int NE,
static void PADiffusionApply2D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
@@ -530,7 +973,7 @@ inline void PADiffusionApply2D(const int NE,
auto D = Reshape(d_.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto X = Reshape(x_.Read(), D1D, D1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -629,7 +1072,7 @@ inline void PADiffusionApply2D(const int NE,
// Shared memory PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
inline void SmemPADiffusionApply2D(const int NE,
static void SmemPADiffusionApply2D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
@@ -651,7 +1094,7 @@ inline void SmemPADiffusionApply2D(const int NE,
auto D = Reshape(d_.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE(int e)
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
@@ -787,7 +1230,7 @@ inline void SmemPADiffusionApply2D(const int NE,
// PA Diffusion Apply 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PADiffusionApply3D(const int NE,
static void PADiffusionApply3D(const int NE,
const bool symmetric,
const Array<double> &b,
const Array<double> &g,
@@ -809,7 +1252,7 @@ inline void PADiffusionApply3D(const int NE,
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, symmetric ? 6 : 9, NE);
auto X = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -978,9 +1421,8 @@ inline void PADiffusionApply3D(const int NE,
});
}
// Shared memory PA Diffusion Apply 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPADiffusionApply3D(const int NE,
static void SmemPADiffusionApply3D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
@@ -1001,7 +1443,7 @@ inline void SmemPADiffusionApply3D(const int NE,
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1201,8 +1643,99 @@ inline void SmemPADiffusionApply3D(const int NE,
});
}
} // namespace internal
static void PADiffusionApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
if (dim == 2)
{
OccaPADiffusionApply2D(D1D,Q1D,NE,B,G,Bt,Gt,D,X,Y);
return;
}
if (dim == 3)
{
OccaPADiffusionApply3D(D1D,Q1D,NE,B,G,Bt,Gt,D,X,Y);
return;
}
MFEM_ABORT("OCCA PADiffusionApply unknown kernel!");
}
#endif // MFEM_USE_OCCA
const int id = (D1D << 4) | Q1D;
if (dim == 2)
{
switch (id)
{
case 0x22: return SmemPADiffusionApply2D<2,2,16>(NE,symm,B,G,D,X,Y);
case 0x33: return SmemPADiffusionApply2D<3,3,16>(NE,symm,B,G,D,X,Y);
case 0x44: return SmemPADiffusionApply2D<4,4,8>(NE,symm,B,G,D,X,Y);
case 0x55: return SmemPADiffusionApply2D<5,5,8>(NE,symm,B,G,D,X,Y);
case 0x66: return SmemPADiffusionApply2D<6,6,4>(NE,symm,B,G,D,X,Y);
case 0x77: return SmemPADiffusionApply2D<7,7,4>(NE,symm,B,G,D,X,Y);
case 0x88: return SmemPADiffusionApply2D<8,8,2>(NE,symm,B,G,D,X,Y);
case 0x99: return SmemPADiffusionApply2D<9,9,2>(NE,symm,B,G,D,X,Y);
default: return PADiffusionApply2D(NE,symm,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
if (dim == 3)
{
switch (id)
{
case 0x22: return SmemPADiffusionApply3D<2,2>(NE,symm,B,G,D,X,Y);
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,symm,B,G,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,symm,B,G,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,symm,B,G,D,X,Y);
case 0x46: return SmemPADiffusionApply3D<4,6>(NE,symm,B,G,D,X,Y);
case 0x56: return SmemPADiffusionApply3D<5,6>(NE,symm,B,G,D,X,Y);
case 0x58: return SmemPADiffusionApply3D<5,8>(NE,symm,B,G,D,X,Y);
case 0x67: return SmemPADiffusionApply3D<6,7>(NE,symm,B,G,D,X,Y);
case 0x78: return SmemPADiffusionApply3D<7,8>(NE,symm,B,G,D,X,Y);
case 0x89: return SmemPADiffusionApply3D<8,9>(NE,symm,B,G,D,X,Y);
default: return PADiffusionApply3D(NE,symm,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel: 0x"<<std::hex << id << std::dec);
}
// PA Diffusion Apply kernel
void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
{
ceedOp->AddMult(x, y);
}
else
{
PADiffusionApply(dim, dofs1D, quad1D, ne, symmetric,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
}
}
void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
if (symmetric)
{
AddMultPA(x, y);
}
else
{
MFEM_ABORT("DiffusionIntegrator::AddMultTransposePA only implemented in "
"the symmetric case.")
}
}
} // namespace mfem
#endif
@@ -9,13 +9,17 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
using namespace std;
namespace mfem
{
// PA Divergence Integrator
// PA Divergence Assemble 2D kernel
static void PADivergenceSetup2D(const int Q1D,
const int NE,
@@ -29,7 +33,7 @@ static void PADivergenceSetup2D(const int Q1D,
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto y = Reshape(op.Write(), NQ, 2, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -58,7 +62,7 @@ static void PADivergenceSetup3D(const int Q1D,
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto y = Reshape(op.Write(), NQ, 3, 3, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -179,7 +183,7 @@ static void PADivergenceApply2D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D, 2,2, NE);
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, 2, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
@@ -317,7 +321,7 @@ static void PADivergenceApplyTranspose2D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D, 2,2, NE);
auto x = Reshape(x_.Read(), TE_D1D, TE_D1D, NE);
auto y = Reshape(y_.ReadWrite(), TR_D1D, TR_D1D, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
@@ -433,7 +437,7 @@ static void PADivergenceApply3D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D*Q1D, 3,3, NE);
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, TR_D1D, 3, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
@@ -616,7 +620,7 @@ static void PADivergenceApplyTranspose3D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D*Q1D, 3,3, NE);
auto x = Reshape(x_.Read(), TE_D1D, TE_D1D, TE_D1D, NE);
auto y = Reshape(y_.ReadWrite(), TR_D1D, TR_D1D, TR_D1D, 3, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
@@ -797,7 +801,7 @@ static void SmemPADivergenceApply3D(const int NE,
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, TR_D1D, 3, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
constexpr int VDIM = 3;
const int tidz = MFEM_THREAD_ID(z);
@@ -1036,25 +1040,11 @@ static void PADivergenceApply(const int dim,
{
if (dim == 2)
{
if (transpose)
{
return PADivergenceApplyTranspose2D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
else
{
return PADivergenceApply2D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
return PADivergenceApply2D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
if (dim == 3)
{
if (transpose)
{
return PADivergenceApplyTranspose3D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
else
{
return PADivergenceApply3D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
return PADivergenceApply3D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
MFEM_ABORT("Unknown kernel.");
}
@@ -9,14 +9,18 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "qfunction.hpp"
using namespace std;
namespace mfem
{
// PA Gradient Integrator
/* Description of the *SetupND functions
Inputs are as follows
\b Q1D number of quadrature points in one dimension.
@@ -58,8 +62,8 @@ namespace mfem
The shared memory (Smem) versions of the kernels differ from the regular
versions in the following properties.
\b mfem::forall is using only one level of parallelism.
\b mfem::forall_ND uses an additional level of parallelism
\b MFEM_FORALL is using only one level of parallelism.
\b MFEM_FORALL_ND uses an additional level of parallelism
\b MFEM_FOREACH_THREAD
These macros allow automatic mapping of manually defined blocks to
@@ -83,7 +87,7 @@ static void PAGradientSetup2D(const int Q1D,
const auto C = const_c ? Reshape(c.Read(), 1,1) :
Reshape(c.Read(), NQ, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -118,7 +122,7 @@ static void PAGradientSetup3D(const int Q1D,
const auto C = const_c ? Reshape(c.Read(), 1,1) :
Reshape(c.Read(), NQ,NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -238,7 +242,7 @@ static void PAGradientApply2D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D, 2,2, NE);
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
@@ -368,7 +372,7 @@ static void PAGradientApply3D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D*Q1D, 3,3, NE);
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, TR_D1D, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, 3, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
@@ -568,8 +572,7 @@ static void SmemPAGradientApply3D(const int NE,
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, TR_D1D, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, 3, NE);
mfem::forall_3D(NE, (Q1D>8)?8:Q1D, (Q1D>8)?8:Q1D, (Q1D>8)?8:Q1D,
[=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, (Q1D>8)?8:Q1D, (Q1D>8)?8:Q1D, (Q1D>8)?8:Q1D,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1DR = T_TR_D1D ? T_TR_D1D : tr_d1d;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -9,9 +9,9 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
namespace mfem
{
@@ -32,7 +32,7 @@ static void EAMassAssemble1D(const int NE,
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto M = Reshape(eadata.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -82,7 +82,7 @@ static void EAMassAssemble2D(const int NE,
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, NE);
auto M = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -154,51 +154,20 @@ static void EAMassAssemble3D(const int NE,
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, NE);
auto M = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int DQ = T_D1D * T_Q1D;
// For quadratic and lower it's better to use registers but for higher-order you start to
// spill and it's better to use shared memory
constexpr bool USE_REG = DQ != 0 && DQ <= 12;
constexpr int MD1r = USE_REG ? MD1 : 1;
constexpr int MQ1r = USE_REG ? MQ1 : 1;
constexpr int MD1s = USE_REG ? 1 : MD1;
constexpr int MQ1s = USE_REG ? 1 : MQ1;
MFEM_SHARED double s_B[MQ1s][MD1s];
double r_B[MQ1r][MD1r];
double (*l_B)[MD1] = nullptr;
if (USE_REG)
double r_B[MQ1][MD1];
for (int d = 0; d < D1D; d++)
{
for (int d = 0; d < D1D; d++)
for (int q = 0; q < Q1D; q++)
{
for (int q = 0; q < Q1D; q++)
{
r_B[q][d] = B(q,d);
}
r_B[q][d] = B(q,d);
}
l_B = (double (*)[MD1])r_B;
}
else
{
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD(d,x,D1D)
{
MFEM_FOREACH_THREAD(q,y,Q1D)
{
s_B[q][d] = B(q,d);
}
}
}
l_B = (double (*)[MD1])s_B;
}
MFEM_SHARED double s_D[MQ1][MQ1][MQ1];
MFEM_FOREACH_THREAD(k1,x,Q1D)
{
@@ -230,9 +199,9 @@ static void EAMassAssemble3D(const int NE,
{
for (int k3 = 0; k3 < Q1D; ++k3)
{
val += l_B[k1][i1] * l_B[k1][j1]
* l_B[k2][i2] * l_B[k2][j2]
* l_B[k3][i3] * l_B[k3][j3]
val += r_B[k1][i1] * r_B[k1][j1]
* r_B[k2][i2] * r_B[k2][j2]
* r_B[k3][i3] * r_B[k3][j3]
* s_D[k1][k2][k3];
}
}
@@ -9,9 +9,12 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../ceed/integrators/mass/mass.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "ceed/integrators/mass/mass.hpp"
using namespace std;
namespace mfem
{
+736
View File
@@ -0,0 +1,736 @@
// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "qfunction.hpp"
#include "ceed/integrators/mass/mass.hpp"
#include "bilininteg_mass_pa.hpp"
using namespace std;
namespace mfem
{
// PA Mass Integrator
// PA Mass Assemble kernel
void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
// Assuming the same element type
fespace = &fes;
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation *T0 = mesh->GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0);
if (DeviceCanUseCeed())
{
delete ceedOp;
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPAMassIntegrator(*this, fes, Q);
}
else
{
ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q);
}
return;
}
int map_type = el.GetMapType();
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, mt);
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
if (dim==1) { MFEM_ABORT("Not supported yet... stay tuned!"); }
if (dim==2)
{
const int NE = ne;
const int Q1D = quad1D;
const bool const_c = coeff.Size() == 1;
const bool by_val = map_type == FiniteElement::VALUE;
const auto W = Reshape(ir->GetWeights().Read(), Q1D,Q1D);
const auto J = Reshape(geom->detJ.Read(), Q1D,Q1D,NE);
const auto C = const_c ? Reshape(coeff.Read(), 1,1,1) :
Reshape(coeff.Read(), Q1D,Q1D,NE);
auto v = Reshape(pa_data.Write(), Q1D,Q1D, NE);
MFEM_FORALL_2D(e, NE, Q1D,Q1D,1,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
const double detJ = J(qx,qy,e);
const double coeff = const_c ? C(0,0,0) : C(qx,qy,e);
v(qx,qy,e) = W(qx,qy) * coeff * (by_val ? detJ : 1.0/detJ);
}
}
});
}
if (dim==3)
{
const int NE = ne;
const int Q1D = quad1D;
const bool const_c = coeff.Size() == 1;
const bool by_val = map_type == FiniteElement::VALUE;
const auto W = Reshape(ir->GetWeights().Read(), Q1D,Q1D,Q1D);
const auto J = Reshape(geom->detJ.Read(), Q1D,Q1D,Q1D,NE);
const auto C = const_c ? Reshape(coeff.Read(), 1,1,1,1) :
Reshape(coeff.Read(), Q1D,Q1D,Q1D,NE);
auto v = Reshape(pa_data.Write(), Q1D,Q1D,Q1D,NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
const double detJ = J(qx,qy,qz,e);
const double coeff = const_c ? C(0,0,0,0) : C(qx,qy,qz,e);
v(qx,qy,qz,e) = W(qx,qy,qz) * coeff * (by_val ? detJ : 1.0/detJ);
}
}
}
});
}
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAMassAssembleDiagonal2D(const int NE,
const Array<double> &b,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto D = Reshape(d.Read(), Q1D, Q1D, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double QD[MQ1][MD1];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
QD[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QD[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
for (int qx = 0; qx < Q1D; ++qx)
{
Y(dx,dy,e) += B(qx, dx) * B(qx, dx) * QD[qx][dy];
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void SmemPAMassAssembleDiagonal2D(const int NE,
const Array<double> &b_,
const Vector &d_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D, Q1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_SHARED double B[MQ1][MD1];
MFEM_SHARED double QDZ[NBZ][MQ1][MD1];
double (*QD)[MD1] = (double (*)[MD1])(QDZ + tidz);
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
QD[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QD[qx][dy] += B[qy][dy] * B[qy][dy] * D(qx, qy, e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
for (int qx = 0; qx < Q1D; ++qx)
{
// might need absolute values on next line
Y(dx,dy,e) += B[qx][dx] * B[qx][dx] * QD[qx][dy];
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAMassAssembleDiagonal3D(const int NE,
const Array<double> &b,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto D = Reshape(d.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double QQD[MQ1][MQ1][MD1];
double QDD[MQ1][MD1][MD1];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
QQD[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
QQD[qx][qy][dz] += B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
}
}
}
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
QDD[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QDD[qx][dy][dz] += B(qy, dy) * B(qy, dy) * QQD[qx][qy][dz];
}
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double t = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
t += B(qx, dx) * B(qx, dx) * QDD[qx][dy][dz];
}
Y(dx, dy, dz, e) += t;
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void SmemPAMassAssembleDiagonal3D(const int NE,
const Array<double> &b_,
const Vector &d_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
MFEM_SHARED double B[MQ1][MD1];
MFEM_SHARED double QQD[MQ1][MQ1][MD1];
MFEM_SHARED double QDD[MQ1][MD1][MD1];
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dz,z,D1D)
{
QQD[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
QQD[qx][qy][dz] += B[qz][dz] * B[qz][dz] * D(qx, qy, qz, e);
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
QDD[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QDD[qx][dy][dz] += B[qy][dy] * B[qy][dy] * QQD[qx][qy][dz];
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double t = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
t += B[qx][dx] * B[qx][dx] * QDD[qx][dy][dz];
}
Y(dx, dy, dz, e) += t;
}
}
}
});
}
static void PAMassAssembleDiagonal(const int dim, const int D1D,
const int Q1D, const int NE,
const Array<double> &B,
const Vector &D,
Vector &Y)
{
if (dim == 2)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPAMassAssembleDiagonal2D<2,2,16>(NE,B,D,Y);
case 0x33: return SmemPAMassAssembleDiagonal2D<3,3,16>(NE,B,D,Y);
case 0x44: return SmemPAMassAssembleDiagonal2D<4,4,8>(NE,B,D,Y);
case 0x55: return SmemPAMassAssembleDiagonal2D<5,5,8>(NE,B,D,Y);
case 0x66: return SmemPAMassAssembleDiagonal2D<6,6,4>(NE,B,D,Y);
case 0x77: return SmemPAMassAssembleDiagonal2D<7,7,4>(NE,B,D,Y);
case 0x88: return SmemPAMassAssembleDiagonal2D<8,8,2>(NE,B,D,Y);
case 0x99: return SmemPAMassAssembleDiagonal2D<9,9,2>(NE,B,D,Y);
default: return PAMassAssembleDiagonal2D(NE,B,D,Y,D1D,Q1D);
}
}
else if (dim == 3)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x23: return SmemPAMassAssembleDiagonal3D<2,3>(NE,B,D,Y);
case 0x24: return SmemPAMassAssembleDiagonal3D<2,4>(NE,B,D,Y);
case 0x26: return SmemPAMassAssembleDiagonal3D<2,6>(NE,B,D,Y);
case 0x34: return SmemPAMassAssembleDiagonal3D<3,4>(NE,B,D,Y);
case 0x35: return SmemPAMassAssembleDiagonal3D<3,5>(NE,B,D,Y);
case 0x45: return SmemPAMassAssembleDiagonal3D<4,5>(NE,B,D,Y);
case 0x48: return SmemPAMassAssembleDiagonal3D<4,8>(NE,B,D,Y);
case 0x56: return SmemPAMassAssembleDiagonal3D<5,6>(NE,B,D,Y);
case 0x67: return SmemPAMassAssembleDiagonal3D<6,7>(NE,B,D,Y);
case 0x78: return SmemPAMassAssembleDiagonal3D<7,8>(NE,B,D,Y);
case 0x89: return SmemPAMassAssembleDiagonal3D<8,9>(NE,B,D,Y);
default: return PAMassAssembleDiagonal3D(NE,B,D,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
void MassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
PAMassAssembleDiagonal(dim, dofs1D, quad1D, ne, maps->B, pa_data, diag);
}
}
#ifdef MFEM_USE_OCCA
// OCCA PA Mass Apply 2D kernel
static void OccaPAMassApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &D,
const Vector &X,
Vector &Y)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_D = OccaMemoryRead(D.GetMemory(), D.Size());
const occa::memory o_X = OccaMemoryRead(X.GetMemory(), X.Size());
occa::memory o_Y = OccaMemoryReadWrite(Y.GetMemory(), Y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaMassApply2D_cpu;
if (OccaMassApply2D_cpu.find(id) == OccaMassApply2D_cpu.end())
{
const occa::kernel MassApply2D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply2D_CPU", props);
OccaMassApply2D_cpu.emplace(id, MassApply2D_CPU);
}
OccaMassApply2D_cpu.at(id)(NE, o_B, o_Bt, o_D, o_X, o_Y);
}
else
{
static occa_kernel_t OccaMassApply2D_gpu;
if (OccaMassApply2D_gpu.find(id) == OccaMassApply2D_gpu.end())
{
const occa::kernel MassApply2D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply2D_GPU", props);
OccaMassApply2D_gpu.emplace(id, MassApply2D_GPU);
}
OccaMassApply2D_gpu.at(id)(NE, o_B, o_Bt, o_D, o_X, o_Y);
}
}
// OCCA PA Mass Apply 3D kernel
static void OccaPAMassApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &D,
const Vector &X,
Vector &Y)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_D = OccaMemoryRead(D.GetMemory(), D.Size());
const occa::memory o_X = OccaMemoryRead(X.GetMemory(), X.Size());
occa::memory o_Y = OccaMemoryReadWrite(Y.GetMemory(), Y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaMassApply3D_cpu;
if (OccaMassApply3D_cpu.find(id) == OccaMassApply3D_cpu.end())
{
const occa::kernel MassApply3D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply3D_CPU", props);
OccaMassApply3D_cpu.emplace(id, MassApply3D_CPU);
}
OccaMassApply3D_cpu.at(id)(NE, o_B, o_Bt, o_D, o_X, o_Y);
}
else
{
static occa_kernel_t OccaMassApply3D_gpu;
if (OccaMassApply3D_gpu.find(id) == OccaMassApply3D_gpu.end())
{
const occa::kernel MassApply3D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply3D_GPU", props);
OccaMassApply3D_gpu.emplace(id, MassApply3D_GPU);
}
OccaMassApply3D_gpu.at(id)(NE, o_B, o_Bt, o_D, o_X, o_Y);
}
}
#endif // MFEM_USE_OCCA
template<int T_D1D = 0, int T_Q1D = 0>
static void PAMassApply2D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_VERIFY(T_D1D ? T_D1D : d1d <= MAX_D1D, "");
MFEM_VERIFY(T_Q1D ? T_Q1D : q1d <= MAX_Q1D, "");
const auto B = b_.Read();
const auto Bt = bt_.Read();
const auto D = d_.Read();
const auto X = x_.Read();
auto Y = y_.ReadWrite();
MFEM_FORALL(e, NE,
{
internal::PAMassApply2D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
});
}
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void SmemPAMassApply2D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_CONTRACT_VAR(bt_);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
const auto b = b_.Read();
const auto D = d_.Read();
const auto x = x_.Read();
auto Y = y_.ReadWrite();
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
internal::SmemPAMassApply2D_Element<T_D1D,T_Q1D,T_NBZ>(e, NE, b, D, x, Y, d1d, q1d);
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAMassApply3D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_VERIFY(T_D1D ? T_D1D : d1d <= MAX_D1D, "");
MFEM_VERIFY(T_Q1D ? T_Q1D : q1d <= MAX_Q1D, "");
const auto B = b_.Read();
const auto Bt = bt_.Read();
const auto D = d_.Read();
const auto X = x_.Read();
auto Y = y_.ReadWrite();
MFEM_FORALL(e, NE,
{
internal::PAMassApply3D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void SmemPAMassApply3D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_CONTRACT_VAR(bt_);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int M1Q = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int M1D = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= M1D, "");
MFEM_VERIFY(Q1D <= M1Q, "");
auto b = b_.Read();
auto d = d_.Read();
auto x = x_.Read();
auto y = y_.ReadWrite();
MFEM_FORALL_3D(e, NE, Q1D, Q1D, 1,
{
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
});
}
static void PAMassApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &D,
const Vector &X,
Vector &Y)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
if (dim == 2)
{
return OccaPAMassApply2D(D1D,Q1D,NE,B,Bt,D,X,Y);
}
if (dim == 3)
{
return OccaPAMassApply3D(D1D,Q1D,NE,B,Bt,D,X,Y);
}
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
}
#endif // MFEM_USE_OCCA
const int id = (D1D << 4) | Q1D;
if (dim == 2)
{
switch (id)
{
case 0x22: return SmemPAMassApply2D<2,2,16>(NE,B,Bt,D,X,Y);
case 0x24: return SmemPAMassApply2D<2,4,16>(NE,B,Bt,D,X,Y);
case 0x33: return SmemPAMassApply2D<3,3,16>(NE,B,Bt,D,X,Y);
case 0x34: return SmemPAMassApply2D<3,4,16>(NE,B,Bt,D,X,Y);
case 0x35: return SmemPAMassApply2D<3,5,16>(NE,B,Bt,D,X,Y);
case 0x36: return SmemPAMassApply2D<3,6,16>(NE,B,Bt,D,X,Y);
case 0x44: return SmemPAMassApply2D<4,4,8>(NE,B,Bt,D,X,Y);
case 0x46: return SmemPAMassApply2D<4,6,8>(NE,B,Bt,D,X,Y);
case 0x48: return SmemPAMassApply2D<4,8,4>(NE,B,Bt,D,X,Y);
case 0x55: return SmemPAMassApply2D<5,5,8>(NE,B,Bt,D,X,Y);
case 0x57: return SmemPAMassApply2D<5,7,8>(NE,B,Bt,D,X,Y);
case 0x58: return SmemPAMassApply2D<5,8,2>(NE,B,Bt,D,X,Y);
case 0x66: return SmemPAMassApply2D<6,6,4>(NE,B,Bt,D,X,Y);
case 0x77: return SmemPAMassApply2D<7,7,4>(NE,B,Bt,D,X,Y);
case 0x88: return SmemPAMassApply2D<8,8,2>(NE,B,Bt,D,X,Y);
case 0x99: return SmemPAMassApply2D<9,9,2>(NE,B,Bt,D,X,Y);
default: return PAMassApply2D(NE,B,Bt,D,X,Y,D1D,Q1D);
}
}
else if (dim == 3)
{
switch (id)
{
case 0x22: return SmemPAMassApply3D<2,2>(NE,B,Bt,D,X,Y);
case 0x23: return SmemPAMassApply3D<2,3>(NE,B,Bt,D,X,Y);
case 0x24: return SmemPAMassApply3D<2,4>(NE,B,Bt,D,X,Y);
case 0x26: return SmemPAMassApply3D<2,6>(NE,B,Bt,D,X,Y);
case 0x34: return SmemPAMassApply3D<3,4>(NE,B,Bt,D,X,Y);
case 0x35: return SmemPAMassApply3D<3,5>(NE,B,Bt,D,X,Y);
case 0x36: return SmemPAMassApply3D<3,6>(NE,B,Bt,D,X,Y);
case 0x37: return SmemPAMassApply3D<3,7>(NE,B,Bt,D,X,Y);
case 0x45: return SmemPAMassApply3D<4,5>(NE,B,Bt,D,X,Y);
case 0x46: return SmemPAMassApply3D<4,6>(NE,B,Bt,D,X,Y);
case 0x48: return SmemPAMassApply3D<4,8>(NE,B,Bt,D,X,Y);
case 0x56: return SmemPAMassApply3D<5,6>(NE,B,Bt,D,X,Y);
case 0x58: return SmemPAMassApply3D<5,8>(NE,B,Bt,D,X,Y);
case 0x67: return SmemPAMassApply3D<6,7>(NE,B,Bt,D,X,Y);
case 0x78: return SmemPAMassApply3D<7,8>(NE,B,Bt,D,X,Y);
case 0x89: return SmemPAMassApply3D<8,9>(NE,B,Bt,D,X,Y);
case 0x9A: return SmemPAMassApply3D<9,10>(NE,B,Bt,D,X,Y);
default: return PAMassApply3D(NE,B,Bt,D,X,Y,D1D,Q1D);
}
}
mfem::out << "Unknown kernel 0x" << std::hex << id << std::endl;
MFEM_ABORT("Unknown kernel.");
}
void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
{
ceedOp->AddMult(x, y);
}
else
{
PAMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
}
}
void MassIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
// Mass integrator is symmetric
AddMultPA(x, y);
}
} // namespace mfem
@@ -9,15 +9,12 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_BILININTEG_MASS_KERNELS_HPP
#define MFEM_BILININTEG_MASS_KERNELS_HPP
#ifndef MFEM_BILININTEG_MASS_PA_HPP
#define MFEM_BILININTEG_MASS_PA_HPP
#include "../../config/config.hpp"
#include "../../general/array.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "../config/config.hpp"
#include "../general/forall.hpp"
#include "../linalg/dtensor.hpp"
namespace mfem
{
@@ -25,315 +22,6 @@ namespace mfem
namespace internal
{
void PAMassAssembleDiagonal(const int dim, const int D1D,
const int Q1D, const int NE,
const Array<double> &B,
const Vector &D,
Vector &Y);
// PA Mass Diagonal 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PAMassAssembleDiagonal2D(const int NE,
const Array<double> &b,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto D = Reshape(d.Read(), Q1D, Q1D, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double QD[MQ1][MD1];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
QD[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QD[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
for (int qx = 0; qx < Q1D; ++qx)
{
Y(dx,dy,e) += B(qx, dx) * B(qx, dx) * QD[qx][dy];
}
}
}
});
}
// Shared memory PA Mass Diagonal 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
inline void SmemPAMassAssembleDiagonal2D(const int NE,
const Array<double> &b_,
const Vector &d_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D, Q1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_SHARED double B[MQ1][MD1];
MFEM_SHARED double QDZ[NBZ][MQ1][MD1];
double (*QD)[MD1] = (double (*)[MD1])(QDZ + tidz);
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
QD[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QD[qx][dy] += B[qy][dy] * B[qy][dy] * D(qx, qy, e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
for (int qx = 0; qx < Q1D; ++qx)
{
// might need absolute values on next line
Y(dx,dy,e) += B[qx][dx] * B[qx][dx] * QD[qx][dy];
}
}
}
});
}
// PA Mass Diagonal 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PAMassAssembleDiagonal3D(const int NE,
const Array<double> &b,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto D = Reshape(d.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double QQD[MQ1][MQ1][MD1];
double QDD[MQ1][MD1][MD1];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
QQD[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
QQD[qx][qy][dz] += B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
}
}
}
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
QDD[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QDD[qx][dy][dz] += B(qy, dy) * B(qy, dy) * QQD[qx][qy][dz];
}
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double t = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
t += B(qx, dx) * B(qx, dx) * QDD[qx][dy][dz];
}
Y(dx, dy, dz, e) += t;
}
}
}
});
}
// Shared memory PA Mass Diagonal 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAMassAssembleDiagonal3D(const int NE,
const Array<double> &b_,
const Vector &d_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
MFEM_SHARED double B[MQ1][MD1];
MFEM_SHARED double QQD[MQ1][MQ1][MD1];
MFEM_SHARED double QDD[MQ1][MD1][MD1];
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dz,z,D1D)
{
QQD[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
QQD[qx][qy][dz] += B[qz][dz] * B[qz][dz] * D(qx, qy, qz, e);
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
QDD[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
QDD[qx][dy][dz] += B[qy][dy] * B[qy][dy] * QQD[qx][qy][dz];
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double t = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
t += B[qx][dx] * B[qx][dx] * QDD[qx][dy][dz];
}
Y(dx, dy, dz, e) += t;
}
}
}
});
}
void PAMassApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &D,
const Vector &X,
Vector &Y);
#ifdef MFEM_USE_OCCA
// OCCA PA Mass Apply 2D kernel
void OccaPAMassApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &D,
const Vector &X,
Vector &Y);
// OCCA PA Mass Apply 3D kernel
void OccaPAMassApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &D,
const Vector &X,
Vector &Y);
#endif // MFEM_USE_OCCA
template <bool ACCUMULATE = true>
MFEM_HOST_DEVICE inline
void PAMassApply2D_Element(const int e,
@@ -937,116 +625,6 @@ void SmemPAMassApply3D_Element(const int e,
MFEM_SYNC_THREAD;
}
// PA Mass Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PAMassApply2D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_VERIFY(T_D1D ? T_D1D : d1d <= MAX_D1D, "");
MFEM_VERIFY(T_Q1D ? T_Q1D : q1d <= MAX_Q1D, "");
const auto B = b_.Read();
const auto Bt = bt_.Read();
const auto D = d_.Read();
const auto X = x_.Read();
auto Y = y_.ReadWrite();
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
internal::PAMassApply2D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
});
}
// Shared memory PA Mass Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
inline void SmemPAMassApply2D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_CONTRACT_VAR(bt_);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
const auto b = b_.Read();
const auto D = d_.Read();
const auto x = x_.Read();
auto Y = y_.ReadWrite();
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
{
internal::SmemPAMassApply2D_Element<T_D1D,T_Q1D,T_NBZ>(e, NE, b, D, x, Y, d1d,
q1d);
});
}
// PA Mass Apply 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void PAMassApply3D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_VERIFY(T_D1D ? T_D1D : d1d <= MAX_D1D, "");
MFEM_VERIFY(T_Q1D ? T_Q1D : q1d <= MAX_Q1D, "");
const auto B = b_.Read();
const auto Bt = bt_.Read();
const auto D = d_.Read();
const auto X = x_.Read();
auto Y = y_.ReadWrite();
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
internal::PAMassApply3D_Element(e, NE, B, Bt, D, X, Y, d1d, q1d);
});
}
// Shared memory PA Mass Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAMassApply3D(const int NE,
const Array<double> &b_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
MFEM_CONTRACT_VAR(bt_);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int M1Q = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int M1D = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= M1D, "");
MFEM_VERIFY(Q1D <= M1Q, "");
auto b = b_.Read();
auto d = d_.Read();
auto x = x_.Read();
auto y = y_.ReadWrite();
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
});
}
} // namespace internal
} // namespace mfem
@@ -9,8 +9,8 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
namespace mfem
{
@@ -27,7 +27,7 @@ void TransposeIntegrator::AssembleEA(const FiniteElementSpace &fes,
const int dofs = fes.GetFE(0)->GetDof();
auto A = Reshape(ea_data_tmp.Read(), dofs, dofs, ne);
auto AT = Reshape(ea_data.ReadWrite(), dofs, dofs, ne);
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, ne,
{
for (int i = 0; i < dofs; i++)
{
@@ -46,7 +46,7 @@ void TransposeIntegrator::AssembleEA(const FiniteElementSpace &fes,
if (ne == 0) { return; }
const int dofs = fes.GetFE(0)->GetDof();
auto A = Reshape(ea_data.ReadWrite(), dofs, dofs, ne);
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, ne,
{
for (int i = 0; i < dofs; i++)
{
@@ -80,7 +80,7 @@ void TransposeIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace& fes,
auto A_ext = Reshape(ea_data_ext_tmp.Read(), faceDofs, faceDofs, 2, nf);
auto AT_int = Reshape(ea_data_int.ReadWrite(), faceDofs, faceDofs, 2, nf);
auto AT_ext = Reshape(ea_data_ext.ReadWrite(), faceDofs, faceDofs, 2, nf);
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, nf,
{
for (int i = 0; i < faceDofs; i++)
{
@@ -105,7 +105,7 @@ void TransposeIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace& fes,
fes.GetMesh()->GetFaceGeometry(0))->GetDof();
auto A_int = Reshape(ea_data_int.ReadWrite(), faceDofs, faceDofs, 2, nf);
auto A_ext = Reshape(ea_data_ext.ReadWrite(), faceDofs, faceDofs, 2, nf);
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, nf,
{
for (int i = 0; i < faceDofs; i++)
{
@@ -149,7 +149,7 @@ void TransposeIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace& fes,
fes.GetMesh()->GetFaceGeometry(0))->GetDof();
auto A_bdr = Reshape(ea_data_bdr_tmp.Read(), faceDofs, faceDofs, nf);
auto AT_bdr = Reshape(ea_data_bdr.ReadWrite(), faceDofs, faceDofs, nf);
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, nf,
{
for (int i = 0; i < faceDofs; i++)
{
@@ -167,7 +167,7 @@ void TransposeIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace& fes,
const int faceDofs = fes.GetTraceElement(0,
fes.GetMesh()->GetFaceGeometry(0))->GetDof();
auto A_bdr = Reshape(ea_data_bdr.ReadWrite(), faceDofs, faceDofs, nf);
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
MFEM_FORALL(f, nf,
{
for (int i = 0; i < faceDofs; i++)
{
@@ -9,15 +9,19 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "qfunction.hpp"
#include "ceed/integrators/diffusion/diffusion.hpp"
using namespace std;
namespace mfem
{
// PA Vector Diffusion Integrator
// PA Diffusion Assemble 2D kernel
static void PAVectorDiffusionSetup2D(const int Q1D,
const int NE,
@@ -37,7 +41,7 @@ static void PAVectorDiffusionSetup2D(const int Q1D,
Reshape(c.Read(), NQ, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -73,7 +77,7 @@ static void PAVectorDiffusionSetup3D(const int Q1D,
Reshape(c.Read(), NQ,NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -87,8 +91,8 @@ static void PAVectorDiffusionSetup3D(const int Q1D,
const double J23 = J(q,1,2,e);
const double J33 = J(q,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double C1 = const_c ? C(0,0) : C(q,e);
@@ -193,7 +197,7 @@ void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
const auto C = const_c ? Reshape(coeff.Read(), 1,1) :
Reshape(coeff.Read(), NQ,ne);
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, ne,
{
for (int q = 0; q < NQ; ++q)
{
@@ -222,209 +226,6 @@ void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
}
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAVectorDiffusionDiagonal2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
// note the different shape for D, this is a (symmetric) matrix so we only
// store necessary entries
auto D = Reshape(d.Read(), Q1D*Q1D, 3, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
// gradphi \cdot Q \gradphi has four terms
double QD0[MQ1][MD1];
double QD1[MQ1][MD1];
double QD2[MQ1][MD1];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
QD0[qx][dy] = 0.0;
QD1[qx][dy] = 0.0;
QD2[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
const int q = qx + qy * Q1D;
const double D0 = D(q,0,e);
const double D1 = D(q,1,e);
const double D2 = D(q,2,e);
QD0[qx][dy] += B(qy, dy) * B(qy, dy) * D0;
QD1[qx][dy] += B(qy, dy) * G(qy, dy) * D1;
QD2[qx][dy] += G(qy, dy) * G(qy, dy) * D2;
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp += G(qx, dx) * G(qx, dx) * QD0[qx][dy];
temp += G(qx, dx) * B(qx, dx) * QD1[qx][dy];
temp += B(qx, dx) * G(qx, dx) * QD1[qx][dy];
temp += B(qx, dx) * B(qx, dx) * QD2[qx][dy];
}
Y(dx,dy,0,e) += temp;
Y(dx,dy,1,e) += temp;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAVectorDiffusionDiagonal3D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
constexpr int DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, 6, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, 3, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double QQD[MQ1][MQ1][MD1];
double QDD[MQ1][MD1][MD1];
for (int i = 0; i < DIM; ++i)
{
for (int j = 0; j < DIM; ++j)
{
// first tensor contraction, along z direction
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
QQD[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const int k = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const double O = Q(q,k,e);
const double Bz = B(qz,dz);
const double Gz = G(qz,dz);
const double L = i==2 ? Gz : Bz;
const double R = j==2 ? Gz : Bz;
QQD[qx][qy][dz] += L * O * R;
}
}
}
}
// second tensor contraction, along y direction
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
QDD[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
const double By = B(qy,dy);
const double Gy = G(qy,dy);
const double L = i==1 ? Gy : By;
const double R = j==1 ? Gy : By;
QDD[qx][dy][dz] += L * QQD[qx][qy][dz] * R;
}
}
}
}
// third tensor contraction, along x direction
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const double Bx = B(qx,dx);
const double Gx = G(qx,dx);
const double L = i==0 ? Gx : Bx;
const double R = j==0 ? Gx : Bx;
temp += L * QDD[qx][dy][dz] * R;
}
Y(dx, dy, dz, 0, e) += temp;
Y(dx, dy, dz, 1, e) += temp;
Y(dx, dy, dz, 2, e) += temp;
}
}
}
}
}
});
}
static void PAVectorDiffusionAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Vector &op,
Vector &y)
{
if (dim == 2)
{
return PAVectorDiffusionDiagonal2D(NE, B, G, op, y, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorDiffusionDiagonal3D(NE, B, G, op, y, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorDiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
PAVectorDiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne,
maps->B, maps->G,
pa_data, diag);
}
}
// PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_VDIM = 0> static
void PAVectorDiffusionApply2D(const int NE,
@@ -451,7 +252,7 @@ void PAVectorDiffusionApply2D(const int NE,
auto D = Reshape(d_.Read(), Q1D*Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -572,7 +373,7 @@ void PAVectorDiffusionApply3D(const int NE,
auto op = Reshape(op_.Read(), Q1D*Q1D*Q1D, 6, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -780,4 +581,212 @@ void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAVectorDiffusionDiagonal2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
// note the different shape for D, this is a (symmetric) matrix so we only
// store necessary entries
auto D = Reshape(d.Read(), Q1D*Q1D, 3, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, 2, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
// gradphi \cdot Q \gradphi has four terms
double QD0[MQ1][MD1];
double QD1[MQ1][MD1];
double QD2[MQ1][MD1];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
QD0[qx][dy] = 0.0;
QD1[qx][dy] = 0.0;
QD2[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
const int q = qx + qy * Q1D;
const double D0 = D(q,0,e);
const double D1 = D(q,1,e);
const double D2 = D(q,2,e);
QD0[qx][dy] += B(qy, dy) * B(qy, dy) * D0;
QD1[qx][dy] += B(qy, dy) * G(qy, dy) * D1;
QD2[qx][dy] += G(qy, dy) * G(qy, dy) * D2;
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp += G(qx, dx) * G(qx, dx) * QD0[qx][dy];
temp += G(qx, dx) * B(qx, dx) * QD1[qx][dy];
temp += B(qx, dx) * G(qx, dx) * QD1[qx][dy];
temp += B(qx, dx) * B(qx, dx) * QD2[qx][dy];
}
Y(dx,dy,0,e) += temp;
Y(dx,dy,1,e) += temp;
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAVectorDiffusionDiagonal3D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Vector &d,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
constexpr int DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, 6, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, 3, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
double QQD[MQ1][MQ1][MD1];
double QDD[MQ1][MD1][MD1];
for (int i = 0; i < DIM; ++i)
{
for (int j = 0; j < DIM; ++j)
{
// first tensor contraction, along z direction
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
QQD[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const int k = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const double O = Q(q,k,e);
const double Bz = B(qz,dz);
const double Gz = G(qz,dz);
const double L = i==2 ? Gz : Bz;
const double R = j==2 ? Gz : Bz;
QQD[qx][qy][dz] += L * O * R;
}
}
}
}
// second tensor contraction, along y direction
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
QDD[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
const double By = B(qy,dy);
const double Gy = G(qy,dy);
const double L = i==1 ? Gy : By;
const double R = j==1 ? Gy : By;
QDD[qx][dy][dz] += L * QQD[qx][qy][dz] * R;
}
}
}
}
// third tensor contraction, along x direction
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const double Bx = B(qx,dx);
const double Gx = G(qx,dx);
const double L = i==0 ? Gx : Bx;
const double R = j==0 ? Gx : Bx;
temp += L * QDD[qx][dy][dz] * R;
}
Y(dx, dy, dz, 0, e) += temp;
Y(dx, dy, dz, 1, e) += temp;
Y(dx, dy, dz, 2, e) += temp;
}
}
}
}
}
});
}
static void PAVectorDiffusionAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Vector &op,
Vector &y)
{
if (dim == 2)
{
return PAVectorDiffusionDiagonal2D(NE, B, G, op, y, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorDiffusionDiagonal3D(NE, B, G, op, y, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorDiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
PAVectorDiffusionAssembleDiagonal(dim,
dofs1D,
quad1D,
ne,
maps->B,
maps->G,
pa_data,
diag);
}
}
} // namespace mfem
@@ -9,9 +9,12 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "ceed/integrators/diffusion/diffusion.hpp"
using namespace std;
namespace mfem
{
@@ -9,14 +9,19 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../ceed/integrators/mass/mass.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "ceed/integrators/mass/mass.hpp"
using namespace std;
namespace mfem
{
// PA Mass Integrator
// PA Mass Assemble kernel
void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assuming the same element type
@@ -69,7 +74,7 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
auto w = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -90,7 +95,7 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
auto W = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
auto v = Reshape(pa_data.Write(), NQ,NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
@@ -98,177 +103,16 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const double J21 = J(q,1,0,e), J22 = J(q,1,1,e), J23 = J(q,1,2,e);
const double J31 = J(q,2,0,e), J32 = J(q,2,1,e), J33 = J(q,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
v(q,e) = W[q] * constant * detJ;
}
});
}
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal2D(const int NE,
const Array<double> &B_,
const Array<double> &Bt_,
const Vector &op_,
Vector &diag_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, NE);
auto y = Reshape(diag_.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double temp[max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp[qx][dy] += B(qy, dy) * B(qy, dy) * op(qx, qy, e);
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp1 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp1 += B(qx, dx) * B(qx, dx) * temp[qx][dy];
}
y(dx, dy, 0, e) = temp1;
y(dx, dy, 1, e) = temp1;
}
}
});
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal3D(const int NE,
const Array<double> &B_,
const Array<double> &Bt_,
const Vector &op_,
Vector &diag_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 3;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, NE);
auto y = Reshape(diag_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double temp[max_Q1D][max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
temp[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
temp[qx][qy][dz] += B(qz, dz) * B(qz, dz) * op(qx, qy, qz, e);
}
}
}
}
double temp2[max_Q1D][max_D1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp2[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp2[qx][dy][dz] += B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
}
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp3 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp3 += B(qx, dx) * B(qx, dx)
* temp2[qx][dy][dz];
}
y(dx, dy, dz, 0, e) = temp3;
y(dx, dy, dz, 1, e) = temp3;
y(dx, dy, dz, 2, e) = temp3;
}
}
}
});
}
static void PAVectorMassAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &op,
Vector &y)
{
if (dim == 2)
{
return PAVectorMassAssembleDiagonal2D(NE, B, Bt, op, y, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorMassAssembleDiagonal3D(NE, B, Bt, op, y, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
PAVectorMassAssembleDiagonal(dim, dofs1D, quad1D, ne,
maps->B, maps->Bt,
pa_data, diag);
}
}
template<const int T_D1D = 0, const int T_Q1D = 0>
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void PAVectorMassApply2D(const int NE,
const Array<double> &B_,
const Array<double> &Bt_,
@@ -288,7 +132,7 @@ static void PAVectorMassApply2D(const int NE,
auto op = Reshape(op_.Read(), Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -364,7 +208,8 @@ static void PAVectorMassApply2D(const int NE,
});
}
template<const int T_D1D = 0, const int T_Q1D = 0>
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void PAVectorMassApply3D(const int NE,
const Array<double> &B_,
const Array<double> &Bt_,
@@ -384,7 +229,7 @@ static void PAVectorMassApply3D(const int NE,
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -542,4 +387,171 @@ void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal2D(const int NE,
const Array<double> &B_,
const Array<double> &Bt_,
const Vector &op_,
Vector &diag_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, NE);
auto y = Reshape(diag_.ReadWrite(), D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double temp[max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp[qx][dy] += B(qy, dy) * B(qy, dy) * op(qx, qy, e);
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp1 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp1 += B(qx, dx) * B(qx, dx) * temp[qx][dy];
}
y(dx, dy, 0, e) = temp1;
y(dx, dy, 1, e) = temp1;
}
}
});
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal3D(const int NE,
const Array<double> &B_,
const Array<double> &Bt_,
const Vector &op_,
Vector &diag_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 3;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, NE);
auto y = Reshape(diag_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double temp[max_Q1D][max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
temp[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
temp[qx][qy][dz] += B(qz, dz) * B(qz, dz) * op(qx, qy, qz, e);
}
}
}
}
double temp2[max_Q1D][max_D1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp2[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp2[qx][dy][dz] += B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
}
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
double temp3 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp3 += B(qx, dx) * B(qx, dx)
* temp2[qx][dy][dz];
}
y(dx, dy, dz, 0, e) = temp3;
y(dx, dy, dz, 1, e) = temp3;
y(dx, dy, dz, 2, e) = temp3;
}
}
}
});
}
static void PAVectorMassAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &op,
Vector &y)
{
if (dim == 2)
{
return PAVectorMassAssembleDiagonal2D(NE, B, Bt, op, y, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorMassAssembleDiagonal3D(NE, B, Bt, op, y, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
PAVectorMassAssembleDiagonal(dim,
dofs1D,
quad1D,
ne,
maps->B,
maps->Bt,
pa_data,
diag);
}
}
} // namespace mfem
@@ -9,13 +9,19 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../ceed/integrators/mass/mass.hpp"
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "ceed/integrators/mass/mass.hpp"
using namespace std;
namespace mfem
{
// MF Mass Integrator
// MF Mass Assemble kernel
void VectorMassIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
// Assuming the same element type
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -288,7 +288,7 @@ void InitCoefficientWithIndices(mfem::Coefficient *Q, mfem::Mesh &mesh,
auto in = Reshape(qFun.Read(), nq, ne);
auto d_indices = Read(m_indices, nelem);
auto out = Reshape(ceedCoeff->coeff.Write(), nq, nelem);
mfem::forall(nelem * nq, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, nelem * nq,
{
const int q = i%nq;
const int sub_e = i/nq;
@@ -378,7 +378,7 @@ void InitCoefficientWithIndices(mfem::VectorCoefficient *VQ, mfem::Mesh &mesh,
auto in = Reshape(qFun.Read(), dim, nq, ne);
auto d_indices = Read(m_indices, nelem);
auto out = Reshape(ceedCoeff->coeff.Write(), dim, nq, nelem);
mfem::forall(nelem * nq, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, nelem * nq,
{
const int q = i%nq;
const int sub_e = i/nq;
+2 -2
View File
@@ -519,7 +519,7 @@ int CeedVectorPointwiseMult(CeedVector a, const CeedVector b)
ierr = CeedVectorGetArray(a, mem, &a_data); CeedChk(ierr);
ierr = CeedVectorGetArrayRead(b, mem, &b_data); CeedChk(ierr);
MFEM_VERIFY(int(length) == length, "length overflow");
mfem::forall(length, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, length,
{a_data[i] *= b_data[i];});
ierr = CeedVectorRestoreArray(a, &a_data); CeedChk(ierr);
@@ -593,7 +593,7 @@ void AlgebraicInterpolation::MultTranspose(const mfem::Vector& x,
&multiplicitydata); PCeedChk(ierr);
ierr = CeedVectorGetArrayWrite(fine_work, mem, &workdata); PCeedChk(ierr);
MFEM_VERIFY((int)length == length, "length overflow");
mfem::forall(length, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, length,
{workdata[i] = in_ptr[i] * multiplicitydata[i];});
ierr = CeedVectorRestoreArrayRead(fine_multiplicity_r,
&multiplicitydata);
+5 -55
View File
@@ -144,54 +144,11 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
}
}
double CartesianCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
T.Transform(ip, transip);
return transip[comp];
}
double CylindricalRadialCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
T.Transform(ip, transip);
return sqrt(transip[0] * transip[0] + transip[1] * transip[1]);
}
double CylindricalAzimuthalCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
T.Transform(ip, transip);
return atan2(transip[1], transip[0]);
}
double SphericalRadialCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
T.Transform(ip, transip);
return sqrt(transip * transip);
}
double SphericalAzimuthalCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
T.Transform(ip, transip);
return atan2(transip[1], transip[0]);
}
double SphericalPolarCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
T.Transform(ip, transip);
return atan2(sqrt(transip[0] * transip[0] + transip[1] * transip[1]),
transip[2]);
}
double GridFunctionCoefficient::Eval (ElementTransformation &T,
const IntegrationPoint &ip)
{
Mesh *gf_mesh = GridF->FESpace()->GetMesh();
if (T.mesh->GetNE() == gf_mesh->GetNE())
if (T.mesh == gf_mesh)
{
return GridF->GetValue(T, ip, Component);
}
@@ -356,13 +313,6 @@ void PWVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
V = 0.0;
}
void PositionVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
T.Transform(ip, V);
}
void VectorFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -446,7 +396,7 @@ void VectorGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
Mesh *gf_mesh = GridFunc->FESpace()->GetMesh();
if (T.mesh->GetNE() == gf_mesh->GetNE())
if (T.mesh == gf_mesh)
{
GridFunc->GetVectorValue(T, ip, V);
}
@@ -494,7 +444,7 @@ void GradientGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
Mesh *gf_mesh = GridFunc->FESpace()->GetMesh();
if (T.mesh->GetNE() == gf_mesh->GetNE())
if (T.mesh == gf_mesh)
{
GridFunc->GetGradient(T, V);
}
@@ -535,7 +485,7 @@ void CurlGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
Mesh *gf_mesh = GridFunc->FESpace()->GetMesh();
if (T.mesh->GetNE() == gf_mesh->GetNE())
if (T.mesh == gf_mesh)
{
GridFunc->GetCurl(T, V);
}
@@ -557,7 +507,7 @@ double DivergenceGridFunctionCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
Mesh *gf_mesh = GridFunc->FESpace()->GetMesh();
if (T.mesh->GetNE() == gf_mesh->GetNE())
if (T.mesh == gf_mesh)
{
return GridFunc->GetDivergence(T);
}
-134
View File
@@ -258,124 +258,6 @@ public:
const IntegrationPoint &ip);
};
/// A common base class for returning individual components of the domain's
/// Cartesian coordinates.
class CartesianCoefficient : public Coefficient
{
protected:
int comp;
mutable Vector transip;
/// @a comp_ index of the desired component (0 -> x, 1 -> y, 2 -> z)
CartesianCoefficient(int comp_) : comp(comp_), transip(3) {}
public:
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the x-component of the evaluation point
class CartesianXCoefficient : public CartesianCoefficient
{
public:
CartesianXCoefficient() : CartesianCoefficient(0) {}
};
/// Scalar coefficient which returns the y-component of the evaluation point
class CartesianYCoefficient : public CartesianCoefficient
{
public:
CartesianYCoefficient() : CartesianCoefficient(1) {}
};
/// Scalar coefficient which returns the z-component of the evaluation point
class CartesianZCoefficient : public CartesianCoefficient
{
public:
CartesianZCoefficient() : CartesianCoefficient(2) {}
};
/// Scalar coefficient which returns the radial distance from the axis of
/// the evaluation point in the cylindrical coordinate system
class CylindricalRadialCoefficient : public Coefficient
{
private:
mutable Vector transip;
public:
CylindricalRadialCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the angular position or azimuth (often
/// denoted by theta) of the evaluation point in the cylindrical coordinate
/// system
class CylindricalAzimuthalCoefficient : public Coefficient
{
private:
mutable Vector transip;
public:
CylindricalAzimuthalCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the height or altitude of
/// the evaluation point in the cylindrical coordinate system
typedef CartesianZCoefficient CylindricalZCoefficient;
/// Scalar coefficient which returns the radial distance from the origin of
/// the evaluation point in the spherical coordinate system
class SphericalRadialCoefficient : public Coefficient
{
private:
mutable Vector transip;
public:
SphericalRadialCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the azimuthal angle (often denoted by phi)
/// of the evaluation point in the spherical coordinate system
class SphericalAzimuthalCoefficient : public Coefficient
{
private:
mutable Vector transip;
public:
SphericalAzimuthalCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the polar angle (often denoted by theta)
/// of the evaluation point in the spherical coordinate system
class SphericalPolarCoefficient : public Coefficient
{
private:
mutable Vector transip;
public:
SphericalPolarCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
class GridFunction;
/// Coefficient defined by a GridFunction. This coefficient is mesh dependent.
@@ -718,22 +600,6 @@ public:
using VectorCoefficient::Eval;
};
/// A vector coefficient which returns the physical location of the
/// evaluation point in the Cartesian coordinate system.
class PositionVectorCoefficient : public VectorCoefficient
{
public:
PositionVectorCoefficient(int dim) : VectorCoefficient(dim) {}
using VectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~PositionVectorCoefficient() { }
};
/// A general vector function coefficient
class VectorFunctionCoefficient : public VectorCoefficient
{
+2 -2
View File
@@ -497,7 +497,7 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
auto d_X_r = X_r.Read();
auto d_X_i = X_i.Read();
auto d_idx = ess_tdof_list.Read();
mfem::forall(n, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, n,
{
const int j = d_idx[i];
d_B_r[j] = d_X_r[j];
@@ -1230,7 +1230,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
auto d_X_r = X_r.Read();
auto d_X_i = X_i.Read();
auto d_idx = ess_tdof_list.Read();
mfem::forall(n, [=] MFEM_HOST_DEVICE (int i)
MFEM_FORALL(i, n,
{
const int j = d_idx[i];
d_B_r[j] = d_X_r[j];
+2 -2
View File
@@ -107,7 +107,7 @@ void DGMassInverse::Update()
{
M->Assemble();
M->AssembleDiagonal(diag_inv);
diag_inv.Reciprocal();
internal::MakeReciprocal(diag_inv.Size(), diag_inv.ReadWrite());
}
DGMassInverse::~DGMassInverse()
@@ -168,7 +168,7 @@ void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
constexpr int NB = Q1D ? Q1D : 1; // block size
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
MFEM_FORALL_2D(e, NE, NB, NB, 1,
{
constexpr int NB = Q1D ? Q1D : 1; // redefine here for some compilers
+2 -4
View File
@@ -87,8 +87,6 @@ public:
///
/// If @ref iterative_mode is @a true, @a u is used as an initial guess.
void Mult(const Vector &b, Vector &u) const;
/// Same as Mult() since the mass matrix is symmetric.
void MultTranspose(const Vector &b, Vector &u) const { Mult(b, u); }
/// Not implemented. Aborts.
void SetOperator(const Operator &op);
/// Set the relative tolerance.
@@ -103,8 +101,8 @@ public:
~DGMassInverse();
/// @brief Solve the system M b = u. <b>Not part of the public interface.</b>
/// @note This member function must be public because it defines an
/// extended lambda used in an mfem::forall kernel (nvcc limitation)
/// @note This member function must be public because it contains an
/// MFEM_FORALL kernel (nvcc limitation)
template<int DIM, int D1D = 0, int Q1D = 0>
void DGMassCGIteration(const Vector &b_, Vector &u_) const;
};
+6 -1
View File
@@ -12,9 +12,9 @@
#ifndef MFEM_DGMASSINV_KERNELS_HPP
#define MFEM_DGMASSINV_KERNELS_HPP
#include "bilininteg_mass_pa.hpp"
#include "../linalg/kernels.hpp"
#include "kernels.hpp"
#include "integ/bilininteg_mass_kernels.hpp"
namespace mfem
{
@@ -22,6 +22,11 @@ namespace mfem
namespace internal
{
void MakeReciprocal(int n, double *x)
{
MFEM_FORALL(i, n, x[i] = 1.0/x[i]; );
}
template <int DIM, int D1D, int Q1D>
MFEM_HOST_DEVICE inline
void DGMassApply(const int e,
+3 -8
View File
@@ -401,7 +401,7 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
}
}
}
else if (range_type == VECTOR)
else
{
d2q->B.SetSize(nqpt*dim*dof);
d2q->Bt.SetSize(dof*nqpt*dim);
@@ -419,10 +419,6 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
}
}
}
else
{
// Skip B and Bt for unknown range type
}
switch (deriv_type)
{
case GRAD:
@@ -476,7 +472,7 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
{
for (int j = 0; j < dof; j++)
{
d2q->G[i+nqpt*(d+cdim*j)] = d2q->Gt[j+dof*(i+nqpt*d)] = curlshape(j, d);
d2q->G[i+nqpt*(d+dim*j)] = d2q->Gt[j+dof*(i+nqpt*d)] = curlshape(j, d);
}
}
}
@@ -484,8 +480,7 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
}
case NONE:
default:
// Skip G and Gt for unknown derivative type
break;
MFEM_ABORT("invalid finite element derivative type");
}
dof2quad_array.Append(d2q);
return *d2q;
+3 -3
View File
@@ -1288,9 +1288,9 @@ public:
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const override
{
return (mode == DofToQuad::FULL) ?
FiniteElement::GetDofToQuad(ir, mode) :
GetTensorDofToQuad(*this, ir, mode, basis1d, true, dof2quad_array);
MFEM_VERIFY(mode != DofToQuad::FULL, "invalid mode requested");
return GetTensorDofToQuad(*this, ir, mode, basis1d, true,
dof2quad_array);
}
const DofToQuad &GetDofToQuadOpen(const IntegrationRule &ir,
-21
View File
@@ -385,38 +385,21 @@ void FiniteElementSpace::BuildBdrElementToDofTable() const
if (bdr_elem_dof) { return; }
Table *bel_dof = new Table;
Table *bel_fos = (mesh->Dimension() == 3) ? (new Table) : NULL;
Array<int> dofs;
int F, Fo;
bel_dof->MakeI(mesh->GetNBE());
if (bel_fos) { bel_fos->MakeI(mesh->GetNBE()); }
for (int i = 0; i < mesh->GetNBE(); i++)
{
GetBdrElementDofs(i, dofs);
bel_dof->AddColumnsInRow(i, dofs.Size());
if (bel_fos)
{
bel_fos->AddAColumnInRow(i);
}
}
bel_dof->MakeJ();
if (bel_fos) { bel_fos->MakeJ(); }
for (int i = 0; i < mesh->GetNBE(); i++)
{
GetBdrElementDofs(i, dofs);
bel_dof->AddConnections(i, (int *)dofs, dofs.Size());
if (bel_fos)
{
mesh->GetBdrElementFace(i, &F, &Fo);
bel_fos->AddConnection(i, Fo);
}
}
bel_dof->ShiftUpI();
if (bel_fos) { bel_fos->ShiftUpI(); }
bdr_elem_dof = bel_dof;
bdr_elem_fos = bel_fos;
}
void FiniteElementSpace::BuildFaceToDofTable() const
@@ -1559,10 +1542,6 @@ FiniteElementSpace::RefinementOperator::~RefinementOperator()
{
delete old_elem_dof;
delete old_elem_fos;
for (int i=0; i<old_DoFTrans.Size(); i++)
{
delete old_DoFTrans[i];
}
}
void FiniteElementSpace::RefinementOperator
+1 -3
View File
@@ -994,9 +994,7 @@ public:
/// @brief Returns indices of degrees of freedom for the @a i'th element.
/// The returned indices are offsets into an @ref ldof vector with @b vdim
/// not necessarily equal to 1. The returned indexes are always ordered
/// byNODES, irrespective of whether the space is byNODES or byVDIM.
/// See also GetElementDofs().
/// not necessarily equal to 1. See also GetElementDofs().
///
/// @note In many cases the returned DofTransformation object will be NULL.
/// In other cases see the documentation of the DofTransformation class for
+10 -28
View File
@@ -397,6 +397,8 @@ void GridFunction::GetNodalValues(int i, Array<double> &nval, int vdim) const
{
Array<int> vdofs;
int k;
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
const FiniteElement *FElem = fes->GetFE(i);
const IntegrationRule *ElemVert =
@@ -417,7 +419,7 @@ void GridFunction::GetNodalValues(int i, Array<double> &nval, int vdim) const
Vector shape(dof);
if (FElem->GetMapType() == FiniteElement::VALUE)
{
for (int k = 0; k < n; k++)
for (k = 0; k < n; k++)
{
FElem->CalcShape(ElemVert->IntPoint(k), shape);
nval[k] = shape * (&loc_data[dof * vdim]);
@@ -426,7 +428,7 @@ void GridFunction::GetNodalValues(int i, Array<double> &nval, int vdim) const
else
{
ElementTransformation *Tr = fes->GetElementTransformation(i);
for (int k = 0; k < n; k++)
for (k = 0; k < n; k++)
{
Tr->SetIntPoint(&ElemVert->IntPoint(k));
FElem->CalcPhysShape(*Tr, shape);
@@ -438,7 +440,7 @@ void GridFunction::GetNodalValues(int i, Array<double> &nval, int vdim) const
{
ElementTransformation *Tr = fes->GetElementTransformation(i);
DenseMatrix vshape(dof, FElem->GetDim());
for (int k = 0; k < n; k++)
for (k = 0; k < n; k++)
{
Tr->SetIntPoint(&ElemVert->IntPoint(k));
FElem->CalcVShape(*Tr, vshape);
@@ -2399,11 +2401,7 @@ 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);
SetSubVector(vdofs, vals);
loc_mass_vals.SetSize(vals.Size());
loc_mass.Mult(vals, loc_mass_vals);
@@ -2757,11 +2755,7 @@ 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);
SetSubVector(dofs, lvec);
}
#endif
@@ -4037,19 +4031,11 @@ double 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);
ffes->GetElementVDofs(i, fdofs);
u.GetSubVector(udofs, ul);
flux.GetSubVector(fdofs, fla);
if (utrans)
{
utrans->InvTransformPrimal(ul);
}
if (ftrans)
{
ftrans->InvTransformPrimal(fla);
}
Transf = ufes->GetElementTransformation(i);
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
@@ -4344,12 +4330,8 @@ double LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
flux_order));
int num_integration_pts = ir->GetNPoints();
const DofTransformation* const utrans = ufes->GetElementVDofs(ielem, udofs);
ufes->GetElementVDofs(ielem, udofs);
u.GetSubVector(udofs, ul);
if (utrans)
{
utrans->InvTransformPrimal(ul);
}
Transf = ufes->GetElementTransformation(ielem);
FiniteElement *dummy = nullptr;
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
-4
View File
@@ -684,10 +684,6 @@ public:
/// Transform by the Space UpdateMatrix (e.g., on Mesh change).
virtual void Update();
/** Return update counter, similar to Mesh::GetSequence(). Used to
check if it is up to date with the space. */
long GetSequence() const { return fes_sequence; }
FiniteElementSpace *FESpace() { return fes; }
const FiniteElementSpace *FESpace() const { return fes; }
+29 -63
View File
@@ -108,7 +108,8 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
const int npt_max)
{
MFEM_VERIFY(m.GetNodes() != NULL, "Mesh nodes are required.");
const int meshOrder = m.GetNodes()->FESpace()->GetMaxElementOrder();
MFEM_VERIFY(!(m.GetNodes()->FESpace()->IsVariableOrder()),
"Variable order mesh is not currently supported.");
// call FreeData if FindPointsGSLIB::Setup has been called already
if (setupflag) { FreeData(); }
@@ -116,36 +117,30 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
crystal_init(cr, gsl_comm);
mesh = &m;
dim = mesh->Dimension();
unsigned dof1D = meshOrder + 1;
const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(0);
unsigned dof1D = fe->GetOrder() + 1;
SetupSplitMeshes();
if (dim == 2)
{
if (ir_split[0]) { delete ir_split[0]; ir_split[0] = NULL; }
ir_split[0] = new IntegrationRule(3*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_split[0], meshOrder);
if (ir_split[1]) { delete ir_split[1]; ir_split[1] = NULL; }
ir_split[1] = new IntegrationRule(pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_split[1], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_split[0], fe->GetOrder());
}
else if (dim == 3)
{
if (ir_split[0]) { delete ir_split[0]; ir_split[0] = NULL; }
ir_split[0] = new IntegrationRule(pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_split[0], meshOrder);
if (ir_split[1]) { delete ir_split[1]; ir_split[1] = NULL; }
ir_split[1] = new IntegrationRule(4*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_split[1], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_split[1], fe->GetOrder());
if (ir_split[2]) { delete ir_split[2]; ir_split[2] = NULL; }
ir_split[2] = new IntegrationRule(3*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[2], ir_split[2], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[2], ir_split[2], fe->GetOrder());
if (ir_split[3]) { delete ir_split[3]; ir_split[3] = NULL; }
ir_split[3] = new IntegrationRule(8*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[3], ir_split[3], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[3], ir_split[3], fe->GetOrder());
}
GetNodalValues(mesh->GetNodes(), gsl_mesh);
@@ -184,7 +179,7 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
gsl_ref.SetSize(points_cnt * dim);
gsl_dist.SetSize(points_cnt);
auto xvFill = [&](const double *xv_base[], unsigned xv_stride[])
auto xvFill = [&](const double *xv_base[], unsigned xv_stride[], int dim)
{
for (int d = 0; d < dim; d++)
{
@@ -204,7 +199,7 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
{
const double *xv_base[2];
unsigned xv_stride[2];
xvFill(xv_base, xv_stride);
xvFill(xv_base, xv_stride, dim);
findpts_2(gsl_code.GetData(), sizeof(unsigned int),
gsl_proc.GetData(), sizeof(unsigned int),
gsl_elem.GetData(), sizeof(unsigned int),
@@ -216,7 +211,7 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
{
const double *xv_base[3];
unsigned xv_stride[3];
xvFill(xv_base, xv_stride);
xvFill(xv_base, xv_stride, dim);
findpts_3(gsl_code.GetData(), sizeof(unsigned int),
gsl_proc.GetData(), sizeof(unsigned int),
gsl_elem.GetData(), sizeof(unsigned int),
@@ -338,14 +333,9 @@ void FindPointsGSLIB::SetupSplitMeshes()
(*gf_rst_map[0])(j+k*npt) = quad_v[j][k];
}
}
mesh_split[1] = new Mesh(Mesh::MakeCartesian2D(1, 1,
Element::QUADRILATERAL));
}
else if (mesh->Dimension() == 3)
{
mesh_split[0] = new Mesh(Mesh::MakeCartesian3D(1, 1, 1,
Element::HEXAHEDRON));
// Tetrahedron
{
int Nvert = 15;
@@ -575,12 +565,11 @@ void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
const GridFunction *nodes = gf_in;
const FiniteElementSpace *fes = nodes->FESpace();
const int NE = mesh->GetNE();
const int vdim = fes->GetVDim();
const int vdim = gf_in->FESpace()->GetVDim();
IntegrationRule *ir_split_temp = NULL;
const int maxOrder = fes->GetMaxElementOrder();
const int dof_1D = maxOrder+1;
const int dof_1D = nodes->FESpace()->GetFE(0)->GetOrder()+1;
const int pts_el = std::pow(dof_1D, dim);
const int pts_cnt = NE_split_total * pts_el;
node_vals.SetSize(vdim * pts_cnt);
@@ -590,7 +579,7 @@ void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
for (int e = 0; e < NE; e++)
{
const FiniteElement *fe = fes->GetFE(e);
const FiniteElement *fe = nodes->FESpace()->GetFE(e);
const Geometry::Type gt = fe->GetGeomType();
bool el_to_split = true;
if (gt == Geometry::TRIANGLE)
@@ -609,22 +598,16 @@ void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
{
ir_split_temp = ir_split[3];
}
else if (gt == Geometry::SQUARE)
else if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
{
ir_split_temp = ir_split[1];
el_to_split = gf_in->FESpace()->IsVariableOrder();
}
else if (gt == Geometry::CUBE)
{
ir_split_temp = ir_split[0];
el_to_split = gf_in->FESpace()->IsVariableOrder();
el_to_split = false;
}
else
{
MFEM_ABORT("Unsupported geometry type.");
}
if (el_to_split) // Triangle/Tet/Prism or Quads/Hex but variable order
if (el_to_split) // Triangle/Tet/Prism
{
// Fill gsl_mesh with location of split points.
Vector locval(vdim);
@@ -639,7 +622,7 @@ void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
gsl_mesh_pt_index++;
}
}
else // Quad/Hex and constant polynomial order
else // Quad/Hex
{
const int dof_cnt_split = fe->GetDof();
@@ -820,8 +803,8 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
Vector &field_out)
{
const int gf_order = field_in.FESpace()->GetMaxElementOrder(),
mesh_order = mesh->GetNodalFESpace()->GetMaxElementOrder();
const int gf_order = field_in.FESpace()->GetFE(0)->GetOrder(),
mesh_order = mesh->GetNodalFESpace()->GetFE(0)->GetOrder();
const FiniteElementCollection *fec_in = field_in.FESpace()->FEColl();
const H1_FECollection *fec_h1 = dynamic_cast<const H1_FECollection *>(fec_in);
@@ -829,8 +812,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
if (fec_h1 && gf_order == mesh_order &&
fec_h1->GetBasisType() == BasisType::GaussLobatto &&
field_in.FESpace()->IsVariableOrder() ==
mesh->GetNodalFESpace()->IsVariableOrder())
!field_in.FESpace()->IsVariableOrder())
{
InterpolateH1(field_in, field_out);
return;
@@ -904,21 +886,12 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
Vector &field_out)
{
FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
if (field_in.FESpace()->IsVariableOrder())
{
for (int e = 0; e < ind_fes.GetMesh()->GetNE(); e++)
{
ind_fes.SetElementOrder(e, field_in.FESpace()->GetElementOrder(e));
}
ind_fes.Update(false);
}
GridFunction field_in_scalar(&ind_fes);
Vector node_vals;
const int ncomp = field_in.FESpace()->GetVDim(),
points_fld = field_in.Size() / ncomp;
MFEM_VERIFY(points_cnt == gsl_code.Size(),
"FindPointsGSLIB::InterpolateH1: Inconsistent size of gsl_code");
points_fld = field_in.Size() / ncomp,
points_cnt = gsl_code.Size();
field_out.SetSize(points_cnt*ncomp);
field_out = default_interp_value;
@@ -1138,7 +1111,8 @@ void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
const int npt_max)
{
MFEM_VERIFY(m.GetNodes() != NULL, "Mesh nodes are required.");
const int meshOrder = m.GetNodes()->FESpace()->GetMaxElementOrder();
MFEM_VERIFY(!(m.GetNodes()->FESpace()->IsVariableOrder()),
"Variable order mesh is not currently supported.");
// FreeData if OversetFindPointsGSLIB::Setup has been called already
if (setupflag) { FreeData(); }
@@ -1154,29 +1128,21 @@ void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
{
if (ir_split[0]) { delete ir_split[0]; ir_split[0] = NULL; }
ir_split[0] = new IntegrationRule(3*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_split[0], meshOrder);
if (ir_split[1]) { delete ir_split[1]; ir_split[1] = NULL; }
ir_split[1] = new IntegrationRule(pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_split[1], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_split[0], fe->GetOrder());
}
else if (dim == 3)
{
if (ir_split[0]) { delete ir_split[0]; ir_split[0] = NULL; }
ir_split[0] = new IntegrationRule(pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_split[0], meshOrder);
if (ir_split[1]) { delete ir_split[1]; ir_split[1] = NULL; }
ir_split[1] = new IntegrationRule(4*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_split[1], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_split[1], fe->GetOrder());
if (ir_split[2]) { delete ir_split[2]; ir_split[2] = NULL; }
ir_split[2] = new IntegrationRule(3*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[2], ir_split[2], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[2], ir_split[2], fe->GetOrder());
if (ir_split[3]) { delete ir_split[3]; ir_split[3] = NULL; }
ir_split[3] = new IntegrationRule(8*pow(dof1D, dim));
SetupIntegrationRuleForSplitMesh(mesh_split[3], ir_split[3], meshOrder);
SetupIntegrationRuleForSplitMesh(mesh_split[3], ir_split[3], fe->GetOrder());
}
GetNodalValues(mesh->GetNodes(), gsl_mesh);
+1 -3
View File
@@ -57,9 +57,7 @@ public:
protected:
Mesh *mesh;
Array<Mesh *> mesh_split; // Meshes used to split simplices.
// IntegrationRules for simplex->Quad/Hex and to project to highest polynomial
// order in-case of p-refinement.
Array<IntegrationRule *> ir_split;
Array<IntegrationRule *> ir_split; // IntegrationRules for simplex->Quad/Hex
Array<FiniteElementSpace *>
fes_rst_map; // FESpaces to map info Quad/Hex->Simplex
Array<GridFunction *> gf_rst_map; // GridFunctions to map info Quad/Hex->Simplex
-205
View File
@@ -1,205 +0,0 @@
// Copyright (c) 2010-2023, 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 "../qfunction.hpp"
#include "bilininteg_hcurl_kernels.hpp"
namespace mfem
{
void CurlCurlIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement *fel = fes.GetFE(0);
const VectorTensorFiniteElement *el =
dynamic_cast<const VectorTensorFiniteElement*>(fel);
MFEM_VERIFY(el != NULL, "Only VectorTensorFiniteElement is supported!");
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(*el, *el,
*mesh->GetElementTransformation(0));
const int dims = el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
quad1D = mapsC->nqpt;
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(qs, CoefficientStorage::SYMMETRIC);
if (Q) { coeff.Project(*Q); }
else if (MQ) { coeff.ProjectTranspose(*MQ); }
else if (DQ) { coeff.Project(*DQ); }
else { coeff.SetConstant(1.0); }
const int coeff_dim = coeff.GetVDim();
symmetric = (coeff_dim != dim*dim);
const int sym_dims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int ndata = (dim == 2) ? 1 : (symmetric ? sym_dims : dim*dim);
pa_data.SetSize(ndata * nq * ne, Device::GetMemoryType());
if (el->GetDerivType() != mfem::FiniteElement::CURL)
{
MFEM_ABORT("Unknown kernel.");
}
if (dim == 3)
{
internal::PACurlCurlSetup3D(quad1D, coeff_dim, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else
{
internal::PACurlCurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J, coeff,
pa_data);
}
}
void CurlCurlIntegrator::AssembleDiagonalPA(Vector& diag)
{
if (dim == 3)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
const int ID = (dofs1D << 4) | quad1D;
switch (ID)
{
case 0x23:
return internal::SmemPACurlCurlAssembleDiagonal3D<2,3>(
dofs1D,
quad1D,
symmetric, ne,
mapsO->B, mapsC->B,
mapsO->G, mapsC->G,
pa_data, diag);
case 0x34:
return internal::SmemPACurlCurlAssembleDiagonal3D<3,4>(
dofs1D,
quad1D,
symmetric, ne,
mapsO->B, mapsC->B,
mapsO->G, mapsC->G,
pa_data, diag);
case 0x45:
return internal::SmemPACurlCurlAssembleDiagonal3D<4,5>(
dofs1D,
quad1D,
symmetric, ne,
mapsO->B, mapsC->B,
mapsO->G, mapsC->G,
pa_data, diag);
case 0x56:
return internal::SmemPACurlCurlAssembleDiagonal3D<5,6>(
dofs1D,
quad1D,
symmetric, ne,
mapsO->B, mapsC->B,
mapsO->G, mapsC->G,
pa_data, diag);
default:
return internal::SmemPACurlCurlAssembleDiagonal3D(
dofs1D, quad1D,
symmetric, ne,
mapsO->B, mapsC->B,
mapsO->G, mapsC->G,
pa_data, diag);
}
}
else
{
internal::PACurlCurlAssembleDiagonal3D(dofs1D, quad1D, symmetric, ne,
mapsO->B, mapsC->B,
mapsO->G, mapsC->G,
pa_data, diag);
}
}
else if (dim == 2)
{
internal::PACurlCurlAssembleDiagonal2D(dofs1D, quad1D, ne,
mapsO->B, mapsC->G, pa_data, diag);
}
else
{
MFEM_ABORT("Unsupported dimension!");
}
}
void CurlCurlIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (dim == 3)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
const int ID = (dofs1D << 4) | quad1D;
switch (ID)
{
case 0x23:
return internal::SmemPACurlCurlApply3D<2,3>(
dofs1D, quad1D,
symmetric, ne,
mapsO->B, mapsC->B, mapsO->Bt, mapsC->Bt,
mapsC->G, mapsC->Gt, pa_data, x, y);
case 0x34:
return internal::SmemPACurlCurlApply3D<3,4>(
dofs1D, quad1D,
symmetric, ne,
mapsO->B, mapsC->B, mapsO->Bt, mapsC->Bt,
mapsC->G, mapsC->Gt, pa_data, x, y);
case 0x45:
return internal::SmemPACurlCurlApply3D<4,5>(
dofs1D, quad1D,
symmetric, ne,
mapsO->B, mapsC->B, mapsO->Bt, mapsC->Bt,
mapsC->G, mapsC->Gt, pa_data, x, y);
case 0x56:
return internal::SmemPACurlCurlApply3D<5,6>(
dofs1D, quad1D,
symmetric, ne,
mapsO->B, mapsC->B, mapsO->Bt, mapsC->Bt,
mapsC->G, mapsC->Gt, pa_data, x, y);
default:
return internal::SmemPACurlCurlApply3D(
dofs1D, quad1D, symmetric, ne,
mapsO->B, mapsC->B, mapsO->Bt, mapsC->Bt,
mapsC->G, mapsC->Gt, pa_data, x, y);
}
}
else
{
internal::PACurlCurlApply3D(dofs1D, quad1D, symmetric, ne, mapsO->B, mapsC->B,
mapsO->Bt, mapsC->Bt, mapsC->G, mapsC->Gt,
pa_data, x, y);
}
}
else if (dim == 2)
{
internal::PACurlCurlApply2D(dofs1D, quad1D, ne, mapsO->B, mapsO->Bt,
mapsC->G, mapsC->Gt, pa_data, x, y);
}
else
{
MFEM_ABORT("Unsupported dimension!");
}
}
} // namespace mfem
-578
View File
@@ -1,578 +0,0 @@
// Copyright (c) 2010-2023, 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 "bilininteg_diffusion_kernels.hpp"
namespace mfem
{
namespace internal
{
template<>
void PADiffusionSetup2D<2>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
template<>
void PADiffusionSetup2D<3>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
void PADiffusionSetup(const int dim,
const int sdim,
const int D1D,
const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &D)
{
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PADiffusionSetup"); }
if (dim == 2)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, C, D);
return;
}
#else
MFEM_CONTRACT_VAR(D1D);
#endif // MFEM_USE_OCCA
if (sdim == 2) { PADiffusionSetup2D<2>(Q1D, coeffDim, NE, W, J, C, D); }
if (sdim == 3) { PADiffusionSetup2D<3>(Q1D, coeffDim, NE, W, J, C, D); }
}
if (dim == 3)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup3D(D1D, Q1D, NE, W, J, C, D);
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup3D(Q1D, coeffDim, NE, W, J, C, D);
}
}
template<>
void PADiffusionSetup2D<2>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
const bool symmetric = (coeffDim != 4);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 3 ||
!const_c, "Constant matrix coefficient not supported");
const auto W = Reshape(w.Read(), Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,2,2,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, symmetric ? 3 : 4, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
const double J11 = J(qx,qy,0,0,e);
const double J21 = J(qx,qy,1,0,e);
const double J12 = J(qx,qy,0,1,e);
const double J22 = J(qx,qy,1,1,e);
const double w_detJ = W(qx,qy) / ((J11*J22)-(J21*J12));
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient
{
// First compute entries of R = MJ^{-T}, without det J factor.
const double M11 = C(0,qx,qy,e);
const double M12 = C(1,qx,qy,e);
const double M21 = symmetric ? M12 : C(2,qx,qy,e);
const double M22 = symmetric ? C(2,qx,qy,e) : C(3,qx,qy,e);
const double R11 = M11*J22 - M12*J12;
const double R21 = M21*J22 - M22*J12;
const double R12 = -M11*J21 + M12*J11;
const double R22 = -M21*J21 + M22*J11;
// Now set y to J^{-1}R.
D(qx,qy,0,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
D(qx,qy,1,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
D(qx,qy,2,e) = w_detJ * (symmetric ? (-J21*R12 + J11*R22) :
(J22*R12 - J12*R22)); // 2,2 or 1,2
if (!symmetric)
{
D(qx,qy,3,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
}
else // Vector or scalar coefficient
{
const double C1 = const_c ? C(0,0,0,0) : C(0,qx,qy,e);
const double C2 = const_c ? C(0,0,0,0) :
(coeffDim == 2 ? C(1,qx,qy,e) : C(0,qx,qy,e));
D(qx,qy,0,e) = w_detJ * (C2*J12*J12 + C1*J22*J22); // 1,1
D(qx,qy,1,e) = -w_detJ * (C2*J12*J11 + C1*J22*J21); // 1,2
D(qx,qy,2,e) = w_detJ * (C2*J11*J11 + C1*J21*J21); // 2,2
}
}
}
});
}
template<>
void PADiffusionSetup2D<3>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
MFEM_VERIFY(coeffDim == 1, "Matrix and vector coefficients not supported");
constexpr int DIM = 2;
constexpr int SDIM = 3;
const bool const_c = c.Size() == 1;
const auto W = Reshape(w.Read(), Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,SDIM,DIM,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1) :
Reshape(c.Read(), Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, 3, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
const double wq = W(qx,qy);
const double J11 = J(qx,qy,0,0,e);
const double J21 = J(qx,qy,1,0,e);
const double J31 = J(qx,qy,2,0,e);
const double J12 = J(qx,qy,0,1,e);
const double J22 = J(qx,qy,1,1,e);
const double J32 = J(qx,qy,2,1,e);
const double E = J11*J11 + J21*J21 + J31*J31;
const double G = J12*J12 + J22*J22 + J32*J32;
const double F = J11*J12 + J21*J22 + J31*J32;
const double iw = 1.0 / sqrt(E*G - F*F);
const double coeff = const_c ? C(0,0,0) : C(qx,qy,e);
const double alpha = wq * coeff * iw;
D(qx,qy,0,e) = alpha * G; // 1,1
D(qx,qy,1,e) = -alpha * F; // 1,2
D(qx,qy,2,e) = alpha * E; // 2,2
}
}
});
}
void PADiffusionSetup3D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
const bool symmetric = (coeffDim != 9);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 6 ||
!const_c, "Constant matrix coefficient not supported");
const auto W = Reshape(w.Read(), Q1D,Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,Q1D,3,3,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D,Q1D, symmetric ? 6 : 9, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
const double J11 = J(qx,qy,qz,0,0,e);
const double J21 = J(qx,qy,qz,1,0,e);
const double J31 = J(qx,qy,qz,2,0,e);
const double J12 = J(qx,qy,qz,0,1,e);
const double J22 = J(qx,qy,qz,1,1,e);
const double J32 = J(qx,qy,qz,2,1,e);
const double J13 = J(qx,qy,qz,0,2,e);
const double J23 = J(qx,qy,qz,1,2,e);
const double J33 = J(qx,qy,qz,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
const double w_detJ = W(qx,qy,qz) / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
const double A13 = (J12 * J23) - (J22 * J13);
const double A21 = (J31 * J23) - (J21 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J21 * J13) - (J11 * J23);
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
{
// Compute entries of R = MJ^{-T} = M adj(J)^T, without det J.
const double M11 = C(0, qx,qy,qz, e);
const double M12 = C(1, qx,qy,qz, e);
const double M13 = C(2, qx,qy,qz, e);
const double M21 = (!symmetric) ? C(3, qx,qy,qz, e) : M12;
const double M22 = (!symmetric) ? C(4, qx,qy,qz, e) : C(3, qx,qy,qz, e);
const double M23 = (!symmetric) ? C(5, qx,qy,qz, e) : C(4, qx,qy,qz, e);
const double M31 = (!symmetric) ? C(6, qx,qy,qz, e) : M13;
const double M32 = (!symmetric) ? C(7, qx,qy,qz, e) : M23;
const double M33 = (!symmetric) ? C(8, qx,qy,qz, e) : C(5, qx,qy,qz, e);
const double R11 = M11*A11 + M12*A12 + M13*A13;
const double R12 = M11*A21 + M12*A22 + M13*A23;
const double R13 = M11*A31 + M12*A32 + M13*A33;
const double R21 = M21*A11 + M22*A12 + M23*A13;
const double R22 = M21*A21 + M22*A22 + M23*A23;
const double R23 = M21*A31 + M22*A32 + M23*A33;
const double R31 = M31*A11 + M32*A12 + M33*A13;
const double R32 = M31*A21 + M32*A22 + M33*A23;
const double R33 = M31*A31 + M32*A32 + M33*A33;
// Now set D to J^{-1} R = adj(J) R
D(qx,qy,qz,0,e) = w_detJ * (A11*R11 + A12*R21 + A13*R31); // 1,1
const double D12 = w_detJ * (A11*R12 + A12*R22 + A13*R32);
D(qx,qy,qz,1,e) = D12; // 1,2
D(qx,qy,qz,2,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
const double D22 = w_detJ * (A21*R12 + A22*R22 + A23*R32);
const double D23 = w_detJ * (A21*R13 + A22*R23 + A23*R33);
const double D33 = w_detJ * (A31*R13 + A32*R23 + A33*R33);
D(qx,qy,qz,4,e) = symmetric ? D23 : D22; // 2,3 or 2,2
D(qx,qy,qz,5,e) = symmetric ? D33 : D23; // 3,3 or 2,3
if (symmetric)
{
D(qx,qy,qz,3,e) = D22; // 2,2
}
else
{
D(qx,qy,qz,3,e) = w_detJ * (A21*R11 + A22*R21 + A23*R31); // 2,1
D(qx,qy,qz,6,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
D(qx,qy,qz,7,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
D(qx,qy,qz,8,e) = D33; // 3,3
}
}
else // Vector or scalar coefficient version
{
const double C1 = const_c ? C(0,0,0,0,0) : C(0,qx,qy,qz,e);
const double C2 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(1,qx,qy,qz,e) : C(0,qx,qy,qz,e));
const double C3 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(2,qx,qy,qz,e) : C(0,qx,qy,qz,e));
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
D(qx,qy,qz,0,e) = w_detJ * (C1*A11*A11 + C2*A12*A12 + C3*A13*A13); // 1,1
D(qx,qy,qz,1,e) = w_detJ * (C1*A11*A21 + C2*A12*A22 + C3*A13*A23); // 2,1
D(qx,qy,qz,2,e) = w_detJ * (C1*A11*A31 + C2*A12*A32 + C3*A13*A33); // 3,1
D(qx,qy,qz,3,e) = w_detJ * (C1*A21*A21 + C2*A22*A22 + C3*A23*A23); // 2,2
D(qx,qy,qz,4,e) = w_detJ * (C1*A21*A31 + C2*A22*A32 + C3*A23*A33); // 3,2
D(qx,qy,qz,5,e) = w_detJ * (C1*A31*A31 + C2*A32*A32 + C3*A33*A33); // 3,3
}
}
}
}
});
}
#ifdef MFEM_USE_OCCA
void OccaPADiffusionSetup2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &op)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup2D_ker;
if (OccaDiffSetup2D_ker.find(id) == OccaDiffSetup2D_ker.end())
{
const occa::kernel DiffusionSetup2D =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionSetup2D", props);
OccaDiffSetup2D_ker.emplace(id, DiffusionSetup2D);
}
OccaDiffSetup2D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
}
void OccaPADiffusionSetup3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &op)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup3D_ker;
if (OccaDiffSetup3D_ker.find(id) == OccaDiffSetup3D_ker.end())
{
const occa::kernel DiffusionSetup3D =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionSetup3D", props);
OccaDiffSetup3D_ker.emplace(id, DiffusionSetup3D);
}
OccaDiffSetup3D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
}
#endif // MFEM_USE_OCCA
void PADiffusionAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Vector &D,
Vector &Y)
{
if (dim == 2)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionDiagonal2D<2,2,8>(NE,symm,B,G,D,Y);
case 0x33: return SmemPADiffusionDiagonal2D<3,3,8>(NE,symm,B,G,D,Y);
case 0x44: return SmemPADiffusionDiagonal2D<4,4,4>(NE,symm,B,G,D,Y);
case 0x55: return SmemPADiffusionDiagonal2D<5,5,4>(NE,symm,B,G,D,Y);
case 0x66: return SmemPADiffusionDiagonal2D<6,6,2>(NE,symm,B,G,D,Y);
case 0x77: return SmemPADiffusionDiagonal2D<7,7,2>(NE,symm,B,G,D,Y);
case 0x88: return SmemPADiffusionDiagonal2D<8,8,1>(NE,symm,B,G,D,Y);
case 0x99: return SmemPADiffusionDiagonal2D<9,9,1>(NE,symm,B,G,D,Y);
default: return PADiffusionDiagonal2D(NE,symm,B,G,D,Y,D1D,Q1D);
}
}
else if (dim == 3)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionDiagonal3D<2,2>(NE,symm,B,G,D,Y);
case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,symm,B,G,D,Y);
case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,symm,B,G,D,Y);
case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,symm,B,G,D,Y);
case 0x46: return SmemPADiffusionDiagonal3D<4,6>(NE,symm,B,G,D,Y);
case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,symm,B,G,D,Y);
case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,symm,B,G,D,Y);
case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,symm,B,G,D,Y);
case 0x89: return SmemPADiffusionDiagonal3D<8,9>(NE,symm,B,G,D,Y);
case 0x9A: return SmemPADiffusionDiagonal3D<9,10>(NE,symm,B,G,D,Y);
default: return PADiffusionDiagonal3D(NE,symm,B,G,D,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
void PADiffusionApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
if (dim == 2)
{
OccaPADiffusionApply2D(D1D,Q1D,NE,B,G,Bt,Gt,D,X,Y);
return;
}
if (dim == 3)
{
OccaPADiffusionApply3D(D1D,Q1D,NE,B,G,Bt,Gt,D,X,Y);
return;
}
MFEM_ABORT("OCCA PADiffusionApply unknown kernel!");
}
#endif // MFEM_USE_OCCA
const int id = (D1D << 4) | Q1D;
if (dim == 2)
{
switch (id)
{
case 0x22: return SmemPADiffusionApply2D<2,2,16>(NE,symm,B,G,D,X,Y);
case 0x33: return SmemPADiffusionApply2D<3,3,16>(NE,symm,B,G,D,X,Y);
case 0x44: return SmemPADiffusionApply2D<4,4,8>(NE,symm,B,G,D,X,Y);
case 0x55: return SmemPADiffusionApply2D<5,5,8>(NE,symm,B,G,D,X,Y);
case 0x66: return SmemPADiffusionApply2D<6,6,4>(NE,symm,B,G,D,X,Y);
case 0x77: return SmemPADiffusionApply2D<7,7,4>(NE,symm,B,G,D,X,Y);
case 0x88: return SmemPADiffusionApply2D<8,8,2>(NE,symm,B,G,D,X,Y);
case 0x99: return SmemPADiffusionApply2D<9,9,2>(NE,symm,B,G,D,X,Y);
default: return PADiffusionApply2D(NE,symm,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
if (dim == 3)
{
switch (id)
{
case 0x22: return SmemPADiffusionApply3D<2,2>(NE,symm,B,G,D,X,Y);
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,symm,B,G,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,symm,B,G,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,symm,B,G,D,X,Y);
case 0x46: return SmemPADiffusionApply3D<4,6>(NE,symm,B,G,D,X,Y);
case 0x56: return SmemPADiffusionApply3D<5,6>(NE,symm,B,G,D,X,Y);
case 0x58: return SmemPADiffusionApply3D<5,8>(NE,symm,B,G,D,X,Y);
case 0x67: return SmemPADiffusionApply3D<6,7>(NE,symm,B,G,D,X,Y);
case 0x78: return SmemPADiffusionApply3D<7,8>(NE,symm,B,G,D,X,Y);
case 0x89: return SmemPADiffusionApply3D<8,9>(NE,symm,B,G,D,X,Y);
default: return PADiffusionApply3D(NE,symm,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel: 0x"<<std::hex << id << std::dec);
}
#ifdef MFEM_USE_OCCA
void OccaPADiffusionApply2D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_G = OccaMemoryRead(G.GetMemory(), G.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_Gt = OccaMemoryRead(Gt.GetMemory(), Gt.Size());
const occa::memory o_D = OccaMemoryRead(D.GetMemory(), D.Size());
const occa::memory o_X = OccaMemoryRead(X.GetMemory(), X.Size());
occa::memory o_Y = OccaMemoryReadWrite(Y.GetMemory(), Y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaDiffApply2D_cpu;
if (OccaDiffApply2D_cpu.find(id) == OccaDiffApply2D_cpu.end())
{
const occa::kernel DiffusionApply2D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply2D_CPU", props);
OccaDiffApply2D_cpu.emplace(id, DiffusionApply2D_CPU);
}
OccaDiffApply2D_cpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
else
{
static occa_kernel_t OccaDiffApply2D_gpu;
if (OccaDiffApply2D_gpu.find(id) == OccaDiffApply2D_gpu.end())
{
const occa::kernel DiffusionApply2D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply2D_GPU", props);
OccaDiffApply2D_gpu.emplace(id, DiffusionApply2D_GPU);
}
OccaDiffApply2D_gpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
}
void OccaPADiffusionApply3D(const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &D,
const Vector &X,
Vector &Y)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_G = OccaMemoryRead(G.GetMemory(), G.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_Gt = OccaMemoryRead(Gt.GetMemory(), Gt.Size());
const occa::memory o_D = OccaMemoryRead(D.GetMemory(), D.Size());
const occa::memory o_X = OccaMemoryRead(X.GetMemory(), X.Size());
occa::memory o_Y = OccaMemoryReadWrite(Y.GetMemory(), Y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaDiffApply3D_cpu;
if (OccaDiffApply3D_cpu.find(id) == OccaDiffApply3D_cpu.end())
{
const occa::kernel DiffusionApply3D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply3D_CPU", props);
OccaDiffApply3D_cpu.emplace(id, DiffusionApply3D_CPU);
}
OccaDiffApply3D_cpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
else
{
static occa_kernel_t OccaDiffApply3D_gpu;
if (OccaDiffApply3D_gpu.find(id) == OccaDiffApply3D_gpu.end())
{
const occa::kernel DiffusionApply3D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"DiffusionApply3D_GPU", props);
OccaDiffApply3D_gpu.emplace(id, DiffusionApply3D_GPU);
}
OccaDiffApply3D_gpu.at(id)(NE, o_B, o_G, o_Bt, o_Gt, o_D, o_X, o_Y);
}
}
#endif // MFEM_USE_OCCA
} // namespace internal
} // namespace mfem
-118
View File
@@ -1,118 +0,0 @@
// Copyright (c) 2010-2023, 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 "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "bilininteg_diffusion_kernels.hpp"
namespace mfem
{
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
// Assuming the same element type
fespace = &fes;
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
if (DeviceCanUseCeed())
{
delete ceedOp;
MFEM_VERIFY(!VQ && !MQ,
"Only scalar coefficient supported for DiffusionIntegrator"
" with libCEED");
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
}
else
{
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
}
return;
}
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
const int sdim = mesh->SpaceDimension();
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(qs, CoefficientStorage::COMPRESSED);
if (MQ) { coeff.ProjectTranspose(*MQ); }
else if (VQ) { coeff.Project(*VQ); }
else if (Q) { coeff.Project(*Q); }
else { coeff.SetConstant(1.0); }
const int coeff_dim = coeff.GetVDim();
symmetric = (coeff_dim != dims*dims);
const int pa_size = symmetric ? symmDims : dims*dims;
pa_data.SetSize(pa_size * nq * ne, mt);
internal::PADiffusionSetup(dim, sdim, dofs1D, quad1D, coeff_dim, ne,
ir->GetWeights(), geom->J, coeff, pa_data);
}
void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
else
{
if (pa_data.Size()==0) { AssemblePA(*fespace); }
internal::PADiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne, symmetric,
maps->B, maps->G, pa_data, diag);
}
}
void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
{
ceedOp->AddMult(x, y);
}
else
{
internal::PADiffusionApply(dim, dofs1D, quad1D, ne, symmetric,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
}
}
void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
if (symmetric)
{
AddMultPA(x, y);
}
else
{
MFEM_ABORT("DiffusionIntegrator::AddMultTransposePA only implemented in "
"the symmetric case.")
}
}
} // namespace mfem

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