Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4adfa0fc86 | ||
|
|
857a24f6c4 | ||
|
|
1ae22c7c69 | ||
|
|
161ebff2a1 | ||
|
|
3548f2cb83 | ||
|
|
41a7730048 | ||
|
|
b638fb8960 | ||
|
|
dc80f42710 | ||
|
|
4414a3fc01 | ||
|
|
2f683f80fa | ||
|
|
6ea2f7bf55 | ||
|
|
581cafa7a7 | ||
|
|
c5bab73f9a | ||
|
|
b02eb71967 | ||
|
|
d236571e4a | ||
|
|
8d444d7f92 | ||
|
|
71937096f8 | ||
|
|
4e0978cf3b | ||
|
|
e52fdd205a |
+8
-10
@@ -15,10 +15,8 @@ install:
|
||||
- msmpisdk.msi /passive
|
||||
- set PATH=C:\Program Files\Microsoft MPI\Bin;%PATH%
|
||||
|
||||
# Install METIS, use a mirror because the original source server is not always
|
||||
# up. Original url:
|
||||
# http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-5.1.0.tar.gz
|
||||
- ps: Start-FileDownload 'https://mfem.github.io/tpls/metis-5.1.0.tar.gz'
|
||||
# Install METIS
|
||||
- ps: Start-FileDownload 'http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-5.1.0.tar.gz'
|
||||
- 7z x metis-5.1.0.tar.gz -so | 7z x -si -ttar > nul
|
||||
- cd metis-5.1.0
|
||||
- ps: ( get-content "GKlib\gk_arch.h") | % { If ($_.ReadCount -ge 52) {$_ -replace "#ifdef __MSC__","#ifdef DISABLE_THIS_ANCIENT_MSC_CHECK"} Else {$_} } | set-content "GKlib\gk_arch.h"
|
||||
@@ -28,17 +26,17 @@ install:
|
||||
- cd ..
|
||||
|
||||
# Install hypre
|
||||
- ps: Start-FileDownload 'https://github.com/hypre-space/hypre/archive/v2.19.0.tar.gz'
|
||||
- 7z x v2.19.0.tar.gz -so | 7z x -si -ttar > nul
|
||||
- cd hypre-2.19.0/src
|
||||
- cmake -H. -Bbuild -DMPI_C_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DMPI_C_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include"
|
||||
- ps: Start-FileDownload 'https://github.com/hypre-space/hypre/archive/V2-10-0b.tar.gz'
|
||||
- 7z x V2-10-0b.tar.gz -so | 7z x -si -ttar > nul
|
||||
- cd hypre-2-10-0b
|
||||
- cmake -H. -Bbuild -DHYPRE_USING_FEI=OFF -DMPI_C_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DMPI_C_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include"
|
||||
- cmake --build build
|
||||
- cmake --build build --target install
|
||||
- cd ../..
|
||||
- cd ..
|
||||
|
||||
# MFEM
|
||||
before_build:
|
||||
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_parallel -DMFEM_USE_MPI=TRUE -DMFEM_USE_METIS_5=TRUE -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DHYPRE_DIR=%cd%\hypre-2.19.0\src\hypre -DMETIS_LIBRARIES=%cd%\metis-5.1.0\build\libmetis\Debug\metis.lib -DMETIS_INCLUDE_DIRS=%cd%\metis-5.1.0\include
|
||||
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_parallel -DMFEM_USE_MPI=TRUE -DMFEM_USE_METIS_5=TRUE -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DHYPRE_LIBRARIES=%cd%\hypre-2-10-0b\hypre\lib\HYPRE.lib -DHYPRE_INCLUDE_DIRS=%cd%\hypre-2-10-0b\hypre\include -DHYPRE_VERSION=21000 -DMETIS_LIBRARIES=%cd%\metis-5.1.0\build\libmetis\Debug\metis.lib -DMETIS_INCLUDE_DIRS=%cd%\metis-5.1.0\include
|
||||
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_serial -DMFEM_USE_MPI=FALSE
|
||||
|
||||
build_script:
|
||||
|
||||
+1
-17
@@ -29,8 +29,6 @@ config/sample-runs-build.log
|
||||
doc/CodeDocumentation.conf
|
||||
doc/CodeDocumentation.html
|
||||
doc/CodeDocumentation
|
||||
doc/undoc.log
|
||||
doc/warnings.log
|
||||
|
||||
# Temporary files created by the tests.
|
||||
*.stderr
|
||||
@@ -122,7 +120,7 @@ examples/sundials/ex16-final.*
|
||||
examples/sundials/Example16*
|
||||
|
||||
examples/petsc/ex[1-69]p
|
||||
examples/petsc/ex1[0-1]p
|
||||
examples/petsc/ex10p
|
||||
|
||||
examples/petsc/mesh.*
|
||||
examples/petsc/sol.*
|
||||
@@ -137,7 +135,6 @@ examples/petsc/Example9*
|
||||
examples/petsc/deformed.*
|
||||
examples/petsc/velocity.*
|
||||
examples/petsc/elastic_energy.*
|
||||
examples/petsc/mode_*
|
||||
|
||||
examples/pumi/ex1
|
||||
examples/pumi/ex[126]p
|
||||
@@ -170,12 +167,10 @@ miniapps/meshing/twist
|
||||
miniapps/meshing/mesh-explorer
|
||||
miniapps/meshing/shaper
|
||||
miniapps/meshing/extruder
|
||||
miniapps/meshing/trimmer
|
||||
miniapps/meshing/mesh-optimizer
|
||||
miniapps/meshing/pmesh-optimizer
|
||||
miniapps/meshing/minimal-surface
|
||||
miniapps/meshing/pminimal-surface
|
||||
miniapps/meshing/polar-nc
|
||||
|
||||
miniapps/meshing/mobius-strip.mesh
|
||||
miniapps/meshing/klein-bottle.mesh
|
||||
@@ -185,10 +180,8 @@ miniapps/meshing/mesh-explorer.mesh
|
||||
miniapps/meshing/partitioning.txt
|
||||
miniapps/meshing/shaper.mesh
|
||||
miniapps/meshing/extruder.mesh
|
||||
miniapps/meshing/trimmer.mesh
|
||||
miniapps/meshing/optimized*
|
||||
miniapps/meshing/perturbed*
|
||||
miniapps/meshing/polar-nc.mesh
|
||||
|
||||
miniapps/performance/ex1
|
||||
miniapps/performance/ex1p
|
||||
@@ -235,7 +228,6 @@ miniapps/nurbs/mode_*
|
||||
miniapps/nurbs/Example1*
|
||||
|
||||
miniapps/gslib/field-diff
|
||||
miniapps/gslib/field-interp
|
||||
miniapps/gslib/findpts
|
||||
miniapps/gslib/pfindpts
|
||||
|
||||
@@ -247,9 +239,6 @@ miniapps/navier/navier_3dfoc
|
||||
miniapps/navier/tgv_out*.txt
|
||||
miniapps/navier/*_output
|
||||
|
||||
miniapps/adjoint/cvsRoberts_ASAi_dns
|
||||
miniapps/adjoint/adjoint_advection_diffusion
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
@@ -262,10 +251,5 @@ tests/scripts/*.err
|
||||
tests/scripts/*.out
|
||||
tests/scripts/*.msg
|
||||
|
||||
# Other tests
|
||||
tests/convergence/rates
|
||||
tests/convergence/prates
|
||||
tests/par-mesh-format/ex1p
|
||||
|
||||
# VPATH builds
|
||||
build-*/*
|
||||
|
||||
+1
-17
@@ -71,8 +71,6 @@ stages:
|
||||
- build
|
||||
- test
|
||||
- deallocate
|
||||
- lassen_build
|
||||
- lassen_test
|
||||
- baseline_check
|
||||
- baseline_publish
|
||||
|
||||
@@ -81,11 +79,7 @@ stages:
|
||||
# TODO: updating tests and tpls is not necessary anymore since pipelines are
|
||||
# now using unique directories so repo are never shared with another pipeline.
|
||||
# This is not memory efficient (we keep a lot of data), hence this reminder.
|
||||
# Setup
|
||||
setup:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
.setup:
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
@@ -106,15 +100,6 @@ setup:
|
||||
before_script:
|
||||
- module load gcc/6.1.0
|
||||
|
||||
# On lassen
|
||||
.with_gcc_8_3_1:
|
||||
variables:
|
||||
TOOLCHAIN: gcc_8_3_1
|
||||
CXX: g++
|
||||
CC: gcc
|
||||
before_script:
|
||||
- module load gcc/8.3.1
|
||||
|
||||
.with_gcc_4_9_3:
|
||||
variables:
|
||||
TOOLCHAIN: gcc_4_9_3
|
||||
@@ -305,4 +290,3 @@ setup:
|
||||
# The list on jobs is defined in machine-specific files.
|
||||
include:
|
||||
- local: .gitlab/quartz.yml
|
||||
- local: .gitlab/lassen.yml
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# Copyright (c) 2010-2020, 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.
|
||||
|
||||
# GitLab pipelines configurations for the Lassen machine at LLNL
|
||||
|
||||
.on_lassen:
|
||||
tags:
|
||||
- shell
|
||||
- lassen
|
||||
variables:
|
||||
PLAT: lassen
|
||||
|
||||
# Build MFEM
|
||||
build_mfem_ser_lassen:
|
||||
extends: [.with_gcc_8_3_1, .on_lassen]
|
||||
needs: [setup]
|
||||
stage: lassen_build
|
||||
script:
|
||||
- mkdir -p ${BUILD_PATH}
|
||||
- cp -r ${CI_PROJECT_DIR} ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser
|
||||
- cd ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser
|
||||
- lalloc 1 -W 5 -q pdebug make -j cuda CUDA_ARCH=sm_70
|
||||
|
||||
build_mfem_debug_ser_lassen:
|
||||
extends: [.with_gcc_8_3_1, .on_lassen]
|
||||
needs: [setup]
|
||||
stage: lassen_build
|
||||
script:
|
||||
- mkdir -p ${BUILD_PATH}
|
||||
- cp -r ${CI_PROJECT_DIR} ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser_debug
|
||||
- cd ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser_debug
|
||||
- lalloc 1 -W 5 -q pdebug make -j cuda MFEM_DEBUG="YES" CUDA_ARCH=sm_70
|
||||
|
||||
# Sanity check
|
||||
sanitycheck_mfem_ser_lassen:
|
||||
extends: [.with_gcc_8_3_1, .on_lassen]
|
||||
stage: lassen_test
|
||||
needs: [build_mfem_ser_lassen]
|
||||
script:
|
||||
- cd ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser
|
||||
- lalloc 1 -W 15 -q pdebug make -j test
|
||||
|
||||
sanitycheck_mfem_debug_ser_lassen:
|
||||
extends: [.with_gcc_8_3_1, .on_lassen]
|
||||
stage: lassen_test
|
||||
needs: [build_mfem_debug_ser_lassen]
|
||||
script:
|
||||
- cd ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser_debug
|
||||
- lalloc 1 -W 30 -q pdebug make -j test
|
||||
@@ -22,6 +22,10 @@
|
||||
MAKE_PAR: 6
|
||||
BASELINE_PAR: 18
|
||||
|
||||
# Setup
|
||||
setup_quartz:
|
||||
extends: [.setup, .on_quartz]
|
||||
|
||||
# Allocate
|
||||
allocate_quartz:
|
||||
variables:
|
||||
|
||||
+37
-99
@@ -11,20 +11,13 @@
|
||||
|
||||
language: cpp
|
||||
|
||||
os: linux
|
||||
dist: bionic
|
||||
sudo: false
|
||||
|
||||
stages:
|
||||
- checks
|
||||
- tests
|
||||
- optional
|
||||
|
||||
env:
|
||||
global:
|
||||
- HYPRE_ARCHIVE=v2.19.0.tar.gz
|
||||
HYPRE_URL=https://github.com/hypre-space/hypre/archive/$HYPRE_ARCHIVE
|
||||
HYPRE_TOP_DIR=hypre-2.19.0
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
@@ -37,7 +30,6 @@ jobs:
|
||||
|
||||
- stage: checks
|
||||
os: linux
|
||||
dist: xenial
|
||||
name: "code-style"
|
||||
addons:
|
||||
apt:
|
||||
@@ -56,6 +48,9 @@ jobs:
|
||||
packages:
|
||||
- doxygen
|
||||
- graphviz
|
||||
- mpich
|
||||
- libmpich-dev
|
||||
env: MPI=YES
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- cd tests/scripts
|
||||
@@ -70,24 +65,13 @@ jobs:
|
||||
- mpich
|
||||
- libmpich-dev
|
||||
env: MPI=YES
|
||||
before_script:
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- mpicxx -v
|
||||
- make config MFEM_USE_MPI=YES MFEM_MPI_NP=2
|
||||
- make all -j3
|
||||
- make test-noclean
|
||||
script:
|
||||
- cd tests/scripts
|
||||
- ./runtest gitignore
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
before_cache:
|
||||
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
|
||||
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
|
||||
rm -f Lib/*.{c,o}
|
||||
|
||||
# ========================
|
||||
# Optional Checks/Tests
|
||||
@@ -96,7 +80,6 @@ jobs:
|
||||
|
||||
- stage: optional
|
||||
name: "branch-history"
|
||||
if: branch != next
|
||||
# need full git history for the binary/big files check
|
||||
git:
|
||||
depth: false
|
||||
@@ -125,8 +108,6 @@ jobs:
|
||||
MPI=NO
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=check
|
||||
cache:
|
||||
ccache: true
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
@@ -135,8 +116,6 @@ jobs:
|
||||
MPI=NO
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=test
|
||||
cache:
|
||||
ccache: true
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
@@ -160,9 +139,9 @@ jobs:
|
||||
MFEM_TEST_TARGET=check
|
||||
NPROCS=2
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/lib
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/include
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
before_cache:
|
||||
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
|
||||
@@ -191,9 +170,9 @@ jobs:
|
||||
MFEM_TEST_TARGET=test
|
||||
NPROCS=2
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/lib
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/include
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
before_cache:
|
||||
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
|
||||
@@ -216,16 +195,16 @@ jobs:
|
||||
- cd ${TRAVIS_BUILD_DIR}/build
|
||||
- cmake ..
|
||||
-DMFEM_USE_MPI=ON
|
||||
-DHYPRE_DIR=${TRAVIS_BUILD_DIR}/../$HYPRE_TOP_DIR/src/hypre
|
||||
-DHYPRE_DIR=${TRAVIS_BUILD_DIR}/../hypre-2.10.0b/src/hypre
|
||||
-DMFEM_MPI_NP=$NPROCS
|
||||
- make -j3 mfem examples
|
||||
- cd ${TRAVIS_BUILD_DIR}/build/tests/unit
|
||||
- make -j3
|
||||
- ctest --output-on-failure
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/lib
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/include
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
before_cache:
|
||||
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
|
||||
@@ -241,43 +220,27 @@ jobs:
|
||||
# - parallel
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.2
|
||||
# osx_image: xcode7.3
|
||||
compiler: clang
|
||||
name: "Mac: Serial + Debug"
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
env: DEBUG=YES
|
||||
MPI=NO
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=check
|
||||
cache:
|
||||
ccache: true
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.2
|
||||
# osx_image: xcode7.3
|
||||
compiler: clang
|
||||
name: "Mac: Serial"
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
env: DEBUG=NO
|
||||
MPI=NO
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=test
|
||||
cache:
|
||||
ccache: true
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.2
|
||||
# osx_image: xcode7.3
|
||||
compiler: clang
|
||||
name: "Mac: Parallel + Debug"
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
env: DEBUG=YES
|
||||
MPI=YES
|
||||
CODECOV=NO
|
||||
@@ -285,9 +248,9 @@ jobs:
|
||||
NPROCS=4
|
||||
TMPDIR=/tmp
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/lib
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/include
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
- $HOME/local-cached
|
||||
before_cache:
|
||||
@@ -296,13 +259,9 @@ jobs:
|
||||
rm -f Lib/*.{c,o}
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.2
|
||||
# osx_image: xcode7.3
|
||||
compiler: clang
|
||||
name: "Mac: Parallel"
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
env: DEBUG=NO
|
||||
MPI=YES
|
||||
CODECOV=YES
|
||||
@@ -310,9 +269,9 @@ jobs:
|
||||
NPROCS=4
|
||||
TMPDIR=/tmp
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/lib
|
||||
- $TRAVIS_BUILD_DIR/../hypre-2.10.0b/src/hypre/include
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
- $HOME/local-cached
|
||||
before_cache:
|
||||
@@ -326,19 +285,14 @@ before_install:
|
||||
# brew install open-mpi;
|
||||
# fi
|
||||
|
||||
# Disable ccache while building dependencies that are cached:
|
||||
- echo "before \$PATH = $PATH";
|
||||
export PATH=${PATH//\/usr\/lib\/ccache:/};
|
||||
echo "after \$PATH = $PATH"
|
||||
|
||||
# On Mac OS X, build and cache OpenMPI 2.1.6:
|
||||
# On Mac OS X, build and cache OpenMPI 2.1.1:
|
||||
- if [ $TRAVIS_OS_NAME == "osx" ] && [ $MPI == "YES" ]; then
|
||||
if [ ! -e $HOME/local-cached/bin/mpicc ]; then
|
||||
mkdir -p $HOME/builds && cd $HOME/builds &&
|
||||
wget https://download.open-mpi.org/release/open-mpi/v2.1/openmpi-2.1.6.tar.bz2 &&
|
||||
tar jxf openmpi-2.1.6.tar.bz2 &&
|
||||
wget https://www.open-mpi.org/software/ompi/v2.1/downloads/openmpi-2.1.1.tar.bz2 &&
|
||||
tar jxf openmpi-2.1.1.tar.bz2 &&
|
||||
mkdir openmpi-build && cd openmpi-build &&
|
||||
../openmpi-2.1.6/configure --prefix=$HOME/local-cached &&
|
||||
../openmpi-2.1.1/configure --prefix=$HOME/local-cached &&
|
||||
make -j3 all && make install;
|
||||
fi;
|
||||
PATH=$HOME/local-cached/bin:$PATH;
|
||||
@@ -383,28 +337,26 @@ install:
|
||||
|
||||
# hypre
|
||||
- if [ $MPI == "YES" ]; then
|
||||
if [ ! -e $HYPRE_TOP_DIR/src/hypre/lib/libHYPRE.a ]; then
|
||||
wget $HYPRE_URL;
|
||||
rm -rf $HYPRE_TOP_DIR;
|
||||
tar xvzf $HYPRE_ARCHIVE;
|
||||
cd $HYPRE_TOP_DIR/src;
|
||||
./configure --disable-fortran CC=mpicc CXX=mpic++;
|
||||
if [ ! -e hypre-2.10.0b/src/hypre/lib/libHYPRE.a ]; then
|
||||
wget https://computation.llnl.gov/project/linear_solvers/download/hypre-2.10.0b.tar.gz --no-check-certificate;
|
||||
rm -rf hypre-2.10.0b;
|
||||
tar xvzf hypre-2.10.0b.tar.gz;
|
||||
cd hypre-2.10.0b/src;
|
||||
./configure --disable-fortran --without-fei CC=mpicc CXX=mpic++;
|
||||
make -j3;
|
||||
cd ../..;
|
||||
else
|
||||
echo "Reusing cached $HYPRE_TOP_DIR/";
|
||||
echo "Reusing cached hypre-2.10.0b/";
|
||||
fi;
|
||||
ln -s $HYPRE_TOP_DIR hypre;
|
||||
ln -s hypre-2.10.0b hypre;
|
||||
else
|
||||
echo "Serial build, not using hypre";
|
||||
fi
|
||||
|
||||
# METIS, use a mirror because the original source server is not always up.
|
||||
# Original url:
|
||||
# http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/OLD/metis-4.0.3.tar.gz
|
||||
# METIS
|
||||
- if [ $MPI == "YES" ]; then
|
||||
if [ ! -e metis-4.0/libmetis.a ]; then
|
||||
wget https://mfem.github.io/tpls/metis-4.0.3.tar.gz;
|
||||
wget http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/OLD/metis-4.0.3.tar.gz;
|
||||
tar xvzf metis-4.0.3.tar.gz;
|
||||
make -j3 -C metis-4.0.3/Lib CC="$CC" OPTFLAGS="-O2";
|
||||
rm -rf metis-4.0;
|
||||
@@ -414,26 +366,12 @@ install:
|
||||
fi;
|
||||
fi
|
||||
|
||||
# Re-enable ccache on linux; enable ccache on mac os:
|
||||
- if [ $TRAVIS_OS_NAME == "linux" ]; then
|
||||
export PATH="/usr/lib/ccache:$PATH";
|
||||
else
|
||||
if [ $TRAVIS_OS_NAME == "osx" ]; then
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH";
|
||||
fi;
|
||||
fi
|
||||
|
||||
- printf "which \$CC = "; which $CC;
|
||||
printf "which \$CXX = "; which $CXX
|
||||
|
||||
script:
|
||||
# Compiler
|
||||
- if [ $MPI == "YES" ]; then
|
||||
export MYCXX=mpic++;
|
||||
export MAKE_CXX_FLAG=MPICXX=$MYCXX;
|
||||
else
|
||||
export MYCXX="$CXX";
|
||||
export MAKE_CXX_FLAG=CXX=$MYCXX;
|
||||
fi
|
||||
|
||||
# Print the compiler version
|
||||
@@ -446,12 +384,12 @@ script:
|
||||
if [ "$CODECOV" == "YES" ]; then
|
||||
CPPFLAGS="--coverage -g";
|
||||
fi;
|
||||
if [ "$TRAVIS_OS_NAME" != "linux" ] || [ "$DEBUG" == "YES" ]; then
|
||||
CPPFLAGS+=" -pedantic -Wall -Werror";
|
||||
if [ "$CXX" == "clang++" ]; then
|
||||
export MFEM_PERF_SW=clang;
|
||||
fi
|
||||
|
||||
# Configure the library
|
||||
- make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG $MAKE_CXX_FLAG
|
||||
- make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG MFEM_CXX="$MYCXX"
|
||||
MFEM_MPI_NP=$NPROCS CPPFLAGS="$CPPFLAGS"
|
||||
# Show the configuration
|
||||
- make info
|
||||
|
||||
@@ -16,65 +16,17 @@ Meshing improvements
|
||||
- The graph linear ordering library Gecko, previously an external dependency, is
|
||||
now included directly in MFEM. As a result, Mesh::GetGeckoElementOrdering is
|
||||
always available. The interface has also been improved, see for example the
|
||||
Mesh Explorer miniapp.
|
||||
|
||||
- Improved Gmsh reader (version 2.2), which now supports both high-order and
|
||||
periodic meshes. Segments, triangles, quadrilaterals, and tetrahedra are
|
||||
supported up to order 10. Wedges and hexahedra are supported up to order 9.
|
||||
For sample periodic meshes, see the periodic*.msh files in the data directory.
|
||||
mesh-explorer miniapp.
|
||||
|
||||
- Added support for finite difference-based gradient and Hessian approximation
|
||||
in the TMOP mesh optimization algorithms. This improves the accuracy of the
|
||||
Hessian for r-adaptivity using discrete fields, and allows use of skewness
|
||||
and orientation based metrics.
|
||||
|
||||
- Added support for r-adaptivity with more than one discrete field. This allows
|
||||
the user to specify different discrete functions for controlling the
|
||||
size, aspect-ratio, orientation, and skew of elements in the mesh.
|
||||
|
||||
- Added TMOP capability for approximate tangential mesh relaxation. Added
|
||||
support and examples for using TMOP on mixed meshes.
|
||||
|
||||
- Added complete action of the TMOP Integrator to account for the spatial
|
||||
derivatives of discrete and analytic targets.
|
||||
|
||||
- Added support for initialization of (serial) non-conforming meshes. Hanging
|
||||
nodes can be marked with Mesh::AddVertexParents when building the mesh with
|
||||
the "init" constructor. The usage is demonstrated in a new meshing miniapp
|
||||
(polar-nc) which generates meshes that are non-conforming from the start.
|
||||
|
||||
Performance improvements
|
||||
------------------------
|
||||
- Added support for explicit vectorization in the high-performance templated
|
||||
code, which can now take advantage of specific intrinsics classes on the
|
||||
following architectures:
|
||||
- x86 (SSE/AVX/AVX2/AVX512),
|
||||
- Power8 & Power9 (VSX),
|
||||
- BG/Q (QPX).
|
||||
These are disabled by default, and can be enabled with MFEM_USE_SIMD=YES.
|
||||
See the new file linalg/simd.hpp and the new directory linalg/simd.
|
||||
|
||||
Improved GPU capabilities
|
||||
-------------------------
|
||||
- Added support for Chebyshev accelerated polynomial smoother on GPU.
|
||||
|
||||
- Optimized AMD/HIP kernel support.
|
||||
|
||||
- Added a Full Assembly mode compatible with Device kernel execution. This
|
||||
assembly level builds on top of the current Element Assembly kernels to
|
||||
compute a global sparse matrix. All integrators supported by element assembly
|
||||
are also supported by full assembly. See the '-fa' option in Example 9.
|
||||
|
||||
- Added CUDA support for sparse matrix-vector multiplication with cuSPARSE.
|
||||
|
||||
- Added AmgX solver class for simple integration with NVIDIA's multigrid library.
|
||||
|
||||
- Added support for BlockOperator on GPU. See the updated Example 5.
|
||||
|
||||
- Added partial assembly and GPU support for complex operators, including the
|
||||
classes ComplexOperator, [Par]ComplexGridFunction, [Par]ComplexLinearForm, and
|
||||
[Par]SesquilinearForm. See the updated Example 22.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added support for matrix-free interpolation and restriction operators between
|
||||
@@ -83,34 +35,6 @@ Discretization improvements
|
||||
|
||||
- Added support for simplices in GSLIB-FindPoints.
|
||||
|
||||
- Added support for H1 and L2 element matrix assembly in the mass, convection,
|
||||
diffusion, transpose, and the face DG trace integrators. This is compatible
|
||||
with GPU device execution and is illustrated in Example 9/9p, see the option
|
||||
'-ea'. When enabled, this level of assembly stores independent dense matrices
|
||||
for the elements, and independent dense matrices for the faces in the DG case.
|
||||
|
||||
- Added new partial assembly kernels for H(div) bilinear forms, as well as
|
||||
VectorFEDivergenceIntegrator.
|
||||
|
||||
- Improved the documentation of the GridFunction GetValue and GetVectorValue
|
||||
methods. Expanded the GetValue and GetVectorValue methods which accept an
|
||||
ElementTransformation argument to support evaluation on boundary elements
|
||||
and, in the continuous field case, arbitrary mesh edges and faces.
|
||||
|
||||
- Added new coefficient and vector coefficient classes for QuadratureFunctions.
|
||||
Additionally, new LinearForm integrators were also added which make use of
|
||||
these new QuadratureFunction coefficient classes.
|
||||
|
||||
- Added support face integrals on the boundaries of NURBS meshes.
|
||||
|
||||
- Added support for interpolation of functions in L2, H(div) and H(curl)
|
||||
spaces using GSLIB-FindPoints.
|
||||
|
||||
- Added support for computing asymptotic error estimates and convergence rates
|
||||
for the whole de Rham sequence based on the new class ConvergenceStudy and new
|
||||
member methods in GridFunction and ParGridFunction. See the rates.cpp file in
|
||||
the tests/convergence directory for sample usage.
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added power method to iteratively estimate the largest eigenvalue and the
|
||||
@@ -119,26 +43,6 @@ Linear and nonlinear solvers
|
||||
- Added initial support for h- and p-multigrid solvers and preconditioners for
|
||||
matrix-based and matrix-free discretizations with basic GPU capability.
|
||||
|
||||
- Added a new IterativeSolverMonitor class that allows to monitor the residual
|
||||
and solution during the solving process of an IterativeSolver after every
|
||||
iteration.
|
||||
|
||||
- Added support for the CVODES package in SUNDIALS which provides ODE
|
||||
solvers with sensitivity analysis capabilities. See the CVODESSolver
|
||||
class and the new adjoint miniapps below.
|
||||
|
||||
- Block arrays of parallel matrices can now be merged into a single parallel
|
||||
matrix with the function HypreParMatrixFromBlocks. This could be useful for
|
||||
solving block systems with parallel direct solvers such as STRUMPACK.
|
||||
|
||||
- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
|
||||
solver can work with non-SPD preconditioner B.
|
||||
|
||||
- Added support for the SLEPc eigensolver package.
|
||||
|
||||
- Added partially assembled convergent diagonal preconditioner for adaptively
|
||||
refined meshes (i.e. non-conforming finite element spaces), see Example 6/6p.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new example, Example 25/25p, to demonstrate the use of a Perfectly
|
||||
@@ -148,25 +52,11 @@ New and updated examples and miniapps
|
||||
- Added a new Example 26/26p to demonstrate the construction of a matrix-free
|
||||
geometric and p-multigrid preconditioner for the Laplace problem.
|
||||
|
||||
- Added a new example, Example 27/27p, to demonstrate the enforcement of various
|
||||
boundary conditions with the Laplace operator. The example shows the procedure
|
||||
for applying Dirichlet, Neumann (both homogeneous and inhomogeneous), Robin,
|
||||
and periodic boundary conditions with either H1 or DG discretizations.
|
||||
|
||||
- Added a new miniapp, Navier, that solves the time-dependent Navier-Stokes
|
||||
equations of incompressible fluid dynamics. See the miniapps/navier directory
|
||||
for more details.
|
||||
|
||||
- Added a new miniapps/adjoint directory with two miniapps demonstrating how to
|
||||
solve adjoint problems in MFEM using the CVODES package in SUNDIALS. Both of
|
||||
these miniapps require the MFEM_USE_SUNDIALS configuration option.
|
||||
* The cvsRoberts_ASAi_dns miniapp solves a backward adjoint problem for a
|
||||
system of ODEs, evaluating both forward and adjoint quadratures in serial.
|
||||
* The adjoint_advection_diffusion miniapp solves a backward adjoint problem
|
||||
for an advection diffusion PDE, evaluating adjoint quadratures in parallel.
|
||||
|
||||
- Ported Example 11p to SLEPc, to demonstrate solving the Laplace eigenvalue
|
||||
equation with the shift-and-invert spectral transformation method.
|
||||
- Added a new example, Example 27/27p, to demonstrate the enforcement of
|
||||
various boundary conditions with the Laplace operator. The example shows the
|
||||
procedures for applying Dirichlet, Neumann (both homogeneous and
|
||||
inhomogeneous), Robin, and periodic boundary conditions with either H1 or DG
|
||||
discretizations.
|
||||
|
||||
- Added a simple meshing miniapp, Twist, which demonstrates MFEM's strategy of
|
||||
stitching together opposite surfaces of a mesh to create a topologically
|
||||
@@ -175,63 +65,23 @@ New and updated examples and miniapps
|
||||
- Added a new meshing miniapp, Minimal Surface, which solves Plateau's problem:
|
||||
the Dirichlet problem for the minimal surface equation.
|
||||
|
||||
- Added a new meshing miniapp, Polar NC, which demonstrates the construction of
|
||||
polar non-conforming meshes.
|
||||
|
||||
- Added partial assembly support to Example 4/4p and Example 5/5p, with diagonal
|
||||
preconditioning.
|
||||
|
||||
- Added full assembly support in Example 9/9p.
|
||||
|
||||
- Added a new test problem in Example 24/24p, demonstrating a mixed bilinear
|
||||
form for H1, H(curl), H(div) and L_2, with partial assembly support.
|
||||
|
||||
- Added weak Dirichlet boundary conditions (Nitsche) to the NURBS miniapp.
|
||||
|
||||
- Added a simple mesh editing miniapp, Trimmer, which trims away portions of a
|
||||
mesh based on element attributes. Any newly exposed boundary elements are
|
||||
assigned attribute numbers related to the trimmed element attributes.
|
||||
|
||||
- Added a new miniapp (field-interp) that demonstrates transfer of grid function
|
||||
between different meshes using GSLIB-FindPoints.
|
||||
|
||||
- Added diagonal preconditioner in Example 6/6p for partial assembly with AMR.
|
||||
|
||||
- Added device support in Example 5/5p.
|
||||
|
||||
- Added partial assembly and device support to Example 22/22p, with diagonal
|
||||
preconditioning.
|
||||
|
||||
- Added the option to plot a function in Mesh Explorer.
|
||||
|
||||
Improved testing
|
||||
----------------
|
||||
- Upgraded the Catch unit test framework from version 1.6.1 to version 2.13.0.
|
||||
|
||||
- Added a GitLab pipeline that automates PR testing on supercomputing systems
|
||||
and Linux clusters at Lawrence Livermore National Lab (LLNL). This can be
|
||||
triggered only by LLNL developers, see .gitlab-ci.yml, the .gitlab directory
|
||||
and the updated CONTRIBUTING.md file.
|
||||
|
||||
- Added testing of the parallel mesh format in tests/par-mesh-format.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
|
||||
solver can work with non-SPD preconditioner B.
|
||||
|
||||
- Added support for ADIOS2 for parallel I/O with ParaView visualization. The
|
||||
classes adios2stream and ADIOS2DataCollection are introduced in mfem as the
|
||||
interfaces to generate ADIOS2 Binary Pack (BP4) directory datasets for the
|
||||
entire spatial and temporal node data. Cell centered data is accessible by
|
||||
ADIOS2 data readers (e.g. Python), but currently not yet implement as of
|
||||
ParaView v5.8.1. In addition, ADIOS2 allows for setting a user-defined number
|
||||
of data substreams/subfiles at scale. See examples 5, 9, 12, 16.
|
||||
|
||||
- The integration order used in the ComputeLpError and ComputeElementLpError
|
||||
methods of class GridFunction has been increased.
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
- Renamed "Backend::DEBUG" to "Backend::DEBUG_DEVICE" to avoid conflicts,
|
||||
as DEBUG is sometimes used as a macro.
|
||||
entire spatial and temporal data. In addition, ADIOS2 allows for setting a
|
||||
user-defined number of data substreams/subfiles. See examples 5, 9, 12, 16.
|
||||
|
||||
|
||||
Version 4.1, released on March 10, 2020
|
||||
|
||||
+21
-45
@@ -89,38 +89,8 @@ enable_language(CXX)
|
||||
if (MFEM_USE_CUDA)
|
||||
# MFEM_USE_CUDA requires CMake 3.8 or newer (for direct CUDA support)
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
# Use ${CMAKE_CXX_COMPILER} as the cuda host compiler.
|
||||
if (NOT CMAKE_CUDA_HOST_COMPILER)
|
||||
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
enable_language(CUDA)
|
||||
set(CMAKE_CUDA_STANDARD 11)
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CUDA_EXTENSIONS OFF)
|
||||
set(CUDA_FLAGS "--expt-extended-lambda")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.18.0)
|
||||
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
|
||||
elseif (NOT CMAKE_CUDA_ARCHITECTURES)
|
||||
string(REGEX REPLACE "^sm_" "" ARCH_NUMBER "${CUDA_ARCH}")
|
||||
if ("${CUDA_ARCH}" STREQUAL "sm_${ARCH_NUMBER}")
|
||||
set(CMAKE_CUDA_ARCHITECTURES "${ARCH_NUMBER}")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown CUDA_ARCH: ${CUDA_ARCH}")
|
||||
endif()
|
||||
else()
|
||||
set(CUDA_ARCH "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
|
||||
endif()
|
||||
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
|
||||
if (CMAKE_VERSION VERSION_LESS 3.12.0)
|
||||
# CMake versions 3.8 and 3.9 require this to work; 3.10 and 3.11 are not
|
||||
# tested and may not actually need this (but should be ok to keep).
|
||||
set(CUDA_FLAGS "-ccbin=${CMAKE_CXX_COMPILER} ${CUDA_FLAGS}")
|
||||
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
set(CMAKE_CUDA_FLAGS "${CUDA_FLAGS}" CACHE STRING
|
||||
"CUDA flags set for MFEM" FORCE)
|
||||
set(CUSPARSE_FOUND TRUE)
|
||||
set(CUSPARSE_LIBRARIES "cusparse")
|
||||
endif()
|
||||
|
||||
if (XSDK_ENABLE_C)
|
||||
@@ -179,13 +149,9 @@ if (MFEM_USE_MPI)
|
||||
message(FATAL_ERROR "PETSc version >= 3.8.0 is required")
|
||||
endif()
|
||||
set(PETSC_INCLUDE_DIRS ${PETSC_INCLUDES})
|
||||
if (MFEM_USE_SLEPC)
|
||||
find_package(SLEPc REQUIRED config)
|
||||
message(STATUS "Found SLEPc version ${SLEPC_VERSION}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(PKGS_NEED_MPI SUPERLU PETSC SLEPC STRUMPACK PUMI)
|
||||
set(PKGS_NEED_MPI SUPERLU PETSC STRUMPACK PUMI)
|
||||
foreach(PKG IN LISTS PKGS_NEED_MPI)
|
||||
if (MFEM_USE_${PKG})
|
||||
message(STATUS "Disabling package ${PKG} - requires MPI")
|
||||
@@ -241,10 +207,10 @@ endif()
|
||||
# SUNDIALS
|
||||
if (MFEM_USE_SUNDIALS)
|
||||
if (NOT MFEM_USE_MPI)
|
||||
find_package(SUNDIALS REQUIRED NVector_Serial CVODES ARKODE KINSOL)
|
||||
find_package(SUNDIALS REQUIRED NVector_Serial CVODE ARKODE KINSOL)
|
||||
else()
|
||||
find_package(SUNDIALS REQUIRED
|
||||
NVector_Serial NVector_Parallel NVector_ParHyp CVODES ARKODE KINSOL)
|
||||
NVector_Serial NVector_Parallel NVector_ParHyp CVODE ARKODE KINSOL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -295,11 +261,6 @@ if (MFEM_USE_CEED)
|
||||
find_package(libCEED REQUIRED)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_AMGX)
|
||||
include_directories(${AMGX_DIR}/include)
|
||||
link_directories(${AMGX_DIR}/lib64 -L${AMGX_DIR}/lib -Xlinker=-rpath=${CUDA_HOME}/lib64)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CONDUIT)
|
||||
find_package(Conduit REQUIRED conduit relay blueprint )
|
||||
endif()
|
||||
@@ -331,6 +292,22 @@ if (MFEM_USE_HIOP)
|
||||
# find_package updates HIOP_FOUND, HIOP_INCLUDE_DIRS, HIOP_LIBRARIES
|
||||
endif()
|
||||
|
||||
# CUDA
|
||||
if (MFEM_USE_CUDA)
|
||||
set(CMAKE_CUDA_STANDARD 11)
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CUDA_EXTENSIONS OFF)
|
||||
set(CMAKE_CUDA_FLAGS "-arch=${CUDA_ARCH} --expt-extended-lambda"
|
||||
CACHE STRING "CUDA flags set for MFEM" FORCE)
|
||||
if (MFEM_USE_MPI)
|
||||
set(CUDA_CCBIN_COMPILER ${MPI_CXX_COMPILER})
|
||||
else()
|
||||
set(CUDA_CCBIN_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
string(APPEND CMAKE_CUDA_FLAGS " -ccbin ${CUDA_CCBIN_COMPILER}")
|
||||
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CUDA_CCBIN_COMPILER})
|
||||
endif()
|
||||
|
||||
# OCCA
|
||||
if (MFEM_USE_OCCA)
|
||||
find_package(OCCA REQUIRED)
|
||||
@@ -375,9 +352,8 @@ endif()
|
||||
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
|
||||
# be before SuiteSparse.
|
||||
set(MFEM_TPLS MPI_CXX OPENMP BLAS LAPACK METIS HYPRE SuiteSparse SUNDIALS PETSC
|
||||
SLEPC MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT Ginkgo GNUTLS GSLIB NETCDF
|
||||
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
|
||||
CUSPARSE AMGX)
|
||||
MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT Ginkgo GNUTLS GSLIB NETCDF
|
||||
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2)
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
set(TPL_LIBRARIES "")
|
||||
set(TPL_INCLUDE_DIRS "")
|
||||
|
||||
@@ -109,7 +109,6 @@ The MFEM source code has the following structure:
|
||||
├── linalg
|
||||
├── mesh
|
||||
├── miniapps
|
||||
│ ├── adjoint
|
||||
│ ├── common
|
||||
│ ├── electromagnetics
|
||||
│ ├── gslib
|
||||
|
||||
@@ -350,12 +350,6 @@ MFEM_USE_STRUMPACK = YES/NO
|
||||
classes. When enabled, this option uses the STRUMPACK_* library options, see
|
||||
below.
|
||||
|
||||
MFEM_USE_AMGX = YES/NO
|
||||
Enable MFEM functionality based on the AMGX multigrid library from
|
||||
NVIDIA. When enabled, this options enables the user to use SparseMatrices
|
||||
and HypreParMatrices to solve linear systems using routines from the AMGX
|
||||
library.
|
||||
|
||||
MFEM_USE_GINKGO = YES/NO
|
||||
Enable MFEM functionality based on the Ginkgo library, which provides
|
||||
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
|
||||
@@ -389,10 +383,6 @@ MFEM_USE_PETSC = YES/NO
|
||||
and other features based on the PETSc package. When enabled, this option uses
|
||||
the PETSC_* library options, see below.
|
||||
|
||||
MFEM_USE_SLEPC = YES/NO
|
||||
Enable MFEM eigensolvers based on the SLEPc package. When enabled, this
|
||||
option uses the SLEPC_* library options, see below.
|
||||
|
||||
MFEM_USE_MPFR = YES/NO
|
||||
MPFR is a library for multiple-precision floating-point computations. This
|
||||
option enables the use of MPFR in MFEM, e.g. for precise computation of 1D
|
||||
@@ -406,12 +396,6 @@ MFEM_USE_SIDRE = YES/NO
|
||||
blueprint specification. When enabled, this option requires installation of
|
||||
HDF5 (see also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
|
||||
|
||||
MFEM_USE_SIMD = YES/NO
|
||||
Enables the high performance templated classes to use architecture dependent
|
||||
SIMD intrinsics instead of the generic implementation of class AutoSIMD in
|
||||
linalg/simd/auto.hpp. This option should be combined with suitable
|
||||
compiler options, such as -march=native, to enable optimal vectorization.
|
||||
|
||||
MFEM_USE_CONDUIT = YES/NO
|
||||
Enables support for converting MFEM Mesh and Grid Function objects to and
|
||||
from Conduit Mesh Blueprint Descriptions (https://github.com/LLNL/conduit/)
|
||||
@@ -442,8 +426,6 @@ MFEM_USE_PUMI = YES/NO
|
||||
data management system that is capable of handling general non-manifold
|
||||
models and effectively supports automated adaptive analysis. PUMI enables
|
||||
support for parallel unstructured mesh modifications in MFEM.
|
||||
The develop branch of PUMI repository (https://github.com/SCOREC/core)
|
||||
should be used for most updated features.
|
||||
|
||||
MFEM_USE_UMPIRE = YES/NO
|
||||
Enables support for Umpire, a resource management library that allows the
|
||||
@@ -607,12 +589,6 @@ The specific libraries and their options are:
|
||||
Options: PETSC_OPT, PETSC_LIB.
|
||||
Versions: PETSc >= 3.8.0.
|
||||
|
||||
- SLEPc (optional), used when MFEM_USE_SLEPC = YES. SLEPc depends on PETSc and
|
||||
uses some of the PETSc options when compiled.
|
||||
URL: https://slepc.upv.es/
|
||||
Options: SLEPC_OPT, SLEPC_LIB.
|
||||
Versions: SLEPc >= 3.8.0.
|
||||
|
||||
- Sidre (optional), part of LLNL's axom project, used when MFEM_USE_SIDRE = YES.
|
||||
Starting with MFEM v4.1, Axom version 0.3.1 or later is required.
|
||||
URL: https://github.com/LLNL/axom
|
||||
@@ -633,9 +609,8 @@ The specific libraries and their options are:
|
||||
|
||||
- PUMI (optional), used when MFEM_USE_PUMI = YES.
|
||||
URL: https://scorec.rpi.edu/pumi
|
||||
https://github.com/SCOREC/core
|
||||
Options: PUMI_OPT, PUMI_LIB.
|
||||
Versions: PUMI >= 2.2.3.
|
||||
Versions: PUMI >= 2.2.0.
|
||||
|
||||
- HiOp (optional), used when MFEM_USE_HIOP = YES.
|
||||
URL: https://github.com/LLNL/hiop
|
||||
@@ -656,11 +631,6 @@ The specific libraries and their options are:
|
||||
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
|
||||
Versions: CUDA >= 9.1, older versions may work too.
|
||||
|
||||
- AMGX (optional), used when MFEM_USE_AMGX = YES.
|
||||
URL: https://github.com/NVIDIA/AMGX
|
||||
Options: AMGX_OPT, AMGX_LIB.
|
||||
Versions: AMGX >= 2.1, older versions may work too.
|
||||
|
||||
- HIP (optional), used when MFEM_USE_HIP = YES.
|
||||
URL: https://rocm.github.io/ROCmInstall.html
|
||||
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
|
||||
@@ -670,11 +640,12 @@ The specific libraries and their options are:
|
||||
Options: OCCA_DIR, OCCA_OPT, OCCA_LIB.
|
||||
Versions: OCCA >= 1.0.9.
|
||||
|
||||
- libCEED (optional), used when MFEM_USE_CEED = YES.
|
||||
- libCEED (optional), used when MFEM_USE_CEED = YES. Requires libCEED v0.6
|
||||
or later version, specifically, git-hash 3d05795 or later.
|
||||
URL: https://github.com/CEED/libCEED
|
||||
https://ceed.exascaleproject.org/libceed
|
||||
Options: CEED_DIR, CEED_OPT, CEED_LIB.
|
||||
Versions: libCEED > 0.6, git-hash bdfed75.
|
||||
Versions: libCEED >= 0.6.
|
||||
|
||||
- RAJA (optional), used when MFEM_USE_RAJA = YES.
|
||||
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
|
||||
@@ -817,7 +788,6 @@ MFEM_USE_MESQUITE
|
||||
MFEM_USE_SUITESPARSE
|
||||
MFEM_USE_SUPERLU
|
||||
MFEM_USE_STRUMPACK
|
||||
MFEM_USE_AMGX
|
||||
MFEM_USE_GINKGO
|
||||
MFEM_USE_GNUTLS
|
||||
MFEM_USE_NETCDF
|
||||
|
||||
@@ -244,10 +244,6 @@ IF (DEFINED TPL_ENABLE_PETSC)
|
||||
SET(MFEM_USE_PETSC ${TPL_ENABLE_PETSC} CACHE BOOL "Enable PETSc support." FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF (DEFINED TPL_ENABLE_SLEPC)
|
||||
SET(MFEM_USE_SLEPC ${TPL_ENABLE_SLEPC} CACHE BOOL "Enable SLEPc support." FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF (DEFINED TPL_ENABLE_MPFR)
|
||||
SET(MFEM_USE_MPFR ${TPL_ENABLE_MPFR} CACHE BOOL "Enable MPFR usage." FORCE)
|
||||
ENDIF()
|
||||
|
||||
@@ -38,7 +38,6 @@ set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
|
||||
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
|
||||
set(MFEM_USE_NETCDF @MFEM_USE_NETCDF@)
|
||||
set(MFEM_USE_PETSC @MFEM_USE_PETSC@)
|
||||
set(MFEM_USE_SLEPC @MFEM_USE_SLEPC@)
|
||||
set(MFEM_USE_MPFR @MFEM_USE_MPFR@)
|
||||
set(MFEM_USE_SIDRE @MFEM_USE_SIDRE@)
|
||||
set(MFEM_USE_CONDUIT @MFEM_USE_CONDUIT@)
|
||||
@@ -48,7 +47,6 @@ set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
|
||||
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
|
||||
set(MFEM_USE_CEED @MFEM_USE_CEED@)
|
||||
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
|
||||
set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
|
||||
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
|
||||
|
||||
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
|
||||
|
||||
@@ -104,15 +104,9 @@
|
||||
// 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 Conduit
|
||||
#cmakedefine MFEM_USE_CONDUIT
|
||||
|
||||
@@ -126,9 +120,6 @@
|
||||
// Requires a CUDA compiler (nvcc).
|
||||
#cmakedefine MFEM_USE_CUDA
|
||||
|
||||
// Enable MFEM functionality based on the AMGX library
|
||||
#cmakedefine MFEM_USE_AMGX
|
||||
|
||||
// Enable MFEM functionality based on the RAJA library
|
||||
#cmakedefine MFEM_USE_RAJA
|
||||
|
||||
|
||||
@@ -38,19 +38,7 @@ if(NOT ADIOS2_FOUND)
|
||||
endif()
|
||||
|
||||
find_path(ADIOS2_INCLUDE_DIR adios2.h ${ADIOS2_INCLUDE_OPTS})
|
||||
|
||||
# adios2 version 2.5.0
|
||||
find_library(ADIOS2_LIBRARY NAMES adios2 ${ADIOS2_LIBRARY_OPTS})
|
||||
|
||||
# adios2 version 2.6.0 and onwards
|
||||
if(NOT ADIOS2_LIBRARY)
|
||||
find_library(ADIOS2_CXX11_MPI_LIBRARY NAMES adios2_cxx11_mpi ${ADIOS2_LIBRARY_OPTS})
|
||||
find_library(ADIOS2_CXX11_LIBRARY NAMES adios2_cxx11 ${ADIOS2_LIBRARY_OPTS})
|
||||
set(ADIOS2_LIBRARY ${ADIOS2_CXX11_MPI_LIBRARY} ${ADIOS2_CXX11_LIBRARY})
|
||||
if(MFEM_USE_MPI)
|
||||
add_definitions(-DADIOS2_USE_MPI)
|
||||
endif()
|
||||
endif()
|
||||
find_library(ADIOS2_LIBRARY NAMES adios2 ${ADIOS2_LIBRARY_OPTS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ADIOS2
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# Copyright (c) 2010-2020, 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.
|
||||
|
||||
# Sets the following variables:
|
||||
# - SLEPC_FOUND
|
||||
# - SLEPC_INCLUDE_DIRS
|
||||
# - SLEPC_LIBRARIES
|
||||
|
||||
set(SLEPc_REQUIRED_PACKAGES "PETSC" CACHE STRING
|
||||
"Additional packages required by SLEPc")
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(SLEPc SLEPC SLEPC_DIR
|
||||
"include" "slepceps.h"
|
||||
"${PETSC_ARCH}/lib" "slepc" # add NAMES_PER_DIR?
|
||||
"Paths to headers required by SLEPc."
|
||||
"Libraries required by SLEPc."
|
||||
ADD_COMPONENT "config" "${PETSC_ARCH}/include" "slepcconf.h" "" ""
|
||||
CHECK_BUILD SLEPC_VERSION_OK TRUE
|
||||
"
|
||||
#include \"petsc.h\"
|
||||
#include \"slepceps.h\"
|
||||
int main()
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
int argc = 0;
|
||||
char** argv = NULL;
|
||||
ierr = SlepcInitialize(&argc, &argv, PETSC_NULL, PETSC_NULL);
|
||||
EPS eps;
|
||||
ierr = EPSCreate(PETSC_COMM_SELF, &eps); CHKERRQ(ierr);
|
||||
ierr = EPSDestroy(&eps); CHKERRQ(ierr);
|
||||
ierr = SlepcFinalize(); CHKERRQ(ierr);
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
)
|
||||
@@ -25,6 +25,5 @@ mfem_find_package(SUNDIALS SUNDIALS SUNDIALS_DIR
|
||||
ADD_COMPONENT NVector_ParHyp
|
||||
"include" nvector/nvector_parhyp.h "lib" sundials_nvecparhyp
|
||||
ADD_COMPONENT CVODE "include" cvode/cvode.h "lib" sundials_cvode
|
||||
ADD_COMPONENT CVODES "include" cvodes/cvodes.h "lib" sundials_cvodes
|
||||
ADD_COMPONENT ARKODE "include" arkode/arkode.h "lib" sundials_arkode
|
||||
ADD_COMPONENT KINSOL "include" kinsol/kinsol.h "lib" sundials_kinsol)
|
||||
|
||||
@@ -128,15 +128,7 @@ function(add_mfem_miniapp MFEM_EXE_NAME)
|
||||
if (MFEM_USE_CUDA)
|
||||
set_property(SOURCE ${MAIN_LIST} ${EXTRA_SOURCES_LIST}
|
||||
PROPERTY LANGUAGE CUDA)
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
else()
|
||||
set(LIST_)
|
||||
foreach(item IN LISTS EXTRA_OPTIONS_LIST)
|
||||
list(APPEND LIST_ "-Xcompiler=${item}")
|
||||
endforeach()
|
||||
set(EXTRA_OPTIONS_LIST ${LIST_})
|
||||
endif()
|
||||
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
|
||||
endif()
|
||||
|
||||
# Actually add the executable
|
||||
@@ -739,9 +731,9 @@ function(mfem_export_mk_files)
|
||||
MFEM_USE_LAPACK MFEM_THREAD_SAFE MFEM_USE_OPENMP MFEM_USE_LEGACY_OPENMP
|
||||
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE
|
||||
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GNUTLS
|
||||
MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE
|
||||
MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_MPFR MFEM_USE_SIDRE
|
||||
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA
|
||||
MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2)
|
||||
MFEM_USE_UMPIRE)
|
||||
foreach(var ${CONFIG_MK_BOOL_VARS})
|
||||
if (${var})
|
||||
set(${var} YES)
|
||||
@@ -751,7 +743,6 @@ function(mfem_export_mk_files)
|
||||
endforeach()
|
||||
# TODO: Add support for MFEM_USE_CUDA=YES
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${MFEM_CXX})
|
||||
set(MFEM_CPPFLAGS "")
|
||||
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
|
||||
MFEM_CXXFLAGS)
|
||||
|
||||
@@ -48,9 +48,6 @@
|
||||
#ifdef MFEM_USE_PETSC
|
||||
#error Building with PETSc (MFEM_USE_PETSC=YES) requires MPI (MFEM_USE_MPI=YES)
|
||||
#endif
|
||||
#ifdef MFEM_USE_SLEPC
|
||||
#error Building with SLEPc (MFEM_USE_SLEPC=YES) requires MPI (MFEM_USE_MPI=YES)
|
||||
#endif
|
||||
#ifdef MFEM_USE_PUMI
|
||||
#error Building with PUMI (MFEM_USE_PUMI=YES) requires MPI (MFEM_USE_MPI=YES)
|
||||
#endif
|
||||
|
||||
@@ -106,9 +106,6 @@
|
||||
// Enable Sidre support
|
||||
// #define MFEM_USE_SIDRE
|
||||
|
||||
// Enable the use of SIMD in the high performance templated classes
|
||||
// #define MFEM_USE_SIMD
|
||||
|
||||
// Enable Conduit support
|
||||
// #define MFEM_USE_CONDUIT
|
||||
|
||||
@@ -118,9 +115,6 @@
|
||||
// Enable functionality based on the PETSc library
|
||||
// #define MFEM_USE_PETSC
|
||||
|
||||
// Enable functionality based on the SLEPc library
|
||||
// #define MFEM_USE_SLEPC
|
||||
|
||||
// Enable functionality based on the MPFR library.
|
||||
// #define MFEM_USE_MPFR
|
||||
|
||||
@@ -137,9 +131,6 @@
|
||||
// Requires a CUDA compiler (nvcc).
|
||||
// #define MFEM_USE_CUDA
|
||||
|
||||
// Enable MFEM functionality based on the AMGX library.
|
||||
// #define MFEM_USE_AMGX
|
||||
|
||||
// Build the AMD GPU/HIP-enabled version of the MFEM library.
|
||||
// Requires a HIP compiler (hipcc).
|
||||
// #define MFEM_USE_HIP
|
||||
|
||||
@@ -37,7 +37,6 @@ MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
|
||||
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
|
||||
MFEM_USE_PETSC = @MFEM_USE_PETSC@
|
||||
MFEM_USE_SLEPC = @MFEM_USE_SLEPC@
|
||||
MFEM_USE_MPFR = @MFEM_USE_MPFR@
|
||||
MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
|
||||
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
|
||||
@@ -45,18 +44,15 @@ MFEM_USE_PUMI = @MFEM_USE_PUMI@
|
||||
MFEM_USE_HIOP = @MFEM_USE_HIOP@
|
||||
MFEM_USE_GSLIB = @MFEM_USE_GSLIB@
|
||||
MFEM_USE_CUDA = @MFEM_USE_CUDA@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_HIP = @MFEM_USE_HIP@
|
||||
MFEM_USE_RAJA = @MFEM_USE_RAJA@
|
||||
MFEM_USE_OCCA = @MFEM_USE_OCCA@
|
||||
MFEM_USE_CEED = @MFEM_USE_CEED@
|
||||
MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
|
||||
MFEM_USE_SIMD = @MFEM_USE_SIMD@
|
||||
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
|
||||
|
||||
# Compiler, compile options, and link options
|
||||
MFEM_CXX = @MFEM_CXX@
|
||||
MFEM_HOST_CXX = @MFEM_HOST_CXX@
|
||||
MFEM_CPPFLAGS = @MFEM_CPPFLAGS@
|
||||
MFEM_CXXFLAGS = @MFEM_CXXFLAGS@
|
||||
MFEM_TPLFLAGS = @MFEM_TPLFLAGS@
|
||||
|
||||
@@ -39,7 +39,6 @@ option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
|
||||
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
|
||||
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
|
||||
option(MFEM_USE_PETSC "Enable PETSc support." OFF)
|
||||
option(MFEM_USE_SLEPC "Enable SLEPc support." OFF)
|
||||
option(MFEM_USE_MPFR "Enable MPFR usage." OFF)
|
||||
option(MFEM_USE_SIDRE "Enable Axom/Sidre usage" OFF)
|
||||
option(MFEM_USE_CONDUIT "Enable Conduit usage" OFF)
|
||||
@@ -50,7 +49,6 @@ option(MFEM_USE_OCCA "Enable OCCA" OFF)
|
||||
option(MFEM_USE_RAJA "Enable RAJA" OFF)
|
||||
option(MFEM_USE_CEED "Enable CEED" OFF)
|
||||
option(MFEM_USE_UMPIRE "Enable Umpire" OFF)
|
||||
option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" OFF)
|
||||
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
|
||||
|
||||
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
|
||||
@@ -83,18 +81,11 @@ set(HYPRE_DIR "${MFEM_DIR}/../hypre/src/hypre" CACHE PATH
|
||||
# If hypre was compiled to depend on BLAS and LAPACK:
|
||||
# set(HYPRE_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
|
||||
# "Packages that HYPRE depends on.")
|
||||
if (MFEM_USE_CUDA)
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
set(HYPRE_REQUIRED_LIBRARIES "-lcusparse" "-lcurand" CACHE STRING
|
||||
"Libraries that HYPRE depends on.")
|
||||
endif()
|
||||
|
||||
set(METIS_DIR "${MFEM_DIR}/../metis-4.0" CACHE PATH "Path to the METIS library.")
|
||||
|
||||
set(LIBUNWIND_DIR "" CACHE PATH "Path to Libunwind.")
|
||||
|
||||
# For sundials_nvecparhyp and nvecparallel remember to build with MPI_ENABLED=ON
|
||||
# and modify cmake variables for hypre for sundials
|
||||
set(SUNDIALS_DIR "${MFEM_DIR}/../sundials-5.0.0/instdir" CACHE PATH
|
||||
"Path to the SUNDIALS library.")
|
||||
# The following may be necessary, if SUNDIALS was built with KLU:
|
||||
@@ -163,10 +154,6 @@ set(PETSC_DIR "${MFEM_DIR}/../petsc" CACHE PATH
|
||||
"Path to the PETSc main directory.")
|
||||
set(PETSC_ARCH "arch-linux2-c-debug" CACHE STRING "PETSc build architecture.")
|
||||
|
||||
set(SLEPC_DIR "${MFEM_DIR}/../slepc" CACHE PATH
|
||||
"Path to the SLEPc main directory.")
|
||||
set(SLEPC_ARCH "arch-linux2-c-debug" CACHE STRING "SLEPC build architecture.")
|
||||
|
||||
set(MPFR_DIR "" CACHE PATH "Path to the MPFR library.")
|
||||
|
||||
set(CONDUIT_DIR "${MFEM_DIR}/../conduit" CACHE PATH
|
||||
@@ -187,7 +174,6 @@ set(HIOP_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
|
||||
|
||||
set(OCCA_DIR "${MFEM_DIR}/../occa" CACHE PATH "Path to OCCA")
|
||||
set(RAJA_DIR "${MFEM_DIR}/../raja" CACHE PATH "Path to RAJA")
|
||||
set(AMGX_DIR "${MFEM_DIR}/../amgx" CACHE PATH "Path to AMGX")
|
||||
set(CEED_DIR "${MFEM_DIR}/../libCEED" CACHE PATH "Path to libCEED")
|
||||
set(UMPIRE_DIR "${MFEM_DIR}/../umpire" CACHE PATH "Path to Umpire")
|
||||
|
||||
|
||||
+3
-31
@@ -125,7 +125,6 @@ MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_GNUTLS = NO
|
||||
MFEM_USE_NETCDF = NO
|
||||
MFEM_USE_PETSC = NO
|
||||
MFEM_USE_SLEPC = NO
|
||||
MFEM_USE_MPFR = NO
|
||||
MFEM_USE_SIDRE = NO
|
||||
MFEM_USE_CONDUIT = NO
|
||||
@@ -133,13 +132,11 @@ MFEM_USE_PUMI = NO
|
||||
MFEM_USE_HIOP = NO
|
||||
MFEM_USE_GSLIB = NO
|
||||
MFEM_USE_CUDA = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_HIP = NO
|
||||
MFEM_USE_RAJA = NO
|
||||
MFEM_USE_OCCA = NO
|
||||
MFEM_USE_CEED = NO
|
||||
MFEM_USE_UMPIRE = NO
|
||||
MFEM_USE_SIMD = NO
|
||||
MFEM_USE_ADIOS2 = NO
|
||||
|
||||
# Compile and link options for zlib.
|
||||
@@ -155,10 +152,6 @@ LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
|
||||
HYPRE_DIR = @MFEM_DIR@/../hypre/src/hypre
|
||||
HYPRE_OPT = -I$(HYPRE_DIR)/include
|
||||
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
|
||||
ifeq (YES,$(MFEM_USE_CUDA))
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
HYPRE_LIB += -lcusparse -lcurand
|
||||
endif
|
||||
|
||||
# METIS library configuration
|
||||
ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK),NONO)
|
||||
@@ -194,12 +187,10 @@ OPENMP_LIB =
|
||||
POSIX_CLOCKS_LIB = -lrt
|
||||
|
||||
# SUNDIALS library configuration
|
||||
# For sundials_nvecparhyp and nvecparallel remember to build with MPI_ENABLED=ON
|
||||
# and modify cmake variables for hypre for sundials
|
||||
SUNDIALS_DIR = @MFEM_DIR@/../sundials-5.0.0/instdir
|
||||
SUNDIALS_OPT = -I$(SUNDIALS_DIR)/include
|
||||
SUNDIALS_LIB = -Wl,-rpath,$(SUNDIALS_DIR)/lib64 -L$(SUNDIALS_DIR)/lib64\
|
||||
-lsundials_arkode -lsundials_cvodes -lsundials_nvecserial -lsundials_kinsol
|
||||
-lsundials_arkode -lsundials_cvode -lsundials_nvecserial -lsundials_kinsol
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),YES)
|
||||
SUNDIALS_LIB += -lsundials_nvecparhyp -lsundials_nvecparallel
|
||||
@@ -284,20 +275,6 @@ ifeq ($(PETSC_FOUND),YES)
|
||||
-L$(abspath $(PETSC_DIR))/lib -lpetsc $(PETSC_LIB)
|
||||
endif
|
||||
|
||||
SLEPC_DIR := $(MFEM_DIR)/../slepc
|
||||
SLEPC_VARS := $(SLEPC_DIR)/lib/slepc/conf/slepc_variables
|
||||
SLEPC_FOUND := $(if $(wildcard $(SLEPC_VARS)),YES,)
|
||||
SLEPC_INC_VAR = SLEPC_INCLUDE
|
||||
SLEPC_LIB_VAR = SLEPC_EXTERNAL_LIB
|
||||
ifeq ($(SLEPC_FOUND),YES)
|
||||
SLEPC_OPT := $(shell sed -n "s/$(SLEPC_INC_VAR) *= *//p" $(SLEPC_VARS))
|
||||
# Some additional external libraries might be defined in this file
|
||||
-include ${SLEPC_DIR}/${PETSC_ARCH}/lib/slepc/conf/slepcvariables
|
||||
SLEPC_LIB := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
|
||||
SLEPC_LIB := -Wl,-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
|
||||
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc $(SLEPC_LIB)
|
||||
endif
|
||||
|
||||
# MPFR library configuration
|
||||
MPFR_OPT =
|
||||
MPFR_LIB = -lmpfr
|
||||
@@ -346,9 +323,9 @@ GSLIB_DIR = @MFEM_DIR@/../gslib/build
|
||||
GSLIB_OPT = -I$(GSLIB_DIR)/include
|
||||
GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
|
||||
|
||||
# CUDA library configuration
|
||||
# CUDA library configuration (currently not needed)
|
||||
CUDA_OPT =
|
||||
CUDA_LIB = -lcusparse
|
||||
CUDA_LIB =
|
||||
|
||||
# HIP library configuration (currently not needed)
|
||||
HIP_OPT =
|
||||
@@ -377,11 +354,6 @@ UMPIRE_DIR = @MFEM_DIR@/../umpire
|
||||
UMPIRE_OPT = -I$(UMPIRE_DIR)/include
|
||||
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -lumpire
|
||||
|
||||
# AMGX library configuration
|
||||
AMGX_DIR = @MFEM_DIR@/../amgx
|
||||
AMGX_OPT = -I$(AMGX_DIR)/include
|
||||
AMGX_LIB = -L$(CUDA_HOME)/lib64 -lcusparse -lcusolver -lcublas -lnvToolsExt -L$(AMGX_DIR)/lib -lamgx -Xlinker=-rpath=$(CUDA_HOME)/lib64
|
||||
|
||||
# If YES, enable some informational messages
|
||||
VERBOSE = NO
|
||||
|
||||
|
||||
+7
-50
@@ -78,14 +78,6 @@ groups_parallel=(
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{volta,tesla,joule}.cpp"' # todo: multiline sample runs
|
||||
'"convergence"
|
||||
"Convergence tests:"
|
||||
"tests/convergence"
|
||||
"diffusion.cpp"'
|
||||
'"par-mesh-format"
|
||||
"Parallel mesh tests:"
|
||||
"tests/par-mesh-format"
|
||||
"ex1p.cpp"'
|
||||
)
|
||||
# All groups serial + parallel runs mixed in the same group:
|
||||
groups_all=(
|
||||
@@ -115,14 +107,6 @@ groups_all=(
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{volta,tesla,joule}.cpp"' # todo: multiline sample runs
|
||||
'"convergence"
|
||||
"Convergence tests:"
|
||||
"tests/convergence"
|
||||
"diffusion.cpp"'
|
||||
'"par-mesh-format"
|
||||
"Parallel mesh tests:"
|
||||
"tests/par-mesh-format"
|
||||
"ex1p.cpp"'
|
||||
)
|
||||
make_all="all"
|
||||
base_timeformat=$'real: %3Rs user: %3Us sys: %3Ss %%cpu: %P'
|
||||
@@ -396,15 +380,10 @@ function timed_run()
|
||||
# This function is used to execute the sample runs
|
||||
function go()
|
||||
{
|
||||
# Strip leading and trailing spaces from $1 and store the result in cmd_line
|
||||
shopt -s extglob
|
||||
local cmd_line="${1##+( )}"
|
||||
cmd_line="${cmd_line%%+( )}"
|
||||
shopt -u extglob
|
||||
eval local cmd=(${cmd_line})
|
||||
local cmd=("$@")
|
||||
local res=""
|
||||
echo $sep
|
||||
echo "<${group}>" "${cmd_line}"
|
||||
echo "<${group}>" "${cmd[@]}"
|
||||
echo $sep
|
||||
if [ "${timing}" == "yes" ]; then
|
||||
timed_run "${cmd[@]}"
|
||||
@@ -416,15 +395,15 @@ function go()
|
||||
else
|
||||
res="${red}FAILED${none}"
|
||||
fi
|
||||
printf "[${res}] <${group}> ${cmd_line}\n"
|
||||
printf "[${res}] <${group}> ${cmd[*]}\n"
|
||||
if [ "${timing}" == "yes" ]; then
|
||||
printf "Run time: %s\n" "${timer}"
|
||||
timer=(${timer})
|
||||
timer="${timer[1]}"
|
||||
printf -v line "[$res](%8s) ${cmd_line}" "$timer"
|
||||
printf -v line "[$res](%8s) ${cmd[*]}" "$timer"
|
||||
summary=("${summary[@]}" "$line")
|
||||
else
|
||||
summary=("${summary[@]}" "[${res}] ${cmd_line}")
|
||||
summary=("${summary[@]}" "[${res}] ${cmd[*]}")
|
||||
fi
|
||||
echo $sep
|
||||
}
|
||||
@@ -459,7 +438,7 @@ function go_group()
|
||||
fi
|
||||
for run in "${runs[@]}"; do
|
||||
if [ "${run}" == "" ]; then continue; fi
|
||||
eval go \"\${run_prefix} \${run} \${run_suffix}\" $output
|
||||
eval go \${run_prefix} \${run} \${run_suffix} $output
|
||||
done
|
||||
done
|
||||
${make} clean-exec
|
||||
@@ -525,7 +504,7 @@ function echo_run()
|
||||
{
|
||||
echo " $@"
|
||||
{ echo " $@"; echo "$sep";
|
||||
eval "$@"
|
||||
"$@"
|
||||
echo "$sep"; } >> "$echo_log" 2>&1
|
||||
}
|
||||
|
||||
@@ -545,28 +524,6 @@ function build_all()
|
||||
echo_run ${make} config ${mfem_config} || exit 1
|
||||
echo_run ${make} ${make_j} || exit 1
|
||||
echo_run ${make} ${make_all} ${make_j} || exit 1
|
||||
# Build groups in directories other than the directories built by 'make all':
|
||||
for group_params in "${groups[@]}"; do
|
||||
eval params=(${group_params})
|
||||
group_dir="${params[2]}"
|
||||
case "$group_dir" in
|
||||
(examples*|miniapps*)
|
||||
# Built by 'make all'
|
||||
;;
|
||||
(*)
|
||||
if [ "${mfem_dir}" != "${mfem_build_dir}" ]; then
|
||||
echo_run mkdir -p "${group_dir}" || exit 1
|
||||
echo_run cd "${group_dir}" || exit 1
|
||||
echo_run cp -af "${mfem_dir}/${group_dir}/makefile" . || exit 1
|
||||
else
|
||||
echo_run cd "${group_dir}" || exit 1
|
||||
fi
|
||||
echo_run ${make} clean || exit 1
|
||||
echo_run ${make} MFEM_DIR="${mfem_dir}" ${make_j} || exit 1
|
||||
echo_run cd "${mfem_build_dir}" || exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Function that runs all sample runs, given by the array variable "groups".
|
||||
|
||||
+6
-13
@@ -29,20 +29,8 @@
|
||||
#define MFEM_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
// --- MFEM_VECTORIZE_LOOP (disabled)
|
||||
#if (__cplusplus >= 201103L) && !defined(MFEM_DEBUG) && defined(__GNUC__)
|
||||
//#define MFEM_VECTORIZE_LOOP _Pragma("GCC ivdep")
|
||||
#define MFEM_VECTORIZE_LOOP
|
||||
#else
|
||||
#define MFEM_VECTORIZE_LOOP
|
||||
#endif
|
||||
|
||||
// MFEM_TEMPLATE_BLOCK_SIZE is the block size used by the template matrix-matrix
|
||||
// multiply, Mult_AB, defined in tmatrix.hpp. This parameter will generally
|
||||
// require tuning to determine good value. It is probably highly influenced by
|
||||
// the SIMD width when Mult_AB is used with a SIMD type like AutoSIMD.
|
||||
#define MFEM_TEMPLATE_BLOCK_SIZE 4
|
||||
|
||||
#define MFEM_SIMD_SIZE 32
|
||||
#define MFEM_TEMPLATE_ENABLE_SERIALIZE
|
||||
|
||||
// #define MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
|
||||
@@ -50,6 +38,11 @@
|
||||
// #define MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS
|
||||
#define MFEM_TEMPLATE_INTRULE_COEFF_PRECOMP
|
||||
|
||||
// derived macros
|
||||
#define MFEM_ROUNDUP(val,base) ((((val)+(base)-1)/(base))*(base))
|
||||
#define MFEM_ALIGN_SIZE(size,type) \
|
||||
MFEM_ROUNDUP(size,(MFEM_SIMD_SIZE)/sizeof(type))
|
||||
|
||||
#ifdef MFEM_COUNT_FLOPS
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
SetFactory("OpenCASCADE");
|
||||
|
||||
// Select periodic mesh by setting this to either 0 - standard, 1 - periodic
|
||||
periodic = 1;
|
||||
|
||||
// Set the geometry order (1, 2, ..., 9)
|
||||
order = 3;
|
||||
|
||||
// Set the element type (3 - triangles, 4 - quadrilaterals)
|
||||
type = 3;
|
||||
|
||||
// Number of radial elements
|
||||
nrad = 2;
|
||||
|
||||
// Number of azimuthal elements on inner arc
|
||||
nazm1 = 3;
|
||||
|
||||
// Number of azimuthal elements on outer arc
|
||||
nazm2 = 5;
|
||||
|
||||
// Note: Using type = 4 with nazm1 != nazm2 can lead to mixed meshes
|
||||
// containing both triangles and quadrilaterals.
|
||||
|
||||
// Inner and outer radii
|
||||
R1 = 1.0;
|
||||
R2 = 2.0;
|
||||
|
||||
// Angular size of the sector
|
||||
Phi = Pi/3.0;
|
||||
|
||||
Point(1) = {0.0, 0, 0, 1.0};
|
||||
Point(2) = {R1, 0, 0, 1.0};
|
||||
Point(3) = {R2, 0, 0, 1.0};
|
||||
Point(4) = {R1*Cos(Phi), R1*Sin(Phi), 0, 1.0};
|
||||
Point(5) = {R2*Cos(Phi), R2*Sin(Phi), 0, 1.0};
|
||||
Line(1) = {2, 3};
|
||||
Line(2) = {4, 5};
|
||||
Circle(3) = {2, 1, 4};
|
||||
Circle(4) = {3, 1, 5};
|
||||
Curve Loop(5) = {1, 4, -2, -3};
|
||||
Plane Surface(1) = {5};
|
||||
|
||||
Transfinite Curve{1} = nrad+1;
|
||||
Transfinite Curve{2} = nrad+1;
|
||||
Transfinite Curve{3} = nazm1+1;
|
||||
Transfinite Curve{4} = nazm2+1;
|
||||
|
||||
If (nazm1 == nazm2)
|
||||
Transfinite Surface{1};
|
||||
EndIf
|
||||
|
||||
If (type == 4)
|
||||
Recombine Surface {1};
|
||||
EndIf
|
||||
|
||||
// Set a rotation periodicity constraint:
|
||||
If (periodic)
|
||||
Periodic Line{1} = {2} Rotate{{0,0,1}, {0,0,0}, -Phi};
|
||||
EndIf
|
||||
|
||||
// Tag surfaces and volumes with positive integers
|
||||
Physical Curve(1) = {3};
|
||||
Physical Curve(2) = {4};
|
||||
Physical Curve(3) = {1};
|
||||
Physical Curve(4) = {2};
|
||||
Physical Surface(1) = {1};
|
||||
|
||||
// Optimize the high-order mesh
|
||||
// See https://gmsh.info/doc/texinfo/gmsh.html#index-Mesh_002eHighOrderOptimize
|
||||
// Mesh.ElementOrder = order;
|
||||
// Mesh.HighOrderOptimize = 1;
|
||||
|
||||
// Generate 2D mesh
|
||||
Mesh 2;
|
||||
SetOrder order;
|
||||
Mesh.MshFileVersion = 2.2;
|
||||
|
||||
// Check the element quality (the Plugin may be called AnalyseCurvedMesh)
|
||||
// Plugin(AnalyseMeshQuality).JacobianDeterminant = 1;
|
||||
// Plugin(AnalyseMeshQuality).Run;
|
||||
|
||||
If (periodic)
|
||||
Save Sprintf("periodic-annulus-sector-t%01g-o%01g.msh", type, order);
|
||||
Else
|
||||
Save Sprintf("annulus-sector-t%01g-o%01g.msh", type, order);
|
||||
EndIf
|
||||
@@ -1,192 +0,0 @@
|
||||
$MeshFormat
|
||||
2.2 0 8
|
||||
$EndMeshFormat
|
||||
$Nodes
|
||||
136
|
||||
1 1 0 0
|
||||
2 2 0 0
|
||||
3 0.5000000000000001 0.8660254037844386 0
|
||||
4 1 1.732050807568877 0
|
||||
5 1.5 0 0
|
||||
6 1.166666666666667 0 0
|
||||
7 1.333333333333333 0 0
|
||||
8 1.666666666666667 0 0
|
||||
9 1.833333333333333 0 0
|
||||
10 0.7500000000000002 1.299038105676658 0
|
||||
11 0.5833333333333335 1.010362971081845 0
|
||||
12 0.6666666666666667 1.154700538379251 0
|
||||
13 0.8333333333333335 1.443375672974064 0
|
||||
14 0.9166666666666669 1.587713240271471 0
|
||||
15 0.9396926207859085 0.3420201433256683 0
|
||||
16 0.7660444431189786 0.6427876096865386 0
|
||||
17 0.993238357741943 0.1160929141252301 0
|
||||
18 0.9730448705798238 0.2306158707424401 0
|
||||
19 0.8936326403234125 0.4487991802004617 0
|
||||
20 0.8354878114129367 0.5495089780708056 0
|
||||
21 0.6862416378687343 0.7273736415730481 0
|
||||
22 0.597158591702787 0.8021231927550432 0
|
||||
23 1.956295201467611 0.4158233816355181 0
|
||||
24 1.827090915285202 0.8134732861515996 0
|
||||
25 1.618033988749896 1.175570504584944 0
|
||||
26 1.338261212717719 1.486289650954786 0
|
||||
27 1.995128100519648 0.1395129474882505 0
|
||||
28 1.980536137483141 0.278346201920131 0
|
||||
29 1.922523391876638 0.551274711633998 0
|
||||
30 1.879385241571817 0.6840402866513373 0
|
||||
31 1.765895185717855 0.9389431255717802 0
|
||||
32 1.696096192312853 1.059838528466408 0
|
||||
33 1.532088886237958 1.285575219373077 0
|
||||
34 1.438679600677305 1.389316740917992 0
|
||||
35 1.231322950651319 1.576021507213442 0
|
||||
36 1.118385806941496 1.658075145110082 0
|
||||
37 1.162276263405681 0.6710405135499813 0
|
||||
38 1.248615852873337 1.079531485311822 0
|
||||
39 1.559209616901855 0.5415673055003691 0
|
||||
40 1.478306597054007 0.8535007117539289 0
|
||||
41 0.9210953433941653 0.9653302893212266 0
|
||||
42 1.296548225291847 0.3150268220262836 0
|
||||
43 1.055002035226811 1.358510675893086 0
|
||||
44 1.704005774249187 0.2344032256041583 0
|
||||
45 0.6403651144647218 0.8991270322967013 0
|
||||
46 0.7807302289294435 0.9322286608089638 0
|
||||
47 0.864063562262777 1.07656622810637 0
|
||||
48 0.8070317811313885 1.187802166891514 0
|
||||
49 0.7236984477980553 1.043464599594108 0
|
||||
50 1.432182741763949 0.1050089406754279 0
|
||||
51 1.364365483527898 0.2100178813508558 0
|
||||
52 1.197698816861231 0.2100178813508558 0
|
||||
53 1.098849408430616 0.1050089406754279 0
|
||||
54 1.265516075097282 0.1050089406754278 0
|
||||
55 0.8177280765440409 0.7503018362314346 0
|
||||
56 0.869411709969103 0.8578160627763305 0
|
||||
57 0.7348318576552288 0.8316090412164392 0
|
||||
58 1.177596357123201 0.3240245957927452 0
|
||||
59 1.058644488954555 0.3330223695592067 0
|
||||
60 1.087610484537871 0.2205785356313179 0
|
||||
61 1.267619707955123 0.7318605796179638 0
|
||||
62 1.372963152504565 0.7926806456859463 0
|
||||
63 1.40174301566045 0.9288443029398934 0
|
||||
64 1.325179434266893 1.004187894125858 0
|
||||
65 1.219835989717452 0.9433678280578752 0
|
||||
66 1.191056126561566 0.8072041708039283 0
|
||||
67 1.296399571111008 0.8680242368719107 0
|
||||
68 1.532241943619239 0.645545107584889 0
|
||||
69 1.505274270336623 0.7495229096694089 0
|
||||
70 1.294587381237739 0.6278827775334439 0
|
||||
71 1.426898499069797 0.5847250415169065 0
|
||||
72 1.399930825787181 0.6887028436014264 0
|
||||
73 1.139442349713613 1.041464419981624 0
|
||||
74 1.030268846553889 1.003397354651425 0
|
||||
75 1.001488983398004 0.8672336973974781 0
|
||||
76 1.081882623401843 0.7691371054737297 0
|
||||
77 1.110662486557728 0.9053007627276766 0
|
||||
78 1.207033584034403 0.5523692830420821 0
|
||||
79 1.251790904663125 0.4336980525341829 0
|
||||
80 1.384102022495183 0.3905403165176455 0
|
||||
81 1.471655819698519 0.4660538110090073 0
|
||||
82 1.339344701866461 0.5092115470255447 0
|
||||
83 1.73779714915742 0.7228379592678562 0
|
||||
84 1.648503383029637 0.6322026323841127 0
|
||||
85 1.691571478423774 0.4996526642120855 0
|
||||
86 1.823933339945692 0.4577380229238018 0
|
||||
87 1.787039416783436 0.5922941012619014 0
|
||||
88 1.308379426102925 1.350703595740465 0
|
||||
89 1.278497639488131 1.215117540526144 0
|
||||
90 1.371755231498856 1.111544491736196 0
|
||||
91 1.494894610124376 1.14355749816057 0
|
||||
92 1.4064614465962 1.25147448186763 0
|
||||
93 1.013887168325833 0.451693600067106 0
|
||||
94 1.088081715865757 0.5613670568085436 0
|
||||
95 1.03019898997678 0.6616228789288338 0
|
||||
96 0.8981217165478794 0.6522052443076862 0
|
||||
97 0.9637989050473432 0.5564495572737495 0
|
||||
98 1.432367408277627 0.2881522898855752 0
|
||||
99 1.568186591263407 0.2612777577448668 0
|
||||
100 1.655740388466743 0.3367912522362286 0
|
||||
101 1.607475002684299 0.4391792788682989 0
|
||||
102 1.519921205480963 0.3636657843769371 0
|
||||
103 1.184077913657828 1.17252454883891 0
|
||||
104 1.119539974442319 1.265517612365998 0
|
||||
105 1.010366471282595 1.2274505470358 0
|
||||
106 0.9657309073383804 1.096390418178513 0
|
||||
107 1.074904410498104 1.134457483508712 0
|
||||
108 1.901335258083062 0.07813440853471942 0
|
||||
109 1.802670516166124 0.1562688170694388 0
|
||||
110 1.636003849499458 0.156268817069439 0
|
||||
111 1.568001924749729 0.07813440853471942 0
|
||||
112 1.734668591416396 0.07813440853471944 0
|
||||
113 0.8516673450756037 1.318862295748801 0
|
||||
114 0.9533346901512071 1.338686485820944 0
|
||||
115 1.03666802348454 1.48302405311835 0
|
||||
116 1.01833401174227 1.607537430343614 0
|
||||
117 0.9350006784089369 1.463199863046207 0
|
||||
118 1.710829475874804 0.8268157613523761 0
|
||||
119 1.594568036464405 0.8401582365531526 0
|
||||
120 1.621535709747021 0.7361804344686326 0
|
||||
121 1.52488239428597 0.9608573093642675 0
|
||||
122 1.571458191517933 1.068213906974606 0
|
||||
123 1.448318812892413 1.036200900550232 0
|
||||
124 0.908699126206992 1.207626356963657 0
|
||||
125 1.500184666513678 0.1831433492101474 0
|
||||
126 1.646765991694905 0.9507607885973723 0
|
||||
127 0.9498053499729417 0.7597194708525821 0
|
||||
128 1.132839036494479 0.4426958263006443 0
|
||||
129 1.149421761057113 1.40110366758032 0
|
||||
130 1.243841486887416 1.443696659267553 0
|
||||
131 1.134903597606542 1.530869109405537 0
|
||||
132 1.872198725728137 0.3553499962917315 0
|
||||
133 1.788102249988661 0.2948766109479449 0
|
||||
134 1.893223337417325 0.2174207916708467 0
|
||||
135 1.213959700272622 1.308110604053232 0
|
||||
136 1.739836864206217 0.3972646375800152 0
|
||||
$EndNodes
|
||||
$Elements
|
||||
38
|
||||
1 26 2 3 1 1 5 6 7
|
||||
2 26 2 3 1 5 2 8 9
|
||||
3 26 2 4 2 3 10 11 12
|
||||
4 26 2 4 2 10 4 13 14
|
||||
5 26 2 1 3 1 15 17 18
|
||||
6 26 2 1 3 15 16 19 20
|
||||
7 26 2 1 3 16 3 21 22
|
||||
8 26 2 2 4 2 23 27 28
|
||||
9 26 2 2 4 23 24 29 30
|
||||
10 26 2 2 4 24 25 31 32
|
||||
11 26 2 2 4 25 26 33 34
|
||||
12 26 2 2 4 26 4 35 36
|
||||
13 21 2 1 1 3 41 10 45 46 47 48 12 11 49
|
||||
14 21 2 1 1 5 42 1 50 51 52 53 6 7 54
|
||||
15 21 2 1 1 16 41 3 55 56 46 45 22 21 57
|
||||
16 21 2 1 1 1 42 15 53 52 58 59 18 17 60
|
||||
17 21 2 1 1 37 40 38 61 62 63 64 65 66 67
|
||||
18 21 2 1 1 39 40 37 68 69 62 61 70 71 72
|
||||
19 21 2 1 1 38 41 37 73 74 75 76 66 65 77
|
||||
20 21 2 1 1 37 42 39 78 79 80 81 71 70 82
|
||||
21 21 2 1 1 24 39 23 83 84 85 86 29 30 87
|
||||
22 21 2 1 1 26 38 25 88 89 90 91 33 34 92
|
||||
23 21 2 1 1 15 37 16 93 94 95 96 20 19 97
|
||||
24 21 2 1 1 42 44 39 98 99 100 101 81 80 102
|
||||
25 21 2 1 1 38 43 41 103 104 105 106 74 73 107
|
||||
26 21 2 1 1 2 44 5 108 109 110 111 8 9 112
|
||||
27 21 2 1 1 10 43 4 113 114 115 116 14 13 117
|
||||
28 21 2 1 1 24 40 39 118 119 69 68 84 83 120
|
||||
29 21 2 1 1 38 40 25 64 63 121 122 91 90 123
|
||||
30 21 2 1 1 41 43 10 106 105 114 113 48 47 124
|
||||
31 21 2 1 1 5 44 42 111 110 99 98 51 50 125
|
||||
32 21 2 1 1 25 40 24 122 121 119 118 31 32 126
|
||||
33 21 2 1 1 37 41 16 76 75 56 55 96 95 127
|
||||
34 21 2 1 1 15 42 37 59 58 79 78 94 93 128
|
||||
35 21 2 1 1 4 43 26 116 115 129 130 35 36 131
|
||||
36 21 2 1 1 23 44 2 132 133 109 108 27 28 134
|
||||
37 21 2 1 1 26 43 38 130 129 104 103 89 88 135
|
||||
38 21 2 1 1 39 44 23 101 100 133 132 86 85 136
|
||||
$EndElements
|
||||
$Periodic
|
||||
1
|
||||
1 1 2
|
||||
Affine 0.5000000000000001 0.8660254037844386 0 0 -0.8660254037844386 0.5000000000000001 0 0 0 0 1 0 0 0 0 1
|
||||
3
|
||||
5 10
|
||||
1 3
|
||||
2 4
|
||||
$EndPeriodic
|
||||
@@ -1,141 +0,0 @@
|
||||
// Select periodic mesh by setting this to either 0 - standard, 1 - periodic
|
||||
periodic = 1;
|
||||
|
||||
// Set the geometry order (1, 2, ..., 10 for tetrahedra or 9 for other types)
|
||||
order = 3;
|
||||
|
||||
// Set the element type (4 - tetrahedra, 6 - wedges, 8 - hexahedra)
|
||||
type = 8;
|
||||
|
||||
// Minor and major radii
|
||||
R1 = 1.0;
|
||||
R2 = 2.0;
|
||||
|
||||
// Side length of interior square
|
||||
A1 = 0.8;
|
||||
|
||||
// Angular size of the sector
|
||||
Phi = Pi/3.0;
|
||||
|
||||
// Number of azimuthal elements
|
||||
nazm = 3;
|
||||
|
||||
// Number of elements around a quarter of the circle
|
||||
narc = 2;
|
||||
|
||||
// Number of elements between surface and interior square
|
||||
nshl = 1;
|
||||
|
||||
lc = 0.5;
|
||||
a1 = A1 / Sqrt(2.0);
|
||||
|
||||
Point(1) = {R2+R1, 0, 0, lc};
|
||||
Point(2) = {R2, 0, R1, lc};
|
||||
Point(3) = {R2-R1, 0, 0, lc};
|
||||
Point(4) = {R2, 0, -R1, lc};
|
||||
Point(5) = {R2, 0, 0, lc};
|
||||
Point(6) = {R2+a1, 0, 0, lc};
|
||||
Point(7) = {R2, 0, a1, lc};
|
||||
Point(8) = {R2-a1, 0, 0, lc};
|
||||
Point(9) = {R2, 0, -a1, lc};
|
||||
|
||||
Circle(1) = {1,5,2};
|
||||
Circle(2) = {2,5,3};
|
||||
Circle(3) = {3,5,4};
|
||||
Circle(4) = {4,5,1};
|
||||
|
||||
Line(5) = {6,1};
|
||||
Line(6) = {7,2};
|
||||
Line(7) = {8,3};
|
||||
Line(8) = {9,4};
|
||||
|
||||
Line(9) = {6, 7};
|
||||
Line(10) = {7, 8};
|
||||
Line(11) = {8, 9};
|
||||
Line(12) = {9, 6};
|
||||
|
||||
Line Loop(101) = {1, -6, -9, 5};
|
||||
Line Loop(102) = {2, -7, -10, 6};
|
||||
Line Loop(103) = {3, -8, -11, 7};
|
||||
Line Loop(104) = {4, -5, -12, 8};
|
||||
Line Loop(105) = {9, 10, 11, 12};
|
||||
|
||||
Plane Surface(201) = {101};
|
||||
Plane Surface(202) = {102};
|
||||
Plane Surface(203) = {103};
|
||||
Plane Surface(204) = {104};
|
||||
Plane Surface(205) = {105};
|
||||
|
||||
Transfinite Curve{1} = narc+1;
|
||||
Transfinite Curve{2} = narc+1;
|
||||
Transfinite Curve{3} = narc+1;
|
||||
Transfinite Curve{4} = narc+1;
|
||||
|
||||
Transfinite Curve{5} = nshl+1;
|
||||
Transfinite Curve{6} = nshl+1;
|
||||
Transfinite Curve{7} = nshl+1;
|
||||
Transfinite Curve{8} = nshl+1;
|
||||
|
||||
Transfinite Curve{9} = narc+1;
|
||||
Transfinite Curve{10} = narc+1;
|
||||
Transfinite Curve{11} = narc+1;
|
||||
Transfinite Curve{12} = narc+1;
|
||||
|
||||
If (type == 8)
|
||||
Recombine Surface {201};
|
||||
Recombine Surface {202};
|
||||
Recombine Surface {203};
|
||||
Recombine Surface {204};
|
||||
Recombine Surface {205};
|
||||
|
||||
Transfinite Surface {201} = {1,2,7,6};
|
||||
Transfinite Surface {202} = {2,3,8,7};
|
||||
Transfinite Surface {203} = {3,4,9,8};
|
||||
Transfinite Surface {204} = {4,1,6,9};
|
||||
Transfinite Surface {205} = {6,7,8,9};
|
||||
EndIf
|
||||
|
||||
If (type == 4)
|
||||
Extrude { {0,0,1} , {0,0,0} , Phi} {
|
||||
Surface{201,202,203,204,205}; Layers{nazm};
|
||||
}
|
||||
Else
|
||||
Extrude { {0,0,1} , {0,0,0} , Phi} {
|
||||
Surface{201,202,203,204,205}; Layers{nazm}; Recombine;
|
||||
}
|
||||
EndIf
|
||||
|
||||
// Set a rotation periodicity constraint:
|
||||
If (periodic)
|
||||
Periodic Surface{227} = {201} Rotate{{0,0,1}, {0,0,0}, Phi};
|
||||
Periodic Surface{249} = {202} Rotate{{0,0,1}, {0,0,0}, Phi};
|
||||
Periodic Surface{271} = {203} Rotate{{0,0,1}, {0,0,0}, Phi};
|
||||
Periodic Surface{293} = {204} Rotate{{0,0,1}, {0,0,0}, Phi};
|
||||
Periodic Surface{315} = {205} Rotate{{0,0,1}, {0,0,0}, Phi};
|
||||
EndIf
|
||||
|
||||
// Tag surfaces and volumes with positive integers
|
||||
Physical Surface(1) = {201,202,203,204,205};
|
||||
Physical Surface(2) = {227,249,271,293,315};
|
||||
Physical Surface(3) = {214,236,258,280};
|
||||
Physical Volume(1) = {1,2,3,4,5};
|
||||
|
||||
// Optimize the high-order mesh
|
||||
// See https://gmsh.info/doc/texinfo/gmsh.html#index-Mesh_002eHighOrderOptimize
|
||||
// Mesh.ElementOrder = order;
|
||||
// Mesh.HighOrderOptimize = 1;
|
||||
|
||||
// Generate 3D mesh
|
||||
Mesh 3;
|
||||
SetOrder order;
|
||||
Mesh.MshFileVersion = 2.2;
|
||||
|
||||
// Check the element quality (the Plugin may be called AnalyseCurvedMesh)
|
||||
// Plugin(AnalyseMeshQuality).JacobianDeterminant = 1;
|
||||
// Plugin(AnalyseMeshQuality).Run;
|
||||
|
||||
If (periodic)
|
||||
Save Sprintf("periodic-torus-sector-t%01g-o%01g.msh", type, order);
|
||||
Else
|
||||
Save Sprintf("torus-sector-t%01g-o%01g.msh", type, order);
|
||||
EndIf
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
5
|
||||
1 3 0 3 7 4
|
||||
1 3 3 2 6 7
|
||||
1 3 2 1 5 6
|
||||
1 3 1 0 4 5
|
||||
1 3 2 8 9 1
|
||||
|
||||
boundary
|
||||
10
|
||||
1 1 0 3
|
||||
2 1 3 2
|
||||
2 1 1 0
|
||||
2 1 2 8
|
||||
2 1 9 1
|
||||
3 1 7 4
|
||||
3 1 6 7
|
||||
3 1 5 6
|
||||
3 1 4 5
|
||||
4 1 8 9
|
||||
|
||||
edges
|
||||
15
|
||||
0 0 4
|
||||
0 3 7
|
||||
0 1 5
|
||||
0 2 6
|
||||
1 0 3
|
||||
1 4 7
|
||||
2 3 2
|
||||
2 7 6
|
||||
2 1 0
|
||||
2 5 4
|
||||
1 2 1
|
||||
1 6 5
|
||||
1 8 9
|
||||
3 2 8
|
||||
3 1 9
|
||||
|
||||
vertices
|
||||
10
|
||||
|
||||
patches
|
||||
|
||||
knotvectors
|
||||
2
|
||||
2 3 0 0 0 1 1 1
|
||||
2 4 0 0 0 0.5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints_cartesian
|
||||
-5 5 1
|
||||
-5 3.92523e-16 1
|
||||
-5 -5 1
|
||||
-2.47593 2.47593 1
|
||||
-4.95187 6.06429e-16 0.707107
|
||||
-2.47593 -2.47593 1
|
||||
-0.424264 0.424264 1
|
||||
-0.848528 1.03915e-16 0.707107
|
||||
-0.424264 -0.424264 1
|
||||
-0.353553 0.353553 1
|
||||
-0.707107 8.65956e-17 0.707107
|
||||
-0.353553 -0.353553 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
2 3 0 0 0 1 1 1
|
||||
2 4 0 0 0 0.5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints_cartesian
|
||||
-5 -5 1
|
||||
-1.17757e-15 -5 1
|
||||
5 -5 1
|
||||
-2.47593 -2.47593 1
|
||||
-9.09644e-16 -4.95187 0.707107
|
||||
2.47593 -2.47593 1
|
||||
-0.424264 -0.424264 1
|
||||
-1.55872e-16 -0.848528 0.707107
|
||||
0.424264 -0.424264 1
|
||||
-0.353553 -0.353553 1
|
||||
-1.29893e-16 -0.707107 0.707107
|
||||
0.353553 -0.353553 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
2 3 0 0 0 1 1 1
|
||||
2 4 0 0 0 0.5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints_cartesian
|
||||
5 -5 1
|
||||
5 -1.17757e-15 1
|
||||
5 5 1
|
||||
2.47593 -2.47593 1
|
||||
4.95187 -1.21286e-15 0.707107
|
||||
2.47593 2.47593 1
|
||||
0.424264 -0.424264 1
|
||||
0.848528 -2.07829e-16 0.707107
|
||||
0.424264 0.424264 1
|
||||
0.353553 -0.353553 1
|
||||
0.707107 -1.73191e-16 0.707107
|
||||
0.353553 0.353553 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
2 3 0 0 0 1 1 1
|
||||
2 4 0 0 0 0.5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints_cartesian
|
||||
5 5 1
|
||||
3.92523e-16 5 1
|
||||
-5 5 1
|
||||
2.47593 2.47593 1
|
||||
3.03215e-16 4.95187 0.707107
|
||||
-2.47593 2.47593 1
|
||||
0.424264 0.424264 1
|
||||
5.19574e-17 0.848528 0.707107
|
||||
-0.424264 0.424264 1
|
||||
0.353553 0.353553 1
|
||||
4.32978e-17 0.707107 0.707107
|
||||
-0.353553 0.353553 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
2 3 0 0 0 1 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints_cartesian
|
||||
5 -5 1
|
||||
10 -5 1
|
||||
15 -5 1
|
||||
5 0 1
|
||||
10 0 1
|
||||
15 0 1
|
||||
5 5 1
|
||||
10 5 1
|
||||
15 5 1
|
||||
@@ -1,118 +0,0 @@
|
||||
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
|
||||
2
|
||||
|
||||
elements
|
||||
20
|
||||
1 3 0 1 6 5
|
||||
1 3 1 2 7 6
|
||||
1 3 2 3 8 7
|
||||
1 3 3 4 9 8
|
||||
1 3 5 6 11 10
|
||||
1 2 6 7 11
|
||||
1 2 7 12 11
|
||||
1 2 7 8 13
|
||||
1 2 7 13 12
|
||||
1 3 8 9 14 13
|
||||
1 3 10 11 16 15
|
||||
1 2 11 12 17
|
||||
1 2 11 17 16
|
||||
1 2 12 13 17
|
||||
1 2 13 18 17
|
||||
1 3 13 14 19 18
|
||||
1 3 15 16 21 20
|
||||
1 3 16 17 22 21
|
||||
1 3 17 18 23 22
|
||||
1 3 18 19 24 23
|
||||
|
||||
boundary
|
||||
16
|
||||
2 1 0 1
|
||||
2 1 1 2
|
||||
2 1 2 3
|
||||
2 1 3 4
|
||||
2 1 21 20
|
||||
2 1 22 21
|
||||
2 1 23 22
|
||||
2 1 24 23
|
||||
1 1 5 0
|
||||
1 1 10 5
|
||||
1 1 15 10
|
||||
1 1 20 15
|
||||
1 1 4 9
|
||||
1 1 9 14
|
||||
1 1 14 19
|
||||
1 1 19 24
|
||||
|
||||
vertices
|
||||
25
|
||||
|
||||
nodes
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: H1_2D_P1
|
||||
VDim: 2
|
||||
Ordering: 0
|
||||
|
||||
0
|
||||
0.25
|
||||
0.5
|
||||
0.75
|
||||
1
|
||||
0
|
||||
0.25
|
||||
0.5
|
||||
0.75
|
||||
1
|
||||
0
|
||||
0.25
|
||||
0.5
|
||||
0.75
|
||||
1
|
||||
0
|
||||
0.25
|
||||
0.5
|
||||
0.75
|
||||
1
|
||||
0
|
||||
0.25
|
||||
0.5
|
||||
0.75
|
||||
1
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0.25
|
||||
0.25
|
||||
0.25
|
||||
0.25
|
||||
0.25
|
||||
0.5
|
||||
0.5
|
||||
0.5
|
||||
0.5
|
||||
0.5
|
||||
0.75
|
||||
0.75
|
||||
0.75
|
||||
0.75
|
||||
0.75
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
+29
-14
@@ -16,21 +16,36 @@ if (DOXYGEN_FOUND)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CodeDocumentation.conf.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf @ONLY)
|
||||
|
||||
if (UNIX)
|
||||
# Only create symlinks if UNIX operating system
|
||||
add_custom_target(doc
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
|
||||
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen to CodeDocumentation.html"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(doc
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf
|
||||
COMMAND echo "<meta http-equiv=\"REFRESH\" content=\"0;URL=CodeDocumentation/html/index.html\">" > ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
|
||||
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen to CodeDocumentation.html"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(clean-doc
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/warnings.log
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation
|
||||
COMMENT "Removing API documentation"
|
||||
VERBATIM)
|
||||
add_custom_target(clean-doc
|
||||
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation
|
||||
COMMENT "Removing API documentation"
|
||||
VERBATIM)
|
||||
|
||||
else (UNIX)
|
||||
add_custom_target(doc
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf
|
||||
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen to CodeDocumentation/html/index.html"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(clean-doc
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation
|
||||
COMMENT "Removing API documentation"
|
||||
VERBATIM)
|
||||
endif (UNIX)
|
||||
endif (DOXYGEN_FOUND)
|
||||
|
||||
@@ -51,7 +51,7 @@ PROJECT_BRIEF = "Finite element discretization library"
|
||||
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
|
||||
# the logo to the output directory.
|
||||
|
||||
PROJECT_LOGO = web/logo-small.png
|
||||
PROJECT_LOGO =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
@@ -746,7 +746,7 @@ WARN_FORMAT = "$file:$line: $text"
|
||||
# messages should be written. If left blank the output is written to standard
|
||||
# error (stderr).
|
||||
|
||||
WARN_LOGFILE = warnings.log
|
||||
WARN_LOGFILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the input files
|
||||
@@ -770,7 +770,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/examples/pumi \
|
||||
@MFEM_SOURCE_DIR@/examples/hiop \
|
||||
@MFEM_SOURCE_DIR@/examples/sundials \
|
||||
@MFEM_SOURCE_DIR@/miniapps/adjoint \
|
||||
@MFEM_SOURCE_DIR@/miniapps/common \
|
||||
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
|
||||
@MFEM_SOURCE_DIR@/miniapps/gslib \
|
||||
@@ -1471,7 +1470,7 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# The default value is: http://cdn.mathjax.org/mathjax/latest.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
MATHJAX_RELPATH = https://cdn.llnl.gov/mathjax/2.7.2
|
||||
|
||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||
# extension names that should be enabled during MathJax rendering. For example
|
||||
|
||||
@@ -88,8 +88,8 @@ namespace mfem {
|
||||
* - <a class="el" href="ex24p_8cpp_source.html">Example 24p</a>: parallel mixed finite element spaces and interpolators
|
||||
* - <a class="el" href="ex25_8cpp_source.html">Example 25</a>: simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
|
||||
* - <a class="el" href="ex25p_8cpp_source.html">Example 25p</a>: parallel simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
|
||||
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Laplace problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Laplace problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Laplace problem using nodal H1 FEM
|
||||
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Laplace problem using nodal H1 FEM
|
||||
*
|
||||
* <H4>SUNDIALS Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -101,9 +101,6 @@ namespace mfem {
|
||||
* and
|
||||
* <a class="el" href="sundials_2ex16p_8cpp_source.html">16p</a>
|
||||
* demonstrating the use of MFEM's \link sundials.hpp SUNDIALS classes\endlink
|
||||
* - CVODES adjoint examples:
|
||||
* <a class="el" href="cvsRoberts__ASAi__dns_8cpp_source.html">serial ODE system</a>,
|
||||
* <a class="el" href="adjoint__advection__diffusion_8cpp_source.html">parallel advection-diffusion</a>
|
||||
*
|
||||
* <H4>PETSc Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -143,18 +140,15 @@ namespace mfem {
|
||||
* - <a class="el" href="tesla_8cpp_source.html">Tesla</a>: simple magnetostatics simulation code
|
||||
* - <a class="el" href="maxwell_8cpp_source.html">Maxwell</a>: simple transient full-wave electromagnetics simulation code
|
||||
* - <a class="el" href="joule_8cpp_source.html">Joule</a>: transient magnetics and Joule heating miniapp
|
||||
* - <a class="el" href="classmfem_1_1navier_1_1NavierSolver.html">Navier</a>: solve the transient incompressible Navier-Stokes equations
|
||||
* - <a class="el" href="mobius-strip_8cpp_source.html">Mobius Strip</a>: generate various Mobius strip-like meshes
|
||||
* - <a class="el" href="klein-bottle_8cpp_source.html">Klein Bottle</a>: generate three types of Klein bottle surfaces
|
||||
* - <a class="el" href="toroid_8cpp_source.html">Toroid</a>: generate simple toroidal meshes
|
||||
* - <a class="el" href="twist_8cpp_source.html">Twist</a>: generate simple periodic meshes
|
||||
* - <a class="el" href="minimal-surface_8cpp_source.html">Minimal Surface</a>: compute minimal surfaces, <a class="el" href="minimal-surface_8cpp_source.html">serial</a> and <a class="el" href="pminimal-surface_8cpp_source.html">parallel</a> versions
|
||||
* - <a class="el" href="polar-nc_8cpp_source.html">Polar NC</a>: generate polar non-conforming meshes
|
||||
* - <a class="el" href="shaper_8cpp_source.html">Shaper</a>: resolve material interfaces by mesh refinement
|
||||
* - <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="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
|
||||
* - <a class="el" href="load-dc_8cpp_source.html">Load DC</a>: visualize fields saved via DataCollection classes
|
||||
@@ -162,7 +156,7 @@ namespace mfem {
|
||||
* - <a class="el" href="lor-transfer_8cpp_source.html">LOR Transfer</a>: map functions between high-order and low-order refined spaces
|
||||
* - <a class="el" href="findpts_8cpp_source.html">Find Points</a>: evaluate grid function in physical space, <a class="el" href="findpts_8cpp_source.html">serial</a> and <a class="el" href="pfindpts_8cpp_source.html">parallel</a> versions
|
||||
* - <a class="el" href="field-diff_8cpp_source.html">Field Diff</a>: compare grid functions on different meshes
|
||||
* - <a class="el" href="field-interp_8cpp_source.html">Field Interp</a>: transfer a grid functions betwen meshes
|
||||
* - <a class="el" href="classmfem_1_1navier_1_1NavierSolver.html">Navier</a>: solve the transient incompressible Navier-Stokes equations
|
||||
* - <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
|
||||
*
|
||||
|
||||
+4
-11
@@ -9,25 +9,18 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
SHELL = /bin/bash
|
||||
MFEM_DIR ?= ..
|
||||
DOXYGEN_CONF = CodeDocumentation.conf
|
||||
|
||||
|
||||
# doxygen uses: graphviz, latex
|
||||
html: $(DOXYGEN_CONF)
|
||||
@# Generate the html documentation
|
||||
@doxygen $(DOXYGEN_CONF)
|
||||
@echo "<meta http-equiv=\"REFRESH\" content=\"0;URL=CodeDocumentation/html/index.html\">" > CodeDocumentation.html
|
||||
@cat warnings.log 1>&2
|
||||
@# Generate the log of undocumented methods
|
||||
@( cat $(DOXYGEN_CONF) ; echo "GENERATE_HTML=NO" ; echo "EXTRACT_ALL=NO" ; echo "WARN_LOGFILE=undoc.log" ; echo "QUIET=YES" ) | doxygen - &> /dev/null
|
||||
doxygen $(DOXYGEN_CONF)
|
||||
rm -f CodeDocumentation.html
|
||||
ln -s CodeDocumentation/html/index.html CodeDocumentation.html
|
||||
|
||||
clean:
|
||||
rm -rf $(DOXYGEN_CONF) CodeDocumentation CodeDocumentation.html *~
|
||||
rm -rf undoc.log warnings.log
|
||||
|
||||
$(DOXYGEN_CONF): $(MFEM_DIR)/doc/$(DOXYGEN_CONF).in
|
||||
@sed -e 's%@MFEM_SOURCE_DIR@%$(MFEM_DIR)%g' $(<) \
|
||||
sed -e 's%@MFEM_SOURCE_DIR@%$(MFEM_DIR)%g' $(<) \
|
||||
> $(DOXYGEN_CONF)
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
+4
-11
@@ -64,6 +64,8 @@ if (MFEM_USE_MPI)
|
||||
ex25p.cpp
|
||||
ex26p.cpp
|
||||
ex27p.cpp
|
||||
pa_oper.cpp
|
||||
io_benchmark.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -91,7 +93,7 @@ foreach(SRC_FILE ${ALL_EXE_SRCS})
|
||||
add_test(NAME ${TEST_NAME}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
else()
|
||||
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
|
||||
add_test(NAME ${TEST_NAME}_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
@@ -101,22 +103,13 @@ endforeach()
|
||||
|
||||
# If STRUMPACK is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_STRUMPACK)
|
||||
add_test(NAME ex11p_strumpack_np=${MFEM_MPI_NP}
|
||||
add_test(NAME ex11p_strumpack_np=4
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex11p> "-no-vis" "--strumpack"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
|
||||
# If SuperLU_DIST is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_SUPERLU)
|
||||
add_test(NAME ex11p_superlu_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex11p> "-no-vis" "--superlu"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
|
||||
# Include the examples/sundials directory if SUNDIALS is enabled.
|
||||
if (MFEM_USE_SUNDIALS)
|
||||
add_subdirectory(sundials)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"config_version": 2,
|
||||
"solver": {
|
||||
"preconditioner": {
|
||||
"print_grid_stats": 1,
|
||||
"print_vis_data": 0,
|
||||
"solver": "AMG",
|
||||
"smoother": {
|
||||
"scope": "jacobi",
|
||||
"solver": "BLOCK_JACOBI",
|
||||
"relaxation_factor": 0.7,
|
||||
"monitor_residual": 0,
|
||||
"print_solve_stats": 0
|
||||
},
|
||||
"print_solve_stats": 0,
|
||||
"presweeps": 1,
|
||||
"interpolator": "D2",
|
||||
"max_row_sum" : 0.9,
|
||||
"strength_threshold" : 0.25,
|
||||
"max_iters": 1,
|
||||
"monitor_residual": 0,
|
||||
"store_res_history": 0,
|
||||
"scope": "amg",
|
||||
"max_levels": 100,
|
||||
"cycle": "V",
|
||||
"postsweeps": 1
|
||||
},
|
||||
"solver": "PCG",
|
||||
"print_solve_stats": 1,
|
||||
"obtain_timings": 1,
|
||||
"max_iters": 100,
|
||||
"monitor_residual": 1,
|
||||
"convergence": "RELATIVE_MAX",
|
||||
"scope": "main",
|
||||
"tolerance": 1e-12,
|
||||
"norm": "L2"
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
// MFEM Example 1
|
||||
//
|
||||
// Compile with: make ex1
|
||||
//
|
||||
// Sample runs: ex1 -m ../../data/square-disc.mesh
|
||||
// ex1 -m ../../data/star.mesh
|
||||
// ex1 -m ../../data/star-mixed.mesh
|
||||
// ex1 -m ../../data/escher.mesh
|
||||
// ex1 -m ../../data/fichera.mesh
|
||||
// ex1 -m ../../data/fichera-mixed.mesh
|
||||
// ex1 -m ../../data/toroid-wedge.mesh
|
||||
// ex1 -m ../../data/periodic-annulus-sector.msh
|
||||
// ex1 -m ../../data/periodic-torus-sector.msh
|
||||
// ex1 -m ../../data/square-disc-p2.vtk -o 2
|
||||
// ex1 -m ../../data/square-disc-p3.mesh -o 3
|
||||
// ex1 -m ../../data/square-disc-nurbs.mesh -o -1
|
||||
// ex1 -m ../../data/star-mixed-p2.mesh -o 2
|
||||
// ex1 -m ../../data/disc-nurbs.mesh -o -1
|
||||
// ex1 -m ../../data/pipe-nurbs.mesh -o -1
|
||||
// ex1 -m ../../data/fichera-mixed-p2.mesh -o 2
|
||||
// ex1 -m ../../data/star-surf.mesh
|
||||
// ex1 -m ../../data/square-disc-surf.mesh
|
||||
// ex1 -m ../../data/inline-segment.mesh
|
||||
// ex1 -m ../../data/amr-quad.mesh
|
||||
// ex1 -m ../../data/amr-hex.mesh
|
||||
// ex1 -m ../../data/fichera-amr.mesh
|
||||
// ex1 -m ../../data/mobius-strip.mesh
|
||||
// ex1 -m ../../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex1 -pa -d cuda
|
||||
// ex1 -pa -d raja-cuda
|
||||
// ex1 -pa -d occa-cuda
|
||||
// ex1 -pa -d raja-omp
|
||||
// ex1 -pa -d occa-omp
|
||||
// ex1 -pa -d ceed-cpu
|
||||
// * ex1 -pa -d ceed-cuda
|
||||
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// ex1 -m ../../data/beam-hex.mesh -pa -d cuda
|
||||
// ex1 -m ../../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
// ex1 -m ../../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
//
|
||||
// AmgX sample runs:
|
||||
// ./ex1 --amgx-file multi_gs.json --amgx-solver
|
||||
// ./ex1 --amgx-file precon.json --amgx-preconditioner
|
||||
// ./ex1 --amgx-file multi_gs.json --amgx-solver -d cuda
|
||||
// ./ex1 --amgx-file precon.json --amgx-preconditioner -d cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool amgx_solver = true;
|
||||
const char* amgx_json_file = ""; // jason file for amgx
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&amgx_json_file, "--amgx-file", "--amgx-file",
|
||||
"AMGX solver config file (overrides --amgx-solver, --amgx-verbose)");
|
||||
args.AddOption(&amgx_solver, "--amgx-solver", "--amgx-solver",
|
||||
"--amgx-preconditioner",
|
||||
"--amgx-preconditioner",
|
||||
"Configure AMGX as solver or preconditioner.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
MFEM_VERIFY(!pa && strcmp(amgx_json_file,"") != 0,
|
||||
"An AmgX json file is needed for this example \n");
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
// largest number that gives a final mesh with no more than 50,000
|
||||
// elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(50000./mesh.GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (mesh.GetNodes())
|
||||
{
|
||||
fec = mesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
FiniteElementSpace fespace(&mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
<< fespace.GetTrueVSize() << endl;
|
||||
|
||||
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// In this example, the boundary conditions are defined by marking all
|
||||
// the boundary attributes from the mesh as essential (Dirichlet) and
|
||||
// converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
|
||||
// the basis functions in the finite element fespace.
|
||||
LinearForm b(&fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
|
||||
// 8. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
GridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 9. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
BilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 10. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: eliminating boundary
|
||||
// conditions, applying conforming constraints for non-conforming AMR,
|
||||
// static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
AmgXSolver amgx;
|
||||
|
||||
amgx.ReadParameters(amgx_json_file, AmgXSolver::EXTERNAL);
|
||||
amgx.InitSerial();
|
||||
amgx.SetOperator(*A.As<SparseMatrix>());
|
||||
|
||||
if (amgx_solver)
|
||||
{
|
||||
amgx.Mult(B,X);
|
||||
}
|
||||
else
|
||||
{
|
||||
PCG(*A.As<SparseMatrix>(), amgx, B, X, 3, 40, 1e-12, 0.0);
|
||||
}
|
||||
|
||||
}
|
||||
else // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
CG(*A, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 12. Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 13. 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);
|
||||
|
||||
// 14. 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;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
# Copyright (c) 2010-2020, 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/amgx/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex1
|
||||
PAR_EXAMPLES = ex1p
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
ifeq ($(MFEM_USE_AMGX),NO)
|
||||
$(EXAMPLES):
|
||||
$(error MFEM is not configured with AMGX)
|
||||
endif
|
||||
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
SERIAL_NAME := Serial AMGX example
|
||||
PARALLEL_NAME := Parallel AMGX example
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, $(SERIAL_NAME))
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not build)
|
||||
|
||||
clean: clean-build
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"config_version": 2,
|
||||
"solver": {
|
||||
"max_uncolored_percentage": 0.15,
|
||||
"algorithm": "AGGREGATION",
|
||||
"solver": "AMG",
|
||||
"smoother": "MULTICOLOR_GS",
|
||||
"presweeps": 1,
|
||||
"symmetric_GS" : 1,
|
||||
"selector": "SIZE_2",
|
||||
"coarsest_sweeps": 10,
|
||||
"max_iters": 10000,
|
||||
"postsweeps": 1,
|
||||
"scope": "main",
|
||||
"max_levels": 1000,
|
||||
"matrix_coloring_scheme" : "MIN_MAX",
|
||||
"tolerance": 0.0000001,
|
||||
"print_solve_stats": 1,
|
||||
"obtain_timings": 1,
|
||||
"monitor_residual": 1,
|
||||
"norm": "L2",
|
||||
"cycle": "V"
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"config_version": 2,
|
||||
"solver": {
|
||||
"max_uncolored_percentage": 0.15,
|
||||
"algorithm": "AGGREGATION",
|
||||
"solver": "AMG",
|
||||
"smoother": "MULTICOLOR_GS",
|
||||
"presweeps": 1,
|
||||
"symmetric_GS" : 1,
|
||||
"selector": "SIZE_2",
|
||||
"coarsest_sweeps": 10,
|
||||
"max_iters": 2,
|
||||
"postsweeps": 1,
|
||||
"scope": "main",
|
||||
"max_levels": 1000,
|
||||
"matrix_coloring_scheme" : "MIN_MAX",
|
||||
"tolerance": 0.0,
|
||||
"norm": "L2",
|
||||
"cycle": "V"
|
||||
}
|
||||
}
|
||||
-1211
File diff suppressed because it is too large
Load Diff
-14321
File diff suppressed because it is too large
Load Diff
-3286
File diff suppressed because it is too large
Load Diff
+43
-64
@@ -9,8 +9,6 @@
|
||||
// ex1 -m ../data/fichera.mesh
|
||||
// ex1 -m ../data/fichera-mixed.mesh
|
||||
// ex1 -m ../data/toroid-wedge.mesh
|
||||
// ex1 -m ../data/periodic-annulus-sector.msh
|
||||
// ex1 -m ../data/periodic-torus-sector.msh
|
||||
// ex1 -m ../data/square-disc-p2.vtk -o 2
|
||||
// ex1 -m ../data/square-disc-p3.mesh -o 3
|
||||
// ex1 -m ../data/square-disc-nurbs.mesh -o -1
|
||||
@@ -34,8 +32,7 @@
|
||||
// ex1 -pa -d raja-omp
|
||||
// ex1 -pa -d occa-omp
|
||||
// ex1 -pa -d ceed-cpu
|
||||
// * ex1 -pa -d ceed-cuda
|
||||
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// ex1 -pa -d ceed-cuda
|
||||
// ex1 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
|
||||
@@ -71,7 +68,6 @@ int main(int argc, char *argv[])
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool amgx = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -83,8 +79,6 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&amgx, "-amgx", "--amgx-precon", "-no-amgx",
|
||||
"--no-amgx-precon", "Use AmgX V-cycle as preconditioner for CG.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -106,8 +100,8 @@ int main(int argc, char *argv[])
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
@@ -115,10 +109,10 @@ int main(int argc, char *argv[])
|
||||
// elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(50000./mesh.GetNE())/log(2.)/dim);
|
||||
(int)floor(log(50000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,99 +120,71 @@ int main(int argc, char *argv[])
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (mesh.GetNodes())
|
||||
else if (mesh->GetNodes())
|
||||
{
|
||||
fec = mesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
fec = mesh->GetNodes()->OwnFEC();
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
FiniteElementSpace fespace(&mesh, fec);
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
<< fespace.GetTrueVSize() << endl;
|
||||
<< fespace->GetTrueVSize() << endl;
|
||||
|
||||
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// In this example, the boundary conditions are defined by marking all
|
||||
// the boundary attributes from the mesh as essential (Dirichlet) and
|
||||
// converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
|
||||
// the basis functions in the finite element fespace.
|
||||
LinearForm b(&fespace);
|
||||
LinearForm *b = new LinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 8. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
GridFunction x(&fespace);
|
||||
GridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 9. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
BilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 10. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: eliminating boundary
|
||||
// conditions, applying conforming constraints for non-conforming AMR,
|
||||
// static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (pa)
|
||||
{
|
||||
// Jacobi preconditioning in partial assembly mode
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
CG(*A, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
}
|
||||
else if (amgx)
|
||||
{
|
||||
#if defined(MFEM_USE_AMGX)
|
||||
bool amgx_verbose = false;
|
||||
AmgXSolver amgx(AmgXSolver::PRECONDITIONER, amgx_verbose);
|
||||
amgx.SetOperator(*A.As<SparseMatrix>());
|
||||
PCG(*A, amgx, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
mfem_error("MFEM not configured with AMGX \n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
if (!pa)
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
@@ -232,15 +198,27 @@ int main(int argc, char *argv[])
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
else // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
if (UsesTensorBasis(*fespace))
|
||||
{
|
||||
OperatorJacobiSmoother M(*a, ess_tdof_list);
|
||||
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
CG(*A, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 12. Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 13. 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);
|
||||
mesh->Print(mesh_ofs);
|
||||
ofstream sol_ofs("sol.gf");
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
@@ -252,14 +230,15 @@ int main(int argc, char *argv[])
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
// mpirun -np 4 ex11p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex11p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex11p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex11p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex11p -m ../data/periodic-torus-sector.msh -rs 1
|
||||
// mpirun -np 4 ex11p -m ../data/toroid-wedge.mesh -o 2
|
||||
// mpirun -np 4 ex11p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex11p -m ../data/square-disc-p3.mesh -o 3
|
||||
|
||||
@@ -35,38 +35,6 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class CustomSolverMonitor : public IterativeSolverMonitor
|
||||
{
|
||||
public:
|
||||
CustomSolverMonitor(const ParMesh *m,
|
||||
ParGridFunction *f) :
|
||||
pmesh(m),
|
||||
pgf(f) {}
|
||||
|
||||
void MonitorSolution(int i, double norm, const Vector &x, bool final)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
int num_procs, myid;
|
||||
|
||||
MPI_Comm_size(pmesh->GetComm(),&num_procs);
|
||||
MPI_Comm_rank(pmesh->GetComm(),&myid);
|
||||
|
||||
pgf->SetFromTrueDofs(x);
|
||||
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << *pgf
|
||||
<< "window_title 'Iteration no " << i << "'"
|
||||
<< "keys rRjlc\n" << flush;
|
||||
}
|
||||
|
||||
private:
|
||||
const ParMesh *pmesh;
|
||||
ParGridFunction *pgf;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
@@ -220,7 +188,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomSolverMonitor monitor(pmesh, &x);
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetRelTol(1e-12);
|
||||
@@ -229,7 +196,6 @@ int main(int argc, char *argv[])
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetOperator(*A);
|
||||
gmres.SetPreconditioner(*amg);
|
||||
gmres.SetMonitor(monitor);
|
||||
gmres.Mult(*B, *X);
|
||||
}
|
||||
delete amg;
|
||||
|
||||
+10
-5
@@ -88,6 +88,8 @@ private:
|
||||
Vector funval2;
|
||||
Vector nor;
|
||||
Vector fluxN;
|
||||
IntegrationPoint eip1;
|
||||
IntegrationPoint eip2;
|
||||
|
||||
public:
|
||||
FaceIntegrator(RiemannSolver &rsolver_, const int dim);
|
||||
@@ -416,24 +418,27 @@ void FaceIntegrator::AssembleFaceVector(const FiniteElement &el1,
|
||||
{
|
||||
intorder++;
|
||||
}
|
||||
const IntegrationRule *ir = &IntRules.Get(Tr.GetGeometryType(), intorder);
|
||||
const IntegrationRule *ir = &IntRules.Get(Tr.FaceGeom, intorder);
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
Tr.SetAllIntPoints(&ip); // set face and element int. points
|
||||
Tr.Loc1.Transform(ip, eip1);
|
||||
Tr.Loc2.Transform(ip, eip2);
|
||||
|
||||
// Calculate basis functions on both elements at the face
|
||||
el1.CalcShape(Tr.GetElement1IntPoint(), shape1);
|
||||
el2.CalcShape(Tr.GetElement2IntPoint(), shape2);
|
||||
el1.CalcShape(eip1, shape1);
|
||||
el2.CalcShape(eip2, shape2);
|
||||
|
||||
// Interpolate elfun at the point
|
||||
elfun1_mat.MultTranspose(shape1, funval1);
|
||||
elfun2_mat.MultTranspose(shape2, funval2);
|
||||
|
||||
Tr.Face->SetIntPoint(&ip);
|
||||
|
||||
// Get the normal vector and the flux on the face
|
||||
CalcOrtho(Tr.Jacobian(), nor);
|
||||
CalcOrtho(Tr.Face->Jacobian(), nor);
|
||||
const double mcs = rsolver.Eval(funval1, funval2, nor, fluxN);
|
||||
|
||||
// Update max char speed
|
||||
|
||||
+3
-44
@@ -38,42 +38,6 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class GeneralResidualMonitor : public IterativeSolverMonitor
|
||||
{
|
||||
public:
|
||||
GeneralResidualMonitor(const std::string& prefix_, int print_lvl)
|
||||
: prefix(prefix_)
|
||||
{
|
||||
print_level = print_lvl;
|
||||
}
|
||||
|
||||
virtual void MonitorResidual(int it, double norm, const Vector &r, bool final);
|
||||
|
||||
private:
|
||||
const std::string prefix;
|
||||
int print_level;
|
||||
mutable double norm0;
|
||||
};
|
||||
|
||||
void GeneralResidualMonitor::MonitorResidual(int it, double norm,
|
||||
const Vector &r, bool final)
|
||||
{
|
||||
if (print_level == 1 || (print_level == 3 && (final || it == 0)))
|
||||
{
|
||||
mfem::out << prefix << " iteration " << setw(2) << it
|
||||
<< " : ||r|| = " << norm;
|
||||
if (it > 0)
|
||||
{
|
||||
mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
|
||||
}
|
||||
else
|
||||
{
|
||||
norm0 = norm;
|
||||
}
|
||||
mfem::out << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Custom block preconditioner for the Jacobian of the incompressible nonlinear
|
||||
// elasticity operator. It has the form
|
||||
//
|
||||
@@ -139,11 +103,9 @@ protected:
|
||||
|
||||
// Newton solver for the hyperelastic operator
|
||||
NewtonSolver newton_solver;
|
||||
GeneralResidualMonitor newton_monitor;
|
||||
|
||||
// Solver for the Jacobian solve in the Newton method
|
||||
Solver *j_solver;
|
||||
GeneralResidualMonitor j_monitor;
|
||||
|
||||
// Preconditioner for the Jacobian
|
||||
Solver *j_prec;
|
||||
@@ -448,8 +410,7 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
|
||||
int iter,
|
||||
Coefficient &c_mu)
|
||||
: Operator(fes[0]->GetVSize() + fes[1]->GetVSize()),
|
||||
newton_solver(), newton_monitor("Newton", 1),
|
||||
j_monitor(" GMRES", 3), mu(c_mu), block_offsets(offsets)
|
||||
newton_solver(), mu(c_mu), block_offsets(offsets)
|
||||
{
|
||||
Array<Vector *> rhs(2);
|
||||
rhs = NULL; // Set all entries in the array
|
||||
@@ -485,8 +446,7 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
|
||||
j_gmres->SetRelTol(1e-12);
|
||||
j_gmres->SetAbsTol(1e-12);
|
||||
j_gmres->SetMaxIter(300);
|
||||
j_gmres->SetPrintLevel(-1);
|
||||
j_gmres->SetMonitor(j_monitor);
|
||||
j_gmres->SetPrintLevel(0);
|
||||
j_gmres->SetPreconditioner(*j_prec);
|
||||
j_solver = j_gmres;
|
||||
|
||||
@@ -494,8 +454,7 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
|
||||
newton_solver.iterative_mode = true;
|
||||
newton_solver.SetSolver(*j_solver);
|
||||
newton_solver.SetOperator(*this);
|
||||
newton_solver.SetPrintLevel(-1);
|
||||
newton_solver.SetMonitor(newton_monitor);
|
||||
newton_solver.SetPrintLevel(1);
|
||||
newton_solver.SetRelTol(rel_tol);
|
||||
newton_solver.SetAbsTol(abs_tol);
|
||||
newton_solver.SetMaxIter(iter);
|
||||
|
||||
+3
-60
@@ -38,56 +38,6 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class GeneralResidualMonitor : public IterativeSolverMonitor
|
||||
{
|
||||
public:
|
||||
GeneralResidualMonitor(MPI_Comm comm, const std::string& prefix_,
|
||||
int print_lvl)
|
||||
: prefix(prefix_)
|
||||
{
|
||||
#ifndef MFEM_USE_MPI
|
||||
print_level = print_lvl;
|
||||
#else
|
||||
int rank;
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
if (rank == 0)
|
||||
{
|
||||
print_level = print_lvl;
|
||||
}
|
||||
else
|
||||
{
|
||||
print_level = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void MonitorResidual(int it, double norm, const Vector &r, bool final);
|
||||
|
||||
private:
|
||||
const std::string prefix;
|
||||
int print_level;
|
||||
mutable double norm0;
|
||||
};
|
||||
|
||||
void GeneralResidualMonitor::MonitorResidual(int it, double norm,
|
||||
const Vector &r, bool final)
|
||||
{
|
||||
if (print_level == 1 || (print_level == 3 && (final || it == 0)))
|
||||
{
|
||||
mfem::out << prefix << " iteration " << setw(2) << it
|
||||
<< " : ||r|| = " << norm;
|
||||
if (it > 0)
|
||||
{
|
||||
mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
|
||||
}
|
||||
else
|
||||
{
|
||||
norm0 = norm;
|
||||
}
|
||||
mfem::out << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Custom block preconditioner for the Jacobian of the incompressible nonlinear
|
||||
// elasticity operator. It has the form
|
||||
//
|
||||
@@ -153,11 +103,9 @@ protected:
|
||||
|
||||
// Newton solver for the hyperelastic operator
|
||||
NewtonSolver newton_solver;
|
||||
GeneralResidualMonitor newton_monitor;
|
||||
|
||||
// Solver for the Jacobian solve in the Newton method
|
||||
Solver *j_solver;
|
||||
GeneralResidualMonitor j_monitor;
|
||||
|
||||
// Preconditioner for the Jacobian
|
||||
Solver *j_prec;
|
||||
@@ -511,10 +459,7 @@ RubberOperator::RubberOperator(Array<ParFiniteElementSpace *> &fes,
|
||||
int iter,
|
||||
Coefficient &c_mu)
|
||||
: Operator(fes[0]->TrueVSize() + fes[1]->TrueVSize()),
|
||||
newton_solver(fes[0]->GetComm()),
|
||||
newton_monitor(fes[0]->GetComm(), "Newton", 1),
|
||||
j_monitor(fes[0]->GetComm(), " GMRES", 3),
|
||||
mu(c_mu), block_trueOffsets(trueOffsets)
|
||||
newton_solver(fes[0]->GetComm()), mu(c_mu), block_trueOffsets(trueOffsets)
|
||||
{
|
||||
Array<Vector *> rhs(2);
|
||||
rhs = NULL; // Set all entries in the array
|
||||
@@ -554,8 +499,7 @@ RubberOperator::RubberOperator(Array<ParFiniteElementSpace *> &fes,
|
||||
j_gmres->SetRelTol(1e-12);
|
||||
j_gmres->SetAbsTol(1e-12);
|
||||
j_gmres->SetMaxIter(300);
|
||||
j_gmres->SetPrintLevel(-1);
|
||||
j_gmres->SetMonitor(j_monitor);
|
||||
j_gmres->SetPrintLevel(0);
|
||||
j_gmres->SetPreconditioner(*j_prec);
|
||||
j_solver = j_gmres;
|
||||
|
||||
@@ -563,8 +507,7 @@ RubberOperator::RubberOperator(Array<ParFiniteElementSpace *> &fes,
|
||||
newton_solver.iterative_mode = true;
|
||||
newton_solver.SetSolver(*j_solver);
|
||||
newton_solver.SetOperator(*this);
|
||||
newton_solver.SetPrintLevel(-1);
|
||||
newton_solver.SetMonitor(newton_monitor);
|
||||
newton_solver.SetPrintLevel(1);
|
||||
newton_solver.SetRelTol(rel_tol);
|
||||
newton_solver.SetAbsTol(abs_tol);
|
||||
newton_solver.SetMaxIter(iter);
|
||||
|
||||
+75
-60
@@ -9,8 +9,6 @@
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
@@ -32,8 +30,7 @@
|
||||
// mpirun -np 4 ex1p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex1p -pa -d raja-omp
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
@@ -73,7 +70,7 @@ int main(int argc, char *argv[])
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool amgx = false;
|
||||
int nfiles = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -85,13 +82,12 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&amgx, "-amgx", "--amgx-precon", "-no-amgx",
|
||||
"--no-amgx-precon", "Use AmgX V-cycle as preconditioner for CG.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&nfiles, "-nf", "--num-files", "Number of files to write.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -115,8 +111,8 @@ int main(int argc, char *argv[])
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
@@ -124,23 +120,23 @@ int main(int argc, char *argv[])
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,16 +144,13 @@ int main(int argc, char *argv[])
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
else if (pmesh->GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
fec = pmesh->GetNodes()->OwnFEC();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
@@ -166,10 +159,9 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_Int size = fespace.GlobalTrueVSize();
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec, 1, 0);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
@@ -180,67 +172,56 @@ int main(int argc, char *argv[])
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm b(&fespace);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * If AmgX is available solve using amg preconditioner.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
if (UsesTensorBasis(*fespace))
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
prec = new OperatorJacobiSmoother(*a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else if (amgx)
|
||||
{
|
||||
#if defined(MFEM_USE_AMGX)
|
||||
bool amgx_verbose = false;
|
||||
prec = new AmgXSolver(MPI_COMM_WORLD, AmgXSolver::PRECONDITIONER,
|
||||
amgx_verbose);
|
||||
#else
|
||||
mfem_error("MFEM not configured with AMGX \n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
@@ -256,22 +237,54 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
std::string filename("nranks_");
|
||||
filename += to_string(num_procs);
|
||||
filename += ".gf";
|
||||
{
|
||||
double t1;
|
||||
t1 = MPI_Wtime();
|
||||
x.Save(filename.c_str(), nfiles);
|
||||
double t2 = MPI_Wtime();
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
err << "elapsed write time: " << t2 - t1 << endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
double t1;
|
||||
t1 = MPI_Wtime();
|
||||
ParGridFunction new_x(fespace, filename.c_str());
|
||||
double t2 = MPI_Wtime();
|
||||
if (myid == 0)
|
||||
{
|
||||
err << "elapsed read time: " << t2 - t1 << endl;
|
||||
}
|
||||
// new_x -= x;
|
||||
// out << "GF difference: " << new_x.Norml1() << endl;
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh.Print(mesh_ofs);
|
||||
//mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << num_procs << setfill('0') << setw(6) << myid;
|
||||
|
||||
//ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
//mesh_ofs.precision(8);
|
||||
//pmesh->Print(mesh_ofs);
|
||||
double t1 = MPI_Wtime();
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
double t2 = MPI_Wtime();
|
||||
if (myid == 0)
|
||||
{
|
||||
err << t2 - t1 << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
@@ -282,14 +295,16 @@ int main(int argc, char *argv[])
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << x << flush;
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,503 +0,0 @@
|
||||
// MFEM Example 1 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
double u_exact(const Vector &x);
|
||||
double f_exact(const Vector &x);
|
||||
|
||||
// #define FORM_DEFINITE
|
||||
#define USE_GMRES
|
||||
|
||||
#define USE_CSL
|
||||
|
||||
#define K2 250.0
|
||||
|
||||
int dim;
|
||||
double kappa;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
|
||||
kappa = 2.0 * M_PI;
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
double minsize = pmesh->GetElementSize(0);
|
||||
double maxsize = minsize;
|
||||
for (int i=1; i<pmesh->GetNE(); ++i)
|
||||
{
|
||||
const double size_i = pmesh->GetElementSize(i);
|
||||
minsize = std::min(minsize, size_i);
|
||||
maxsize = std::max(maxsize, size_i);
|
||||
}
|
||||
|
||||
cout << myid << ": Element size range: (" << minsize << ", " << maxsize << ")"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else if (pmesh->GetNodes())
|
||||
{
|
||||
fec = pmesh->GetNodes()->OwnFEC();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
|
||||
//ConstantCoefficient bcoef(1.0);
|
||||
FunctionCoefficient bcoef(f_exact);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
ConstantCoefficient neg(-K2);
|
||||
ConstantCoefficient pos(K2);
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(bcoef));
|
||||
b->Assemble();
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 10. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddDomainIntegrator(new MassIntegrator(neg));
|
||||
|
||||
#ifdef FORM_DEFINITE
|
||||
ParBilinearForm *adef = new ParBilinearForm(fespace);
|
||||
adef->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
adef->AddDomainIntegrator(new MassIntegrator(pos));
|
||||
|
||||
if (static_cond) { adef->EnableStaticCondensation(); }
|
||||
adef->Assemble();
|
||||
|
||||
ParGridFunction xdef(fespace);
|
||||
xdef = 0.0;
|
||||
|
||||
ParLinearForm *bdef = new ParLinearForm(fespace);
|
||||
bdef->AddDomainIntegrator(new DomainLFIntegrator(bcoef));
|
||||
bdef->Assemble();
|
||||
|
||||
HypreParMatrix Adef;
|
||||
Vector Bdef, Xdef;
|
||||
adef->FormLinearSystem(ess_tdof_list, xdef, *bdef, Adef, Xdef, Bdef);
|
||||
#endif
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
//A.Print("helmholtz");
|
||||
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
|
||||
// preconditioner from hypre.
|
||||
#ifdef FORM_DEFINITE
|
||||
HypreSolver *amg = new HypreBoomerAMG(Adef);
|
||||
#else
|
||||
HypreSolver *amg = new HypreBoomerAMG(A);
|
||||
#endif
|
||||
|
||||
const bool fullDirect = true;
|
||||
|
||||
if (fullDirect)
|
||||
{
|
||||
#ifdef USE_CSL
|
||||
Vector Bdef, Xdef;
|
||||
|
||||
ParBilinearForm *Mform = new ParBilinearForm(fespace);
|
||||
Mform->AddDomainIntegrator(new MassIntegrator(pos));
|
||||
Mform->Assemble();
|
||||
|
||||
HypreParMatrix Mmat, Smat, Mcopy;
|
||||
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mmat, Xdef, Bdef);
|
||||
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mcopy, Xdef,
|
||||
Bdef); // There must be a better way than creating two identical matrices.
|
||||
|
||||
ParBilinearForm *Sform = new ParBilinearForm(fespace);
|
||||
Sform->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
Sform->Assemble();
|
||||
|
||||
Sform->FormLinearSystem(ess_tdof_list, x, *b, Smat, Xdef, Bdef);
|
||||
|
||||
const double beta1 = 1.0;
|
||||
const double beta2 = 1.0;
|
||||
|
||||
Mmat *= -beta1;
|
||||
|
||||
HypreParMatrix * cslRe = ParAdd(&Smat, &Mmat);
|
||||
|
||||
Mcopy *= beta2;
|
||||
|
||||
ComplexHypreParMatrix chpm(cslRe, &Mcopy, false, false);
|
||||
|
||||
HypreParMatrix *cSysMat = chpm.GetSystemMatrix();
|
||||
|
||||
Array<int> block_trueOffsets(3); // number of variables + 1
|
||||
block_trueOffsets[0] = 0;
|
||||
block_trueOffsets[1] = fespace->TrueVSize();
|
||||
block_trueOffsets[2] = fespace->TrueVSize();
|
||||
block_trueOffsets.PartialSum();
|
||||
|
||||
// Note that B is of true size.
|
||||
BlockVector trueY(block_trueOffsets), trueX(block_trueOffsets),
|
||||
trueRhs(block_trueOffsets);
|
||||
|
||||
trueRhs.GetBlock(0) = B;
|
||||
trueRhs.GetBlock(1) = 0.0;
|
||||
|
||||
Operator * Arow = new STRUMPACKRowLocMatrix(*cSysMat);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
|
||||
// strumpack->SetSymmetricPattern(true);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
//Solver * precond = strumpack;
|
||||
|
||||
// strumpack->Mult(B, X);
|
||||
|
||||
BlockOperator blockDiagA(block_trueOffsets);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
blockDiagA.SetDiagonalBlock(i, &A);
|
||||
}
|
||||
|
||||
ProductOperator prod(&blockDiagA, strumpack, false, false);
|
||||
//GMRESSolver *gmres = new GMRESSolver(fespace->GetComm());
|
||||
BiCGSTABSolver *gmres = new BiCGSTABSolver(fespace->GetComm());
|
||||
|
||||
gmres->SetOperator(prod);
|
||||
gmres->SetRelTol(1e-8);
|
||||
gmres->SetMaxIter(10000);
|
||||
gmres->SetPrintLevel(1);
|
||||
|
||||
gmres->Mult(trueRhs, trueY);
|
||||
strumpack->Mult(trueY, trueX);
|
||||
|
||||
X = trueX.GetBlock(0);
|
||||
double xim2 = trueX.GetBlock(1).Norml2();
|
||||
xim2 *= xim2;
|
||||
double sumxim2 = 0.0;
|
||||
|
||||
MPI_Allreduce(&xim2, &sumxim2, 1, MPI_DOUBLE, MPI_SUM, fespace->GetComm());
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << myid << ": norm of Xim " << trueX.GetBlock(1).Norml2() << ", global " <<
|
||||
sqrt(sumxim2) << endl;
|
||||
}
|
||||
|
||||
delete gmres;
|
||||
delete strumpack;
|
||||
delete Arow;
|
||||
#else
|
||||
Operator * Arow = new STRUMPACKRowLocMatrix(A);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
|
||||
// strumpack->SetSymmetricPattern(true);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
//Solver * precond = strumpack;
|
||||
|
||||
strumpack->Mult(B, X);
|
||||
|
||||
delete strumpack;
|
||||
delete Arow;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef USE_GMRES
|
||||
HypreGMRES *gmres = new HypreGMRES(A);
|
||||
gmres->SetTol(1e-12);
|
||||
gmres->SetMaxIter(1000);
|
||||
gmres->SetPrintLevel(10);
|
||||
gmres->SetPreconditioner(*amg);
|
||||
gmres->Mult(B, X);
|
||||
delete gmres;
|
||||
#else
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(100);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
pcg->Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
HYPRE_ParCSRMatrix* amgP = amg->Get_Restriction();
|
||||
HypreParMatrix P0(amgP[0], false);
|
||||
HypreParMatrix P1(amgP[1], false);
|
||||
HypreParMatrix P2(amgP[2], false);
|
||||
//HypreParMatrix P3(amgP[3], false);
|
||||
|
||||
P0.Print("P0");
|
||||
*/
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// Compute and print the L^2 norm of the error.
|
||||
{
|
||||
FunctionCoefficient uex(u_exact);
|
||||
|
||||
double err = x.ComputeL2Error(uex);
|
||||
double xnrm = x.ComputeL2Error(zero);
|
||||
ParGridFunction zerogf(fespace);
|
||||
zerogf = 0.0;
|
||||
double normE = zerogf.ComputeL2Error(uex);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "|| E_h - E ||_{L^2} = " << err << endl;
|
||||
cout << "|| E_h ||_{L^2} = " << xnrm << endl;
|
||||
cout << "|| E ||_{L^2} = " << normE << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 16. Free the used memory.
|
||||
//delete pcg;
|
||||
delete amg;
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double u_exact(const Vector & x)
|
||||
{
|
||||
double xi(x(0));
|
||||
double yi(x(1));
|
||||
double zi(1.0);
|
||||
|
||||
if (x.Size() == 3)
|
||||
{
|
||||
zi = x(2);
|
||||
}
|
||||
|
||||
return sin(kappa*xi)*sin(kappa*yi)*sin(kappa*zi);
|
||||
}
|
||||
|
||||
double f_exact(const Vector &x)
|
||||
{
|
||||
double xi(x(0));
|
||||
double yi(x(1));
|
||||
double zi(1.0);
|
||||
|
||||
if (x.Size() == 3)
|
||||
{
|
||||
zi = x(2);
|
||||
}
|
||||
|
||||
const double s = 1.0;
|
||||
|
||||
return ((3.0*kappa*kappa) - (s*K2)) * sin(kappa*xi)*sin(kappa*yi)*sin(
|
||||
kappa*zi) / s;
|
||||
}
|
||||
+45
-63
@@ -6,20 +6,13 @@
|
||||
// ex22 -m ../data/inline-tri.mesh -o 3
|
||||
// ex22 -m ../data/inline-quad.mesh -o 3
|
||||
// ex22 -m ../data/inline-quad.mesh -o 3 -p 1
|
||||
// ex22 -m ../data/inline-quad.mesh -o 3 -p 1 -pa
|
||||
// ex22 -m ../data/inline-quad.mesh -o 3 -p 2
|
||||
// ex22 -m ../data/inline-tet.mesh -o 2
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 1
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2 -pa
|
||||
// ex22 -m ../data/star.mesh -r 1 -o 2 -sigma 10.0
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex22 -m ../data/inline-quad.mesh -o 3 -p 1 -pa -d cuda
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2 -pa -d cuda
|
||||
// ex22 -m ../data/star.mesh -r 1 -o 2 -sigma 10.0 -pa -d cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define and
|
||||
// solve simple complex-valued linear systems. It implements three
|
||||
// variants of a damped harmonic oscillator:
|
||||
@@ -83,8 +76,6 @@ int main(int argc, char *argv[])
|
||||
bool visualization = 1;
|
||||
bool herm_conv = true;
|
||||
bool exact_sol = true;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -115,10 +106,6 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -148,18 +135,13 @@ int main(int argc, char *argv[])
|
||||
ComplexOperator::Convention conv =
|
||||
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes
|
||||
// with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase resolution. In this example we do
|
||||
// 3. Refine the mesh to increase resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement where the user specifies
|
||||
// the number of levels with the '-r' option.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
@@ -167,7 +149,7 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use continuous
|
||||
// 4. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange, Nedelec, or Raviart-Thomas finite elements of the specified
|
||||
// order.
|
||||
if (dim == 1 && prob != 0 )
|
||||
@@ -189,7 +171,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of finite element unknowns: " << fespace->GetTrueVSize()
|
||||
<< endl;
|
||||
|
||||
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// In this example, the boundary conditions are defined based on the type
|
||||
// of mesh and the problem type.
|
||||
Array<int> ess_tdof_list;
|
||||
@@ -201,12 +183,12 @@ int main(int argc, char *argv[])
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system.
|
||||
ComplexLinearForm b(fespace, conv);
|
||||
b.Vector::operator=(0.0);
|
||||
|
||||
// 8. Define the solution vector u as a complex finite element grid function
|
||||
// 7. Define the solution vector u as a complex finite element grid function
|
||||
// corresponding to fespace. Initialize u with initial guess of 1+0i or
|
||||
// the exact solution if it is known.
|
||||
ComplexGridFunction u(fespace);
|
||||
@@ -228,6 +210,7 @@ int main(int argc, char *argv[])
|
||||
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
||||
VectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
u = 0.0;
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
@@ -280,7 +263,7 @@ int main(int argc, char *argv[])
|
||||
<< "window_title 'Exact: Imaginary Part'" << flush;
|
||||
}
|
||||
|
||||
// 9. Set up the sesquilinear form a(.,.) on the finite element space
|
||||
// 8. Set up the sesquilinear form a(.,.) on the finite element space
|
||||
// corresponding to the damped harmonic oscillator operator of the
|
||||
// appropriate type:
|
||||
//
|
||||
@@ -299,7 +282,6 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
|
||||
|
||||
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
@@ -323,7 +305,7 @@ int main(int argc, char *argv[])
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
|
||||
// 9a. Set up the bilinear form for the preconditioner corresponding to the
|
||||
// 8a. Set up the bilinear form for the preconditioner corresponding to the
|
||||
// appropriate operator
|
||||
//
|
||||
// 0) A scalar H1 field
|
||||
@@ -336,8 +318,6 @@ int main(int argc, char *argv[])
|
||||
// -Grad(a Div) - omega^2 b + omega c
|
||||
//
|
||||
BilinearForm *pcOp = new BilinearForm(fespace);
|
||||
if (pa) { pcOp->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
@@ -358,9 +338,9 @@ int main(int argc, char *argv[])
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
|
||||
// 10. Assemble the form and the corresponding linear system, applying any
|
||||
// necessary transformations such as: assembly, eliminating boundary
|
||||
// conditions, conforming constraints for non-conforming AMR, etc.
|
||||
// 9. Assemble the form and the corresponding linear system, applying any
|
||||
// necessary transformations such as: assembly, eliminating boundary
|
||||
// conditions, conforming constraints for non-conforming AMR, etc.
|
||||
a->Assemble();
|
||||
pcOp->Assemble();
|
||||
|
||||
@@ -368,17 +348,28 @@ int main(int argc, char *argv[])
|
||||
Vector B, U;
|
||||
|
||||
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
|
||||
u = 0.0;
|
||||
U = 0.0;
|
||||
|
||||
cout << "Size of linear system: " << A->Width() << endl << endl;
|
||||
OperatorHandle PCOp;
|
||||
pcOp->FormSystemMatrix(ess_tdof_list, PCOp);
|
||||
|
||||
// 11. Define and apply a GMRES solver for AU=B with a block diagonal
|
||||
{
|
||||
ComplexSparseMatrix * Asp =
|
||||
dynamic_cast<ComplexSparseMatrix*>(A.Ptr());
|
||||
|
||||
cout << "Size of linear system: "
|
||||
<< 2 * Asp->real().Width() << endl << endl;
|
||||
}
|
||||
|
||||
// 10. Define and apply a GMRES solver for AU=B with a block diagonal
|
||||
// preconditioner based on the appropriate sparse smoother.
|
||||
{
|
||||
Array<int> blockOffsets;
|
||||
blockOffsets.SetSize(3);
|
||||
blockOffsets[0] = 0;
|
||||
blockOffsets[1] = A->Height() / 2;
|
||||
blockOffsets[2] = A->Height() / 2;
|
||||
blockOffsets[1] = PCOp.Ptr()->Height();
|
||||
blockOffsets[2] = PCOp.Ptr()->Height();
|
||||
blockOffsets.PartialSum();
|
||||
|
||||
BlockDiagonalPreconditioner BDP(blockOffsets);
|
||||
@@ -386,31 +377,22 @@ int main(int argc, char *argv[])
|
||||
Operator * pc_r = NULL;
|
||||
Operator * pc_i = NULL;
|
||||
|
||||
if (pa)
|
||||
double s = 1.0;
|
||||
switch (prob)
|
||||
{
|
||||
pc_r = new OperatorJacobiSmoother(*pcOp, ess_tdof_list);
|
||||
case 0:
|
||||
pc_r = new DSmoother(*PCOp.As<SparseMatrix>());
|
||||
break;
|
||||
case 1:
|
||||
pc_r = new GSSmoother(*PCOp.As<SparseMatrix>());
|
||||
s = -1.0;
|
||||
break;
|
||||
case 2:
|
||||
pc_r = new DSmoother(*PCOp.As<SparseMatrix>());
|
||||
break;
|
||||
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
else
|
||||
{
|
||||
OperatorHandle PCOp;
|
||||
pcOp->SetDiagonalPolicy(mfem::Operator::DIAG_ONE);
|
||||
pcOp->FormSystemMatrix(ess_tdof_list, PCOp);
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
pc_r = new DSmoother(*PCOp.As<SparseMatrix>());
|
||||
break;
|
||||
case 1:
|
||||
pc_r = new GSSmoother(*PCOp.As<SparseMatrix>());
|
||||
break;
|
||||
case 2:
|
||||
pc_r = new DSmoother(*PCOp.As<SparseMatrix>());
|
||||
break;
|
||||
default:
|
||||
break; // This should be unreachable
|
||||
}
|
||||
}
|
||||
double s = (prob != 1) ? 1.0 : -1.0;
|
||||
pc_i = new ScaledOperator(pc_r,
|
||||
(conv == ComplexOperator::HERMITIAN) ?
|
||||
s:-s);
|
||||
@@ -428,7 +410,7 @@ int main(int argc, char *argv[])
|
||||
gmres.Mult(B, U);
|
||||
}
|
||||
|
||||
// 12. Recover the solution as a finite element grid function and compute the
|
||||
// 11. Recover the solution as a finite element grid function and compute the
|
||||
// errors if the exact solution is known.
|
||||
a->RecoverFEMSolution(U, b, u);
|
||||
|
||||
@@ -460,7 +442,7 @@ int main(int argc, char *argv[])
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
// 13. Save the refined mesh and the solution. This output can be viewed
|
||||
// 12. Save the refined mesh and the solution. This output can be viewed
|
||||
// later using GLVis: "glvis -m mesh -g sol".
|
||||
{
|
||||
ofstream mesh_ofs("refined.mesh");
|
||||
@@ -475,7 +457,7 @@ int main(int argc, char *argv[])
|
||||
u.imag().Save(sol_i_ofs);
|
||||
}
|
||||
|
||||
// 14. Send the solution by socket to a GLVis server.
|
||||
// 13. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -534,7 +516,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
// 14. Free the used memory.
|
||||
delete a;
|
||||
delete u_exact;
|
||||
delete pcOp;
|
||||
|
||||
+47
-66
@@ -7,19 +7,12 @@
|
||||
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 3
|
||||
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 3 -p 1
|
||||
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 3 -p 2
|
||||
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 1 -p 1 -pa
|
||||
// mpirun -np 4 ex22p -m ../data/inline-tet.mesh -o 2
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 1
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 2
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 1 -p 2 -pa
|
||||
// mpirun -np 4 ex22p -m ../data/star.mesh -o 2 -sigma 10.0
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 1 -p 1 -pa -d cuda
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 1 -p 2 -pa -d cuda
|
||||
// mpirun -np 4 ex22p -m ../data/star.mesh -o 2 -sigma 10.0 -pa -d cuda
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define and
|
||||
// solve simple complex-valued linear systems. It implements three
|
||||
// variants of a damped harmonic oscillator:
|
||||
@@ -48,6 +41,7 @@
|
||||
// We recommend viewing examples 1, 3 and 4 before viewing this
|
||||
// example.
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -90,8 +84,6 @@ int main(int argc, char *argv[])
|
||||
bool visualization = 1;
|
||||
bool herm_conv = true;
|
||||
bool exact_sol = true;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -124,10 +116,6 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -164,24 +152,19 @@ int main(int argc, char *argv[])
|
||||
ComplexOperator::Convention conv =
|
||||
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution.
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution.
|
||||
for (int l = 0; l < ser_ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
@@ -191,7 +174,7 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange, Nedelec, or Raviart-Thomas finite elements of
|
||||
// the specified order.
|
||||
if (dim == 1 && prob != 0 )
|
||||
@@ -219,7 +202,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// based on the type of mesh and the problem type.
|
||||
Array<int> ess_tdof_list;
|
||||
@@ -231,14 +214,14 @@ int main(int argc, char *argv[])
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system.
|
||||
ParComplexLinearForm b(fespace, conv);
|
||||
b.Vector::operator=(0.0);
|
||||
|
||||
// 10. Define the solution vector u as a parallel complex finite element grid
|
||||
// function corresponding to fespace. Initialize u with initial guess of
|
||||
// 1+0i or the exact solution if it is known.
|
||||
// 9. Define the solution vector u as a parallel complex finite element grid
|
||||
// function corresponding to fespace. Initialize u with initial guess of
|
||||
// 1+0i or the exact solution if it is known.
|
||||
ParComplexGridFunction u(fespace);
|
||||
ParComplexGridFunction * u_exact = NULL;
|
||||
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
|
||||
@@ -258,6 +241,7 @@ int main(int argc, char *argv[])
|
||||
VectorConstantCoefficient zeroVecCoef(zeroVec);
|
||||
VectorConstantCoefficient oneVecCoef(oneVec);
|
||||
|
||||
u = 0.0;
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
@@ -312,7 +296,7 @@ int main(int argc, char *argv[])
|
||||
<< "window_title 'Exact: Imaginary Part'" << flush;
|
||||
}
|
||||
|
||||
// 11. Set up the parallel sesquilinear form a(.,.) on the finite element
|
||||
// 10. Set up the parallel sesquilinear form a(.,.) on the finite element
|
||||
// space corresponding to the damped harmonic oscillator operator of the
|
||||
// appropriate type:
|
||||
//
|
||||
@@ -331,7 +315,6 @@ int main(int argc, char *argv[])
|
||||
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
|
||||
|
||||
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
@@ -355,7 +338,7 @@ int main(int argc, char *argv[])
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
|
||||
// 11a. Set up the parallel bilinear form for the preconditioner
|
||||
// 10a. Set up the parallel bilinear form for the preconditioner
|
||||
// corresponding to the appropriate operator
|
||||
//
|
||||
// 0) A scalar H1 field
|
||||
@@ -368,7 +351,6 @@ int main(int argc, char *argv[])
|
||||
// -Grad(a Div) - omega^2 b + omega c
|
||||
//
|
||||
ParBilinearForm *pcOp = new ParBilinearForm(fespace);
|
||||
if (pa) { pcOp->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
@@ -389,7 +371,7 @@ int main(int argc, char *argv[])
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, etc.
|
||||
@@ -400,22 +382,30 @@ int main(int argc, char *argv[])
|
||||
Vector B, U;
|
||||
|
||||
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
|
||||
u = 0.0;
|
||||
U = 0.0;
|
||||
|
||||
OperatorHandle PCOp;
|
||||
pcOp->FormSystemMatrix(ess_tdof_list, PCOp);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
ComplexHypreParMatrix * Ahyp =
|
||||
dynamic_cast<ComplexHypreParMatrix*>(A.Ptr());
|
||||
|
||||
cout << "Size of linear system: "
|
||||
<< 2 * fespace->GlobalTrueVSize() << endl << endl;
|
||||
<< 2 * Ahyp->real().GetGlobalNumRows() << endl << endl;
|
||||
}
|
||||
|
||||
// 13. Define and apply a parallel FGMRES solver for AU=B with a block
|
||||
// 12. Define and apply a parallel FGMRES solver for AU=B with a block
|
||||
// diagonal preconditioner based on the appropriate multigrid
|
||||
// preconditioner from hypre.
|
||||
{
|
||||
Array<int> blockTrueOffsets;
|
||||
blockTrueOffsets.SetSize(3);
|
||||
blockTrueOffsets[0] = 0;
|
||||
blockTrueOffsets[1] = A->Height() / 2;
|
||||
blockTrueOffsets[2] = A->Height() / 2;
|
||||
blockTrueOffsets[1] = PCOp.Ptr()->Height();
|
||||
blockTrueOffsets[2] = PCOp.Ptr()->Height();
|
||||
blockTrueOffsets.PartialSum();
|
||||
|
||||
BlockDiagonalPreconditioner BDP(blockTrueOffsets);
|
||||
@@ -423,34 +413,25 @@ int main(int argc, char *argv[])
|
||||
Operator * pc_r = NULL;
|
||||
Operator * pc_i = NULL;
|
||||
|
||||
if (pa)
|
||||
switch (prob)
|
||||
{
|
||||
pc_r = new OperatorJacobiSmoother(*pcOp, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
OperatorHandle PCOp;
|
||||
pcOp->FormSystemMatrix(ess_tdof_list, PCOp);
|
||||
switch (prob)
|
||||
{
|
||||
case 0:
|
||||
pc_r = new HypreBoomerAMG(*PCOp.As<HypreParMatrix>());
|
||||
break;
|
||||
case 1:
|
||||
case 0:
|
||||
pc_r = new HypreBoomerAMG(*PCOp.As<HypreParMatrix>());
|
||||
break;
|
||||
case 1:
|
||||
pc_r = new HypreAMS(*PCOp.As<HypreParMatrix>(), fespace);
|
||||
break;
|
||||
case 2:
|
||||
if (dim == 2 )
|
||||
{
|
||||
pc_r = new HypreAMS(*PCOp.As<HypreParMatrix>(), fespace);
|
||||
break;
|
||||
case 2:
|
||||
if (dim == 2 )
|
||||
{
|
||||
pc_r = new HypreAMS(*PCOp.As<HypreParMatrix>(), fespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc_r = new HypreADS(*PCOp.As<HypreParMatrix>(), fespace);
|
||||
}
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pc_r = new HypreADS(*PCOp.As<HypreParMatrix>(), fespace);
|
||||
}
|
||||
break;
|
||||
default: break; // This should be unreachable
|
||||
}
|
||||
pc_i = new ScaledOperator(pc_r,
|
||||
(conv == ComplexOperator::HERMITIAN) ?
|
||||
@@ -468,7 +449,7 @@ int main(int argc, char *argv[])
|
||||
fgmres.SetPrintLevel(1);
|
||||
fgmres.Mult(B, U);
|
||||
}
|
||||
// 14. Recover the parallel grid function corresponding to U. This is the
|
||||
// 13. Recover the parallel grid function corresponding to U. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(U, b, u);
|
||||
|
||||
@@ -503,7 +484,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can be
|
||||
// 14. Save the refined mesh and the solution in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_r_name, sol_i_name;
|
||||
@@ -523,7 +504,7 @@ int main(int argc, char *argv[])
|
||||
u.imag().Save(sol_i_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -588,7 +569,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
// 16. Free the used memory.
|
||||
delete a;
|
||||
delete u_exact;
|
||||
delete pcOp;
|
||||
|
||||
+77
-243
@@ -6,8 +6,6 @@
|
||||
// ex24 -m ../data/square-disc.mesh -o 2
|
||||
// ex24 -m ../data/beam-tet.mesh
|
||||
// ex24 -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// ex24 -m ../data/beam-hex.mesh -o 2 -pa -p 1
|
||||
// ex24 -m ../data/beam-hex.mesh -o 2 -pa -p 2
|
||||
// ex24 -m ../data/escher.mesh
|
||||
// ex24 -m ../data/escher.mesh -o 2
|
||||
// ex24 -m ../data/fichera.mesh
|
||||
@@ -25,16 +23,11 @@
|
||||
// ex24 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code illustrates usage of mixed finite element
|
||||
// spaces, with three variants:
|
||||
// spaces. Using two different approaches, we project a gradient
|
||||
// of a function in H^1 to H(curl). Other spaces and example
|
||||
// computations are to be added in the future.
|
||||
//
|
||||
// 1) (grad p, u) for p in H^1 tested against u in H(curl)
|
||||
// 2) (curl v, u) for v in H(curl) tested against u in H(div), 3D
|
||||
// 3) (div v, q) for v in H(div) tested against q in L_2
|
||||
//
|
||||
// Using different approaches, we project the gradient, curl, or
|
||||
// divergence to the appropriate space.
|
||||
//
|
||||
// We recommend viewing examples 1, 3, and 5 before viewing this
|
||||
// We recommend viewing examples 1 and 3 before viewing this
|
||||
// example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
@@ -46,19 +39,14 @@ using namespace mfem;
|
||||
|
||||
double p_exact(const Vector &x);
|
||||
void gradp_exact(const Vector &, Vector &);
|
||||
double div_gradp_exact(const Vector &x);
|
||||
void v_exact(const Vector &x, Vector &v);
|
||||
void curlv_exact(const Vector &x, Vector &cv);
|
||||
|
||||
int dim;
|
||||
double freq = 1.0, kappa;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-hex.mesh";
|
||||
int order = 1;
|
||||
int prob = 0;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
@@ -69,8 +57,6 @@ int main(int argc, char *argv[])
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&prob, "-p", "--problem-type",
|
||||
"Choose between 0: grad, 1: curl, 2: div");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
@@ -88,7 +74,6 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
@@ -115,129 +100,72 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use Nedelec or
|
||||
// Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *trial_fec = NULL;
|
||||
FiniteElementCollection *test_fec = NULL;
|
||||
// 5. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
FiniteElementSpace *H1fespace = new FiniteElementSpace(mesh, H1fec);
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
trial_fec = new H1_FECollection(order, dim);
|
||||
test_fec = new ND_FECollection(order, dim);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
trial_fec = new ND_FECollection(order, dim);
|
||||
test_fec = new RT_FECollection(order-1, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
trial_fec = new RT_FECollection(order-1, dim);
|
||||
test_fec = new L2_FECollection(order-1, dim);
|
||||
}
|
||||
int size = fespace->GetTrueVSize();
|
||||
int H1size = H1fespace->GetTrueVSize();
|
||||
cout << "Number of Nedelec finite element unknowns: " << size << endl;
|
||||
cout << "Number of H1 finite element unknowns: " << H1size << endl;
|
||||
|
||||
FiniteElementSpace trial_fes(mesh, trial_fec);
|
||||
FiniteElementSpace test_fes(mesh, test_fec);
|
||||
|
||||
int trial_size = trial_fes.GetTrueVSize();
|
||||
int test_size = test_fes.GetTrueVSize();
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
|
||||
cout << "Number of H1 finite element unknowns: " << trial_size << endl;
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
cout << "Number of Nedelec finite element unknowns: " << trial_size << endl;
|
||||
cout << "Number of Raviart-Thomas finite element unknowns: " << test_size <<
|
||||
endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Number of Raviart-Thomas finite element unknowns: "
|
||||
<< trial_size << endl;
|
||||
cout << "Number of L2 finite element unknowns: " << test_size << endl;
|
||||
}
|
||||
|
||||
// 6. Define the solution vector as a finite element grid function
|
||||
// corresponding to the trial fespace.
|
||||
GridFunction gftest(&test_fes);
|
||||
GridFunction gftrial(&trial_fes);
|
||||
GridFunction x(&test_fes);
|
||||
// 6. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
GridFunction x(fespace);
|
||||
FunctionCoefficient p_coef(p_exact);
|
||||
GridFunction p(H1fespace);
|
||||
p.ProjectCoefficient(p_coef);
|
||||
p.SetTrueVector();
|
||||
p.SetFromTrueVector();
|
||||
|
||||
VectorFunctionCoefficient gradp_coef(sdim, gradp_exact);
|
||||
VectorFunctionCoefficient v_coef(sdim, v_exact);
|
||||
VectorFunctionCoefficient curlv_coef(sdim, curlv_exact);
|
||||
FunctionCoefficient divgradp_coef(div_gradp_exact);
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
gftrial.ProjectCoefficient(p_coef);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
gftrial.ProjectCoefficient(v_coef);
|
||||
}
|
||||
else
|
||||
{
|
||||
gftrial.ProjectCoefficient(gradp_coef);
|
||||
}
|
||||
|
||||
gftrial.SetTrueVector();
|
||||
gftrial.SetFromTrueVector();
|
||||
|
||||
// 7. Set up the bilinear forms for L2 projection.
|
||||
ConstantCoefficient one(1.0);
|
||||
BilinearForm a(&test_fes);
|
||||
MixedBilinearForm a_mixed(&trial_fes, &test_fes);
|
||||
// 7. Set up the bilinear forms.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
Coefficient *sigma = new ConstantCoefficient(1.0);
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
MixedBilinearForm *a_NDH1 = new MixedBilinearForm(H1fespace, fespace);
|
||||
if (pa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a_mixed.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a_NDH1->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
}
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
|
||||
a_mixed.AddDomainIntegrator(new MixedVectorGradientIntegrator(one));
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
|
||||
a_mixed.AddDomainIntegrator(new MixedVectorCurlIntegrator(one));
|
||||
}
|
||||
else
|
||||
{
|
||||
a.AddDomainIntegrator(new MassIntegrator(one));
|
||||
a_mixed.AddDomainIntegrator(new VectorFEDivergenceIntegrator(one));
|
||||
}
|
||||
// First approach: L2 projection
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
|
||||
a_NDH1->AddDomainIntegrator(new MixedVectorGradientIntegrator(*muinv));
|
||||
|
||||
// 8. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: eliminating boundary
|
||||
// conditions, applying conforming constraints for non-conforming AMR,
|
||||
// static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
// 8. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
|
||||
a.Assemble();
|
||||
if (!pa) { a.Finalize(); }
|
||||
a->Assemble();
|
||||
if (!pa) { a->Finalize(); }
|
||||
|
||||
a_mixed.Assemble();
|
||||
if (!pa) { a_mixed.Finalize(); }
|
||||
a_NDH1->Assemble();
|
||||
if (!pa) { a_NDH1->Finalize(); }
|
||||
|
||||
if (pa)
|
||||
{
|
||||
a_mixed.Mult(gftrial, x);
|
||||
a_NDH1->Mult(p, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
SparseMatrix& mixed = a_mixed.SpMat();
|
||||
mixed.Mult(gftrial, x);
|
||||
SparseMatrix& NDH1 = a_NDH1->SpMat();
|
||||
NDH1.Mult(p, x);
|
||||
}
|
||||
|
||||
// 9. Define and apply a PCG solver for Ax = b with Jacobi preconditioner.
|
||||
{
|
||||
GridFunction rhs(&test_fes);
|
||||
GridFunction rhs(fespace);
|
||||
rhs = x;
|
||||
x = 0.0;
|
||||
|
||||
@@ -248,15 +176,15 @@ int main(int argc, char *argv[])
|
||||
if (pa)
|
||||
{
|
||||
Array<int> ess_tdof_list; // empty
|
||||
OperatorJacobiSmoother Jacobi(a, ess_tdof_list);
|
||||
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
|
||||
|
||||
cg.SetOperator(a);
|
||||
cg.SetOperator(*a);
|
||||
cg.SetPreconditioner(Jacobi);
|
||||
cg.Mult(rhs, x);
|
||||
}
|
||||
else
|
||||
{
|
||||
SparseMatrix& Amat = a.SpMat();
|
||||
SparseMatrix& Amat = a->SpMat();
|
||||
DSmoother Jacobi(Amat);
|
||||
|
||||
cg.SetOperator(Amat);
|
||||
@@ -265,89 +193,33 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Compute the same field by applying a DiscreteInterpolator.
|
||||
GridFunction discreteInterpolant(&test_fes);
|
||||
DiscreteLinearOperator dlo(&trial_fes, &test_fes);
|
||||
if (prob == 0)
|
||||
{
|
||||
dlo.AddDomainInterpolator(new GradientInterpolator());
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
dlo.AddDomainInterpolator(new CurlInterpolator());
|
||||
}
|
||||
else
|
||||
{
|
||||
dlo.AddDomainInterpolator(new DivergenceInterpolator());
|
||||
}
|
||||
// 10. Second approach: compute the same solution by applying
|
||||
// GradientInterpolator in H(curl).
|
||||
DiscreteLinearOperator grad(H1fespace, fespace);
|
||||
grad.AddDomainInterpolator(new GradientInterpolator());
|
||||
grad.Assemble();
|
||||
|
||||
dlo.Assemble();
|
||||
dlo.Mult(gftrial, discreteInterpolant);
|
||||
GridFunction gradp(fespace);
|
||||
grad.Mult(p, gradp);
|
||||
|
||||
// 11. Compute the projection of the exact field.
|
||||
GridFunction exact_proj(&test_fes);
|
||||
if (prob == 0)
|
||||
{
|
||||
exact_proj.ProjectCoefficient(gradp_coef);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
exact_proj.ProjectCoefficient(curlv_coef);
|
||||
}
|
||||
else
|
||||
{
|
||||
exact_proj.ProjectCoefficient(divgradp_coef);
|
||||
}
|
||||
// 11. Compute the projection of the exact grad p.
|
||||
GridFunction exact_gradp(fespace);
|
||||
exact_gradp.ProjectCoefficient(gradp_coef);
|
||||
exact_gradp.SetTrueVector();
|
||||
exact_gradp.SetFromTrueVector();
|
||||
|
||||
exact_proj.SetTrueVector();
|
||||
exact_proj.SetFromTrueVector();
|
||||
|
||||
// 12. Compute and print the L_2 norm of the error.
|
||||
if (prob == 0)
|
||||
// 12. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double errSol = x.ComputeL2Error(gradp_coef);
|
||||
double errInterp = discreteInterpolant.ComputeL2Error(gradp_coef);
|
||||
double errProj = exact_proj.ComputeL2Error(gradp_coef);
|
||||
double errInterp = gradp.ComputeL2Error(gradp_coef);
|
||||
double errProj = exact_gradp.ComputeL2Error(gradp_coef);
|
||||
|
||||
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
|
||||
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
|
||||
"|| E_h - grad p ||_{L^2} = " << errSol << '\n' << endl;
|
||||
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - grad p"
|
||||
" ||_{L_2} = " << errInterp << '\n' << endl;
|
||||
"||_{L^2} = " << errInterp << '\n' << endl;
|
||||
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
double errSol = x.ComputeL2Error(curlv_coef);
|
||||
double errInterp = discreteInterpolant.ComputeL2Error(curlv_coef);
|
||||
double errProj = exact_proj.ComputeL2Error(curlv_coef);
|
||||
|
||||
cout << "\n Solution of (E_h,w) = (curl v_h,w) for E_h and w in H(div): "
|
||||
"|| E_h - curl v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Curl interpolant E_h = curl v_h in H(div): || E_h - curl v "
|
||||
"||_{L_2} = " << errInterp << '\n' << endl;
|
||||
cout << " Projection E_h of exact curl v in H(div): || E_h - curl v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
int order_quad = max(2, 2*order+1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
double errSol = x.ComputeL2Error(divgradp_coef, irs);
|
||||
double errInterp = discreteInterpolant.ComputeL2Error(divgradp_coef, irs);
|
||||
double errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
|
||||
|
||||
cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
|
||||
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Divergence interpolant f_h = div v_h in L_2: || f_h - div v "
|
||||
"||_{L_2} = " << errInterp << '\n' << endl;
|
||||
cout << " Projection f_h of exact div v in L_2: || f_h - div v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
"||_{L^2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
|
||||
// 13. Save the refined mesh and the solution. This output can be viewed
|
||||
@@ -370,8 +242,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
delete trial_fec;
|
||||
delete test_fec;
|
||||
delete a;
|
||||
delete a_NDH1;
|
||||
delete sigma;
|
||||
delete muinv;
|
||||
delete fespace;
|
||||
delete H1fespace;
|
||||
delete fec;
|
||||
delete H1fec;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
@@ -406,47 +284,3 @@ void gradp_exact(const Vector &x, Vector &f)
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
double div_gradp_exact(const Vector &x)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
return -3.0 * sin(x(0)) * sin(x(1)) * sin(x(2));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
return -2.0 * sin(x(0)) * sin(x(1));
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void v_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
v(0) = sin(kappa * x(1));
|
||||
v(1) = sin(kappa * x(2));
|
||||
v(2) = sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
v(0) = sin(kappa * x(1));
|
||||
v(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { v(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void curlv_exact(const Vector &x, Vector &cv)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
cv(0) = -kappa * cos(kappa * x(2));
|
||||
cv(1) = -kappa * cos(kappa * x(0));
|
||||
cv(2) = -kappa * cos(kappa * x(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
cv = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
+81
-253
@@ -6,8 +6,6 @@
|
||||
// mpirun -np 4 ex24p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex24p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -pa -p 1
|
||||
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -pa -p 2
|
||||
// mpirun -np 4 ex24p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex24p -m ../data/escher.mesh -o 2
|
||||
// mpirun -np 4 ex24p -m ../data/fichera.mesh
|
||||
@@ -25,16 +23,11 @@
|
||||
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code illustrates usage of mixed finite element
|
||||
// spaces, with three variants:
|
||||
// spaces. Using two different approaches, we project a gradient
|
||||
// of a function in H^1 to H(curl). Other spaces and example
|
||||
// computations are to be added in the future.
|
||||
//
|
||||
// 1) (grad p, u) for p in H^1 tested against u in H(curl)
|
||||
// 2) (curl v, u) for v in H(curl) tested against u in H(div), 3D
|
||||
// 3) (div v, q) for v in H(div) tested against q in L_2
|
||||
//
|
||||
// Using different approaches, we project the gradient, curl, or
|
||||
// divergence to the appropriate space.
|
||||
//
|
||||
// We recommend viewing examples 1, 3, and 5 before viewing this
|
||||
// We recommend viewing examples 1 and 3 before viewing this
|
||||
// example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
@@ -46,12 +39,8 @@ using namespace mfem;
|
||||
|
||||
double p_exact(const Vector &x);
|
||||
void gradp_exact(const Vector &, Vector &);
|
||||
double div_gradp_exact(const Vector &x);
|
||||
void v_exact(const Vector &x, Vector &v);
|
||||
void curlv_exact(const Vector &x, Vector &cv);
|
||||
|
||||
int dim;
|
||||
double freq = 1.0, kappa;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -64,7 +53,6 @@ int main(int argc, char *argv[])
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-hex.mesh";
|
||||
int order = 1;
|
||||
int prob = 0;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
@@ -75,8 +63,6 @@ int main(int argc, char *argv[])
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&prob, "-p", "--problem-type",
|
||||
"Choose between 0: grad, 1: curl, 2: div");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
@@ -101,7 +87,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
@@ -144,137 +129,80 @@ int main(int argc, char *argv[])
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use Nedelec or Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *trial_fec = NULL;
|
||||
FiniteElementCollection *test_fec = NULL;
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
trial_fec = new H1_FECollection(order, dim);
|
||||
test_fec = new ND_FECollection(order, dim);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
trial_fec = new ND_FECollection(order, dim);
|
||||
test_fec = new RT_FECollection(order-1, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
trial_fec = new RT_FECollection(order-1, dim);
|
||||
test_fec = new L2_FECollection(order-1, dim);
|
||||
}
|
||||
|
||||
ParFiniteElementSpace trial_fes(pmesh, trial_fec);
|
||||
ParFiniteElementSpace test_fes(pmesh, test_fec);
|
||||
|
||||
HYPRE_Int trial_size = trial_fes.GlobalTrueVSize();
|
||||
HYPRE_Int test_size = test_fes.GlobalTrueVSize();
|
||||
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
ParFiniteElementSpace *H1fespace = new ParFiniteElementSpace(pmesh, H1fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
HYPRE_Int H1size = H1fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
if (prob == 0)
|
||||
{
|
||||
cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
|
||||
cout << "Number of H1 finite element unknowns: " << trial_size << endl;
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
cout << "Number of Nedelec finite element unknowns: " << trial_size << endl;
|
||||
cout << "Number of Raviart-Thomas finite element unknowns: " << test_size <<
|
||||
endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Number of Raviart-Thomas finite element unknowns: "
|
||||
<< trial_size << endl;
|
||||
cout << "Number of L2 finite element unknowns: " << test_size << endl;
|
||||
}
|
||||
cout << "Number of Nedelec finite element unknowns: " << size << endl;
|
||||
cout << "Number of H1 finite element unknowns: " << H1size << endl;
|
||||
}
|
||||
|
||||
// 8. Define the solution vector as a parallel finite element grid function
|
||||
// corresponding to the trial fespace.
|
||||
ParGridFunction gftest(&test_fes);
|
||||
ParGridFunction gftrial(&trial_fes);
|
||||
ParGridFunction x(&test_fes);
|
||||
// 8. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
FunctionCoefficient p_coef(p_exact);
|
||||
ParGridFunction p(H1fespace);
|
||||
p.ProjectCoefficient(p_coef);
|
||||
p.SetTrueVector();
|
||||
p.SetFromTrueVector();
|
||||
|
||||
VectorFunctionCoefficient gradp_coef(sdim, gradp_exact);
|
||||
VectorFunctionCoefficient v_coef(sdim, v_exact);
|
||||
VectorFunctionCoefficient curlv_coef(sdim, curlv_exact);
|
||||
FunctionCoefficient divgradp_coef(div_gradp_exact);
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
gftrial.ProjectCoefficient(p_coef);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
gftrial.ProjectCoefficient(v_coef);
|
||||
}
|
||||
else
|
||||
{
|
||||
gftrial.ProjectCoefficient(gradp_coef);
|
||||
}
|
||||
|
||||
gftrial.SetTrueVector();
|
||||
gftrial.SetFromTrueVector();
|
||||
|
||||
// 9. Set up the parallel bilinear forms for L2 projection.
|
||||
ConstantCoefficient one(1.0);
|
||||
ParBilinearForm a(&test_fes);
|
||||
ParMixedBilinearForm a_mixed(&trial_fes, &test_fes);
|
||||
// 9. Set up the parallel bilinear forms.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
Coefficient *sigma = new ConstantCoefficient(1.0);
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
ParMixedBilinearForm *a_NDH1 = new ParMixedBilinearForm(H1fespace, fespace);
|
||||
if (pa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a_mixed.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a_NDH1->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
}
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
|
||||
a_mixed.AddDomainIntegrator(new MixedVectorGradientIntegrator(one));
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
|
||||
a_mixed.AddDomainIntegrator(new MixedVectorCurlIntegrator(one));
|
||||
}
|
||||
else
|
||||
{
|
||||
a.AddDomainIntegrator(new MassIntegrator(one));
|
||||
a_mixed.AddDomainIntegrator(new VectorFEDivergenceIntegrator(one));
|
||||
}
|
||||
// First approach: L2 projection
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
|
||||
a_NDH1->AddDomainIntegrator(new MixedVectorGradientIntegrator(*muinv));
|
||||
|
||||
// 10. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
|
||||
a.Assemble();
|
||||
if (!pa) { a.Finalize(); }
|
||||
a->Assemble();
|
||||
if (!pa) { a->Finalize(); }
|
||||
|
||||
a_mixed.Assemble();
|
||||
if (!pa) { a_mixed.Finalize(); }
|
||||
a_NDH1->Assemble();
|
||||
if (!pa) { a_NDH1->Finalize(); }
|
||||
|
||||
Vector B(test_fes.GetTrueVSize());
|
||||
Vector X(test_fes.GetTrueVSize());
|
||||
Vector B(fespace->GetTrueVSize());
|
||||
Vector X(fespace->GetTrueVSize());
|
||||
|
||||
if (pa)
|
||||
{
|
||||
ParLinearForm b(&test_fes); // used as a vector
|
||||
a_mixed.Mult(gftrial, b); // process-local multiplication
|
||||
b.ParallelAssemble(B);
|
||||
ParLinearForm *b = new ParLinearForm(fespace); // used as a vector
|
||||
a_NDH1->Mult(p, *b); // process-local multiplication
|
||||
b->ParallelAssemble(B);
|
||||
delete b;
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix *mixed = a_mixed.ParallelAssemble();
|
||||
HypreParMatrix *NDH1 = a_NDH1->ParallelAssemble();
|
||||
|
||||
Vector P(trial_fes.GetTrueVSize());
|
||||
gftrial.GetTrueDofs(P);
|
||||
Vector P(H1fespace->GetTrueVSize());
|
||||
p.GetTrueDofs(P);
|
||||
|
||||
mixed->Mult(P,B);
|
||||
NDH1->Mult(P,B);
|
||||
|
||||
delete mixed;
|
||||
delete NDH1;
|
||||
}
|
||||
|
||||
// 11. Define and apply a parallel PCG solver for AX=B with Jacobi
|
||||
@@ -284,9 +212,9 @@ int main(int argc, char *argv[])
|
||||
Array<int> ess_tdof_list; // empty
|
||||
|
||||
OperatorPtr A;
|
||||
a.FormSystemMatrix(ess_tdof_list, A);
|
||||
a->FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
OperatorJacobiSmoother Jacobi(a, ess_tdof_list);
|
||||
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
@@ -299,7 +227,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix *Amat = a.ParallelAssemble();
|
||||
HypreParMatrix *Amat = a->ParallelAssemble();
|
||||
HypreDiagScale Jacobi(*Amat);
|
||||
HyprePCG pcg(*Amat);
|
||||
pcg.SetTol(1e-12);
|
||||
@@ -314,97 +242,35 @@ int main(int argc, char *argv[])
|
||||
|
||||
x.SetFromTrueDofs(X);
|
||||
|
||||
// 12. Compute the same field by applying a DiscreteInterpolator.
|
||||
ParGridFunction discreteInterpolant(&test_fes);
|
||||
ParDiscreteLinearOperator dlo(&trial_fes, &test_fes);
|
||||
if (prob == 0)
|
||||
{
|
||||
dlo.AddDomainInterpolator(new GradientInterpolator());
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
dlo.AddDomainInterpolator(new CurlInterpolator());
|
||||
}
|
||||
else
|
||||
{
|
||||
dlo.AddDomainInterpolator(new DivergenceInterpolator());
|
||||
}
|
||||
// 12. Second approach: compute the same solution by applying
|
||||
// GradientInterpolator in H(curl).
|
||||
ParDiscreteLinearOperator grad(H1fespace, fespace);
|
||||
grad.AddDomainInterpolator(new GradientInterpolator());
|
||||
grad.Assemble();
|
||||
|
||||
dlo.Assemble();
|
||||
dlo.Mult(gftrial, discreteInterpolant);
|
||||
ParGridFunction gradp(fespace);
|
||||
grad.Mult(p, gradp);
|
||||
|
||||
// 13. Compute the projection of the exact field.
|
||||
ParGridFunction exact_proj(&test_fes);
|
||||
if (prob == 0)
|
||||
{
|
||||
exact_proj.ProjectCoefficient(gradp_coef);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
exact_proj.ProjectCoefficient(curlv_coef);
|
||||
}
|
||||
else
|
||||
{
|
||||
exact_proj.ProjectCoefficient(divgradp_coef);
|
||||
}
|
||||
// 13. Compute the projection of the exact grad p.
|
||||
ParGridFunction exact_gradp(fespace);
|
||||
exact_gradp.ProjectCoefficient(gradp_coef);
|
||||
exact_gradp.SetTrueVector();
|
||||
exact_gradp.SetFromTrueVector();
|
||||
|
||||
exact_proj.SetTrueVector();
|
||||
exact_proj.SetFromTrueVector();
|
||||
|
||||
// 14. Compute and print the L_2 norm of the error.
|
||||
if (prob == 0)
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double errSol = x.ComputeL2Error(gradp_coef);
|
||||
double errInterp = discreteInterpolant.ComputeL2Error(gradp_coef);
|
||||
double errProj = exact_proj.ComputeL2Error(gradp_coef);
|
||||
double errInterp = gradp.ComputeL2Error(gradp_coef);
|
||||
double errProj = exact_gradp.ComputeL2Error(gradp_coef);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl)"
|
||||
": || E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - grad"
|
||||
" p ||_{L_2} = " << errInterp << '\n' << endl;
|
||||
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in "
|
||||
"H(curl): || E_h - grad p ||_{L^2} = " << errSol << '\n' << endl;
|
||||
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - "
|
||||
"grad p ||_{L^2} = " << errInterp << '\n' << endl;
|
||||
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
double errSol = x.ComputeL2Error(curlv_coef);
|
||||
double errInterp = discreteInterpolant.ComputeL2Error(curlv_coef);
|
||||
double errProj = exact_proj.ComputeL2Error(curlv_coef);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n Solution of (E_h,w) = (curl v_h,w) for E_h and w in "
|
||||
"H(div): || E_h - curl v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Curl interpolant E_h = curl v_h in H(div): || E_h - curl v "
|
||||
"||_{L_2} = " << errInterp << '\n' << endl;
|
||||
cout << " Projection E_h of exact curl v in H(div): || E_h - curl v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int order_quad = max(2, 2*order+1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
double errSol = x.ComputeL2Error(divgradp_coef, irs);
|
||||
double errInterp = discreteInterpolant.ComputeL2Error(divgradp_coef, irs);
|
||||
double errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
|
||||
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Divergence interpolant f_h = div v_h in L_2: || f_h - div v"
|
||||
" ||_{L_2} = " << errInterp << '\n' << endl;
|
||||
cout << " Projection f_h of exact div v in L_2: || f_h - div v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
"||_{L^2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,8 +302,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete trial_fec;
|
||||
delete test_fec;
|
||||
delete a;
|
||||
delete a_NDH1;
|
||||
delete sigma;
|
||||
delete muinv;
|
||||
delete fespace;
|
||||
delete H1fespace;
|
||||
delete fec;
|
||||
delete H1fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
@@ -474,47 +346,3 @@ void gradp_exact(const Vector &x, Vector &f)
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
double div_gradp_exact(const Vector &x)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
return -3.0 * sin(x(0)) * sin(x(1)) * sin(x(2));
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
return -2.0 * sin(x(0)) * sin(x(1));
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void v_exact(const Vector &x, Vector &v)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
v(0) = sin(kappa * x(1));
|
||||
v(1) = sin(kappa * x(2));
|
||||
v(2) = sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
v(0) = sin(kappa * x(1));
|
||||
v(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { v(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void curlv_exact(const Vector &x, Vector &cv)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
cv(0) = -kappa * cos(kappa * x(2));
|
||||
cv(1) = -kappa * cos(kappa * x(0));
|
||||
cv(2) = -kappa * cos(kappa * x(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
cv = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
+76
-65
@@ -82,27 +82,24 @@ public:
|
||||
};
|
||||
|
||||
// Class for returning the PML coefficients of the bilinear form
|
||||
class PMLDiagMatrixCoefficient : public VectorCoefficient
|
||||
class PMLMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
CartesianPML * pml = nullptr;
|
||||
void (*Function)(const Vector &, CartesianPML * , Vector &);
|
||||
void (*Function)(const Vector &, CartesianPML * , DenseMatrix &);
|
||||
public:
|
||||
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
Vector &),
|
||||
CartesianPML * pml_)
|
||||
: VectorCoefficient(dim), pml(pml_), Function(F)
|
||||
PMLMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
DenseMatrix &),
|
||||
CartesianPML * pml_)
|
||||
: MatrixCoefficient(dim), pml(pml_), Function(F)
|
||||
{}
|
||||
|
||||
using VectorCoefficient::Eval;
|
||||
|
||||
virtual void Eval(Vector &K, ElementTransformation &T,
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double x[3];
|
||||
Vector transip(x, 3);
|
||||
T.Transform(ip, transip);
|
||||
K.SetSize(vdim);
|
||||
K.SetSize(height, width);
|
||||
(*Function)(transip, pml, K);
|
||||
}
|
||||
};
|
||||
@@ -119,13 +116,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, 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_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
|
||||
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);
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
|
||||
Array2D<double> comp_domain_bdr;
|
||||
Array2D<double> domain_bdr;
|
||||
@@ -368,19 +365,19 @@ int main(int argc, char *argv[])
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(restr_omeg),NULL);
|
||||
|
||||
int cdim = (dim == 2) ? 1 : dim;
|
||||
PMLDiagMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, pml);
|
||||
PMLDiagMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, pml);
|
||||
ScalarVectorProductCoefficient c1_Re(muinv,pml_c1_Re);
|
||||
ScalarVectorProductCoefficient c1_Im(muinv,pml_c1_Im);
|
||||
VectorRestrictedCoefficient restr_c1_Re(c1_Re,attrPML);
|
||||
VectorRestrictedCoefficient restr_c1_Im(c1_Im,attrPML);
|
||||
PMLMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, pml);
|
||||
PMLMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, pml);
|
||||
ScalarMatrixProductCoefficient c1_Re(muinv,pml_c1_Re);
|
||||
ScalarMatrixProductCoefficient c1_Im(muinv,pml_c1_Im);
|
||||
MatrixRestrictedCoefficient restr_c1_Re(c1_Re,attrPML);
|
||||
MatrixRestrictedCoefficient restr_c1_Im(c1_Im,attrPML);
|
||||
|
||||
PMLDiagMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,pml);
|
||||
PMLDiagMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,pml);
|
||||
ScalarVectorProductCoefficient c2_Re(omeg,pml_c2_Re);
|
||||
ScalarVectorProductCoefficient c2_Im(omeg,pml_c2_Im);
|
||||
VectorRestrictedCoefficient restr_c2_Re(c2_Re,attrPML);
|
||||
VectorRestrictedCoefficient restr_c2_Im(c2_Im,attrPML);
|
||||
PMLMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,pml);
|
||||
PMLMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,pml);
|
||||
ScalarMatrixProductCoefficient c2_Re(omeg,pml_c2_Re);
|
||||
ScalarMatrixProductCoefficient c2_Im(omeg,pml_c2_Im);
|
||||
MatrixRestrictedCoefficient restr_c2_Re(c2_Re,attrPML);
|
||||
MatrixRestrictedCoefficient restr_c2_Im(c2_Im,attrPML);
|
||||
|
||||
// Integrators inside the PML region
|
||||
a.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_Re),
|
||||
@@ -392,22 +389,27 @@ int main(int argc, char *argv[])
|
||||
// applying any necessary transformations such as: assembly, eliminating
|
||||
// boundary conditions, applying conforming constraints for
|
||||
// non-conforming AMR, etc.
|
||||
a.Assemble(0);
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
OperatorHandle Ah;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
|
||||
|
||||
// 13. Solve using a direct or an iterative solver
|
||||
// 13. Transform to monolithic SparseMatrix
|
||||
SparseMatrix *A = Ah.As<ComplexSparseMatrix>()->GetSystemMatrix();
|
||||
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 14. Solve using a direct or an iterative solver
|
||||
#ifdef MFEM_USE_SUITESPARSE
|
||||
{
|
||||
ComplexUMFPackSolver csolver(*A.As<ComplexSparseMatrix>());
|
||||
csolver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
csolver.SetPrintLevel(1);
|
||||
csolver.Mult(B, X);
|
||||
UMFPackSolver solver(*A);
|
||||
solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
solver.Mult(B, X);
|
||||
}
|
||||
#else
|
||||
// 13a. Set up the Bilinear form a(.,.) for the preconditioner
|
||||
|
||||
// 14a. Set up the Bilinear form a(.,.) for the preconditioner
|
||||
//
|
||||
// In Comp
|
||||
// Domain: 1/mu (Curl E, Curl F) + omega^2 * epsilon (E,F)
|
||||
@@ -422,23 +424,23 @@ int main(int argc, char *argv[])
|
||||
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_muinv));
|
||||
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_absomeg));
|
||||
|
||||
PMLDiagMatrixCoefficient pml_c1_abs(cdim,detJ_inv_JT_J_abs, pml);
|
||||
ScalarVectorProductCoefficient c1_abs(muinv,pml_c1_abs);
|
||||
VectorRestrictedCoefficient restr_c1_abs(c1_abs,attrPML);
|
||||
PMLMatrixCoefficient pml_c1_abs(cdim,detJ_inv_JT_J_abs, pml);
|
||||
ScalarMatrixProductCoefficient c1_abs(muinv,pml_c1_abs);
|
||||
MatrixRestrictedCoefficient restr_c1_abs(c1_abs,attrPML);
|
||||
|
||||
PMLDiagMatrixCoefficient pml_c2_abs(dim, detJ_JT_J_inv_abs,pml);
|
||||
ScalarVectorProductCoefficient c2_abs(absomeg,pml_c2_abs);
|
||||
VectorRestrictedCoefficient restr_c2_abs(c2_abs,attrPML);
|
||||
PMLMatrixCoefficient pml_c2_abs(dim, detJ_JT_J_inv_abs,pml);
|
||||
ScalarMatrixProductCoefficient c2_abs(absomeg,pml_c2_abs);
|
||||
MatrixRestrictedCoefficient restr_c2_abs(c2_abs,attrPML);
|
||||
|
||||
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_abs));
|
||||
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_abs));
|
||||
|
||||
prec.Assemble();
|
||||
|
||||
OperatorPtr PCOpAh;
|
||||
OperatorHandle PCOpAh;
|
||||
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
|
||||
|
||||
// 13b. Define and apply a GMRES solver for AU=B with a block diagonal
|
||||
// 14b. Define and apply a GMRES solver for AU=B with a block diagonal
|
||||
// preconditioner based on the Gauss-Seidel sparse smoother.
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
@@ -465,15 +467,17 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
|
||||
// 14. Recover the solution as a finite element grid function and compute the
|
||||
// 15. Recover the solution as a finite element grid function and compute the
|
||||
// errors if the exact solution is known.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// If exact is known compute the error
|
||||
if (exact_known)
|
||||
{
|
||||
ComplexGridFunction x_gf(fespace);
|
||||
VectorFunctionCoefficient E_ex_Re(dim, E_exact_Re);
|
||||
VectorFunctionCoefficient E_ex_Im(dim, E_exact_Im);
|
||||
x_gf.ProjectCoefficient(E_ex_Re, E_ex_Im);
|
||||
int order_quad = max(2, 2 * order + 1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i = 0; i < Geometry::NumGeom; ++i)
|
||||
@@ -502,7 +506,7 @@ int main(int argc, char *argv[])
|
||||
<< sqrt(L2Error_Re*L2Error_Re + L2Error_Im*L2Error_Im) << "\n\n";
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution. This output can be viewed
|
||||
// 16. Save the refined mesh and the solution. This output can be viewed
|
||||
// later using GLVis: "glvis -m mesh -g sol".
|
||||
{
|
||||
ofstream mesh_ofs("ex25.mesh");
|
||||
@@ -517,7 +521,7 @@ int main(int argc, char *argv[])
|
||||
x.imag().Save(sol_i_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// 17. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
// Define visualization keys for GLVis (see GLVis documentation)
|
||||
@@ -568,7 +572,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
// 18. Free the used memory.
|
||||
delete A;
|
||||
delete pml;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
@@ -766,7 +771,7 @@ void E_bdr_data_Im(const Vector &x, Vector &E)
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det(1.0, 0.0);
|
||||
@@ -777,13 +782,14 @@ void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (det / pow(dxs[i], 2)).real();
|
||||
M(i, i) = (det / pow(dxs[i], 2)).real();
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -794,13 +800,14 @@ void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (det / pow(dxs[i], 2)).imag();
|
||||
M(i, i) = (det / pow(dxs[i], 2)).imag();
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -811,13 +818,14 @@ void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = abs(det / pow(dxs[i], 2));
|
||||
M(i, i) = abs(det / pow(dxs[i], 2));
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det(1.0, 0.0);
|
||||
@@ -831,18 +839,19 @@ void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
// in the 2D case the coefficient is scalar 1/det(J)
|
||||
if (dim == 2)
|
||||
{
|
||||
D = (1.0 / det).real();
|
||||
M = (1.0 / det).real();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (pow(dxs[i], 2) / det).real();
|
||||
M(i, i) = (pow(dxs[i], 2) / det).real();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -855,18 +864,19 @@ void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
D = (1.0 / det).imag();
|
||||
M = (1.0 / det).imag();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (pow(dxs[i], 2) / det).imag();
|
||||
M(i, i) = (pow(dxs[i], 2) / det).imag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -879,13 +889,14 @@ void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector &D)
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
D = abs(1.0 / det);
|
||||
M = abs(1.0 / det);
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = abs(pow(dxs[i], 2) / det);
|
||||
M(i, i) = abs(pow(dxs[i], 2) / det);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
-57
@@ -82,27 +82,24 @@ public:
|
||||
};
|
||||
|
||||
// Class for returning the PML coefficients of the bilinear form
|
||||
class PMLDiagMatrixCoefficient : public VectorCoefficient
|
||||
class PMLMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
CartesianPML * pml = nullptr;
|
||||
void (*Function)(const Vector &, CartesianPML * , Vector &);
|
||||
void (*Function)(const Vector &, CartesianPML * , DenseMatrix &);
|
||||
public:
|
||||
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
Vector &),
|
||||
CartesianPML * pml_)
|
||||
: VectorCoefficient(dim), pml(pml_), Function(F)
|
||||
PMLMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
DenseMatrix &),
|
||||
CartesianPML * pml_)
|
||||
: MatrixCoefficient(dim), pml(pml_), Function(F)
|
||||
{}
|
||||
|
||||
using VectorCoefficient::Eval;
|
||||
|
||||
virtual void Eval(Vector &K, ElementTransformation &T,
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double x[3];
|
||||
Vector transip(x, 3);
|
||||
T.Transform(ip, transip);
|
||||
K.SetSize(vdim);
|
||||
K.SetSize(height, width);
|
||||
(*Function)(transip, pml, K);
|
||||
}
|
||||
};
|
||||
@@ -119,13 +116,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, 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_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
|
||||
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);
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
|
||||
Array2D<double> comp_domain_bdr;
|
||||
Array2D<double> domain_bdr;
|
||||
@@ -396,19 +393,19 @@ int main(int argc, char *argv[])
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(restr_omeg),NULL);
|
||||
|
||||
int cdim = (dim == 2) ? 1 : dim;
|
||||
PMLDiagMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, pml);
|
||||
PMLDiagMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, pml);
|
||||
ScalarVectorProductCoefficient c1_Re(muinv,pml_c1_Re);
|
||||
ScalarVectorProductCoefficient c1_Im(muinv,pml_c1_Im);
|
||||
VectorRestrictedCoefficient restr_c1_Re(c1_Re,attrPML);
|
||||
VectorRestrictedCoefficient restr_c1_Im(c1_Im,attrPML);
|
||||
PMLMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, pml);
|
||||
PMLMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, pml);
|
||||
ScalarMatrixProductCoefficient c1_Re(muinv,pml_c1_Re);
|
||||
ScalarMatrixProductCoefficient c1_Im(muinv,pml_c1_Im);
|
||||
MatrixRestrictedCoefficient restr_c1_Re(c1_Re,attrPML);
|
||||
MatrixRestrictedCoefficient restr_c1_Im(c1_Im,attrPML);
|
||||
|
||||
PMLDiagMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,pml);
|
||||
PMLDiagMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,pml);
|
||||
ScalarVectorProductCoefficient c2_Re(omeg,pml_c2_Re);
|
||||
ScalarVectorProductCoefficient c2_Im(omeg,pml_c2_Im);
|
||||
VectorRestrictedCoefficient restr_c2_Re(c2_Re,attrPML);
|
||||
VectorRestrictedCoefficient restr_c2_Im(c2_Im,attrPML);
|
||||
PMLMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,pml);
|
||||
PMLMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,pml);
|
||||
ScalarMatrixProductCoefficient c2_Re(omeg,pml_c2_Re);
|
||||
ScalarMatrixProductCoefficient c2_Im(omeg,pml_c2_Im);
|
||||
MatrixRestrictedCoefficient restr_c2_Re(c2_Re,attrPML);
|
||||
MatrixRestrictedCoefficient restr_c2_Im(c2_Im,attrPML);
|
||||
|
||||
// Integrators inside the PML region
|
||||
a.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_Re),
|
||||
@@ -422,15 +419,21 @@ int main(int argc, char *argv[])
|
||||
// constraints for non-conforming AMR, etc.
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr Ah;
|
||||
OperatorHandle Ah;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
|
||||
|
||||
// 15. Solve using a direct or an iterative solver
|
||||
// 15. Transform to monolithic HypreParMatrix
|
||||
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A->GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 16. Solve using a direct or an iterative solver
|
||||
#ifdef MFEM_USE_SUPERLU
|
||||
{
|
||||
// Transform to monolithic HypreParMatrix
|
||||
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
|
||||
SuperLURowLocMatrix SA(*A);
|
||||
SuperLUSolver superlu(MPI_COMM_WORLD);
|
||||
superlu.SetPrintStatistics(false);
|
||||
@@ -438,9 +441,9 @@ int main(int argc, char *argv[])
|
||||
superlu.SetColumnPermutation(superlu::PARMETIS);
|
||||
superlu.SetOperator(SA);
|
||||
superlu.Mult(B, X);
|
||||
delete A;
|
||||
}
|
||||
#else
|
||||
|
||||
// 16a. Set up the parallel Bilinear form a(.,.) for the preconditioner
|
||||
//
|
||||
// In Comp
|
||||
@@ -456,20 +459,20 @@ int main(int argc, char *argv[])
|
||||
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_muinv));
|
||||
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_absomeg));
|
||||
|
||||
PMLDiagMatrixCoefficient pml_c1_abs(cdim,detJ_inv_JT_J_abs, pml);
|
||||
ScalarVectorProductCoefficient c1_abs(muinv,pml_c1_abs);
|
||||
VectorRestrictedCoefficient restr_c1_abs(c1_abs,attrPML);
|
||||
PMLMatrixCoefficient pml_c1_abs(cdim,detJ_inv_JT_J_abs, pml);
|
||||
ScalarMatrixProductCoefficient c1_abs(muinv,pml_c1_abs);
|
||||
MatrixRestrictedCoefficient restr_c1_abs(c1_abs,attrPML);
|
||||
|
||||
PMLDiagMatrixCoefficient pml_c2_abs(dim, detJ_JT_J_inv_abs,pml);
|
||||
ScalarVectorProductCoefficient c2_abs(absomeg,pml_c2_abs);
|
||||
VectorRestrictedCoefficient restr_c2_abs(c2_abs,attrPML);
|
||||
PMLMatrixCoefficient pml_c2_abs(dim, detJ_JT_J_inv_abs,pml);
|
||||
ScalarMatrixProductCoefficient c2_abs(absomeg,pml_c2_abs);
|
||||
MatrixRestrictedCoefficient restr_c2_abs(c2_abs,attrPML);
|
||||
|
||||
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_abs));
|
||||
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_abs));
|
||||
|
||||
prec.Assemble();
|
||||
|
||||
OperatorPtr PCOpAh;
|
||||
OperatorHandle PCOpAh;
|
||||
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
|
||||
|
||||
// 16b. Define and apply a parallel GMRES solver for AU=B with a block
|
||||
@@ -493,7 +496,7 @@ int main(int argc, char *argv[])
|
||||
gmres.SetMaxIter(2000);
|
||||
gmres.SetRelTol(1e-5);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*Ah);
|
||||
gmres.SetOperator(*A);
|
||||
gmres.SetPreconditioner(BlockAMS);
|
||||
gmres.Mult(B, X);
|
||||
}
|
||||
@@ -506,8 +509,10 @@ int main(int argc, char *argv[])
|
||||
// If exact is known compute the error
|
||||
if (exact_known)
|
||||
{
|
||||
ParComplexGridFunction x_gf(fespace);
|
||||
VectorFunctionCoefficient E_ex_Re(dim, E_exact_Re);
|
||||
VectorFunctionCoefficient E_ex_Im(dim, E_exact_Im);
|
||||
x_gf.ProjectCoefficient(E_ex_Re, E_ex_Im);
|
||||
int order_quad = max(2, 2 * order + 1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i = 0; i < Geometry::NumGeom; ++i)
|
||||
@@ -624,6 +629,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 20. Free the used memory.
|
||||
delete A;
|
||||
delete pml;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
@@ -822,7 +828,7 @@ void E_bdr_data_Im(const Vector &x, Vector &E)
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det(1.0, 0.0);
|
||||
@@ -833,13 +839,14 @@ void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (det / pow(dxs[i], 2)).real();
|
||||
M(i, i) = (det / pow(dxs[i], 2)).real();
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -850,13 +857,14 @@ void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (det / pow(dxs[i], 2)).imag();
|
||||
M(i, i) = (det / pow(dxs[i], 2)).imag();
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -867,13 +875,14 @@ void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = abs(det / pow(dxs[i], 2));
|
||||
M(i, i) = abs(det / pow(dxs[i], 2));
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det(1.0, 0.0);
|
||||
@@ -887,18 +896,19 @@ void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
// in the 2D case the coefficient is scalar 1/det(J)
|
||||
if (dim == 2)
|
||||
{
|
||||
D = (1.0 / det).real();
|
||||
M = (1.0 / det).real();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (pow(dxs[i], 2) / det).real();
|
||||
M(i, i) = (pow(dxs[i], 2) / det).real();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -911,18 +921,19 @@ void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
D = (1.0 / det).imag();
|
||||
M = (1.0 / det).imag();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = (pow(dxs[i], 2) / det).imag();
|
||||
M(i, i) = (pow(dxs[i], 2) / det).imag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
@@ -935,13 +946,14 @@ void detJ_inv_JT_J_abs(const Vector &x, CartesianPML * pml, Vector & D)
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
D = abs(1.0 / det);
|
||||
M = abs(1.0 / det);
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
D(i) = abs(pow(dxs[i], 2) / det);
|
||||
M(i, i) = abs(pow(dxs[i], 2) / det);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-12
@@ -60,7 +60,6 @@ int main(int argc, char *argv[])
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
bool amg_elast = 0;
|
||||
bool reorder_space = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -76,8 +75,6 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&reorder_space, "-nodes", "--by-nodes", "-vdim", "--by-vdim",
|
||||
"Use byNODES ordering of vector space instead of byVDIM");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -159,14 +156,7 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
if (reorder_space)
|
||||
{
|
||||
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byNODES);
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byVDIM);
|
||||
}
|
||||
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byVDIM);
|
||||
}
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
@@ -259,7 +249,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
amg->SetSystemsOptions(dim, reorder_space);
|
||||
amg->SetSystemsOptions(dim);
|
||||
}
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-8);
|
||||
@@ -284,6 +274,13 @@ int main(int argc, char *argv[])
|
||||
pmesh->SetNodalFESpace(fespace);
|
||||
}
|
||||
|
||||
{
|
||||
x.Save("ex2p.gf", 1);
|
||||
ParGridFunction new_x(fespace, "ex2p.gf");
|
||||
new_x -= x;
|
||||
out << "GF difference: " << new_x.Norml1() << endl;
|
||||
}
|
||||
|
||||
// 16. Save in parallel the displaced mesh and the inverted solution (which
|
||||
// gives the backward displacements to the original grid). This output
|
||||
// can be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
|
||||
+5
-23
@@ -158,9 +158,9 @@ int main(int argc, char *argv[])
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
@@ -207,36 +207,18 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
|
||||
// (in the full assembly case) or CG with Jacobi preconditioner (in the
|
||||
// partial assembly case).
|
||||
if (pa) // matrix-free auxiliary space solver with PA
|
||||
|
||||
if (pa) // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
StopWatch sw;
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
|
||||
MatrixFreeAMS *ams = new MatrixFreeAMS(*a, *A, *fespace, muinv, sigma, NULL,
|
||||
ess_bdr); //, 2, 2);
|
||||
|
||||
//OperatorJacobiSmoother jacobi(*a, ess_tdof_list);
|
||||
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
cg.SetPreconditioner(*ams);
|
||||
//cg.SetPreconditioner(jacobi);
|
||||
//cg.iterative_mode = false;
|
||||
sw.Stop();
|
||||
cout << myid << ": MatrixFreeAMS-CG setup time " << sw.RealTime() << endl;
|
||||
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
|
||||
cg.SetPreconditioner(Jacobi);
|
||||
cg.Mult(B, X);
|
||||
|
||||
sw.Stop();
|
||||
cout << myid << ": MatrixFreeAMS-CG solve time " << sw.RealTime() << endl;
|
||||
ams->PrintTimings(myid);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,425 +0,0 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic diffusion
|
||||
// problem corresponding to the second order definite Maxwell
|
||||
// equation curl curl E + E = f with boundary condition
|
||||
// E x n = <given tangential field>. Here, we use a given exact
|
||||
// solution E and compute the corresponding r.h.s. f.
|
||||
// We discretize with Nedelec finite elements in 2D or 3D.
|
||||
//
|
||||
// The example demonstrates the use of H(curl) finite element
|
||||
// spaces with the curl-curl and the (vector finite element) mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Static condensation is
|
||||
// also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-2 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
#define TEST_AIRY
|
||||
|
||||
#ifdef TEST_AIRY
|
||||
#include "gsl_sf_airy.h"
|
||||
|
||||
#define XSHIFT 0.0 // 0.25
|
||||
#endif
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
void E_exact(const Vector &, Vector &);
|
||||
void f_exact(const Vector &, Vector &);
|
||||
double freq = 1.0, kappa;
|
||||
int dim;
|
||||
|
||||
//#define K2_VALUE 11.9
|
||||
//#define K2_VALUE 2.1
|
||||
//#define K2_VALUE 1500.9
|
||||
#define K2_VALUE 10981.41589009910 // 104.792251097584^2 or 5 GHz
|
||||
|
||||
void test_Airy_epsilon(const Vector &x, Vector &e)
|
||||
{
|
||||
e(0) = 1.0;
|
||||
e(1) = 1.0;
|
||||
e(2) = (4.0 * (x(0) + XSHIFT)) - 1.0;
|
||||
|
||||
e *= -K2_VALUE;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
//const char *mesh_file = "../data/beam-tet.mesh";
|
||||
const char *mesh_file = "../data/inline-tetHalf.mesh";
|
||||
//const char *mesh_file = "inline-tetSlab.mesh";
|
||||
//const char *mesh_file = "../data/inline-hexHalf.mesh";
|
||||
//const char *mesh_file = "../data/inline-tet.mesh";
|
||||
int order = 2;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
bool use_strumpack = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
args.AddOption(&use_strumpack, "-strumpack", "--strumpack-solver",
|
||||
"-no-strumpack", "--no-strumpack-solver",
|
||||
"Use STRUMPACK's double complex linear solver.");
|
||||
#endif
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
cout << "Using k2 " << K2_VALUE << endl;
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
|
||||
ref_levels = 0;
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
long globalNE = pmesh->GetGlobalNE();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
cout << "Number of mesh elements: " << globalNE << endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
VectorFunctionCoefficient f(sdim, f_exact);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
ParGridFunction solproj(fespace);
|
||||
VectorFunctionCoefficient E(sdim, E_exact);
|
||||
x.ProjectCoefficient(E);
|
||||
|
||||
solproj = x;
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
Coefficient *sigma = new ConstantCoefficient(-K2_VALUE);
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
#ifdef TEST_AIRY
|
||||
VectorFunctionCoefficient epsilon(3, test_Airy_epsilon);
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(epsilon));
|
||||
#else
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
|
||||
#endif
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
StopWatch chrono;
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (use_strumpack)
|
||||
{
|
||||
Operator * Arow = new STRUMPACKRowLocMatrix(A);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
|
||||
// strumpack->SetSymmetricPattern(true);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
//Solver * precond = strumpack;
|
||||
|
||||
strumpack->Mult(B, X);
|
||||
//X = 0.0;
|
||||
|
||||
{
|
||||
// Check residual
|
||||
Vector res(X.Size());
|
||||
Vector ssol(X.Size());
|
||||
ssol = X;
|
||||
|
||||
const double Bnrm = B.Norml2();
|
||||
const double Bnrm2 = Bnrm*Bnrm;
|
||||
|
||||
A.Mult(ssol, res);
|
||||
res -= B;
|
||||
|
||||
const double Rnrm = res.Norml2();
|
||||
const double Rnrm2 = Rnrm*Rnrm;
|
||||
|
||||
double sumBnrm2 = 0.0;
|
||||
double sumRnrm2 = 0.0;
|
||||
MPI_Allreduce(&Bnrm2, &sumBnrm2, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
|
||||
MPI_Allreduce(&Rnrm2, &sumRnrm2, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << myid << ": STRUMPACK residual norm " << sqrt(sumRnrm2) << ", B norm " <<
|
||||
sqrt(sumBnrm2) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
delete strumpack;
|
||||
delete Arow;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
|
||||
// preconditioner from hypre.
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
HypreSolver *ams = new HypreAMS(A, prec_fespace);
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*ams);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
delete pcg;
|
||||
delete ams;
|
||||
}
|
||||
|
||||
chrono.Stop();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Solver time " << chrono.RealTime() << endl;
|
||||
}
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(E);
|
||||
ParGridFunction x0(fespace);
|
||||
x0 = 0.0;
|
||||
|
||||
double solnrm = x0.ComputeL2Error(E);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| E_h - E ||_{L^2} = " << err << ", relative error " << err / solnrm
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
//solproj.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete a;
|
||||
delete sigma;
|
||||
delete muinv;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
#ifdef TEST_AIRY
|
||||
const double y = (4.0 * (x(0) + XSHIFT)) - 1.0;
|
||||
const double k = sqrt(K2_VALUE);
|
||||
const double beta = pow(0.25 * k, 2.0/3.0);
|
||||
|
||||
E(0) = 0.0;
|
||||
E(1) = 0.0;
|
||||
E(2) = gsl_sf_airy_Ai(-beta * y, GSL_PREC_DOUBLE);
|
||||
#else
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(2));
|
||||
E(2) = sin(kappa * x(0));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { E(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
#ifdef TEST_AIRY
|
||||
f = 0.0;
|
||||
#else
|
||||
f(0) = (-K2_VALUE + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (-K2_VALUE + kappa * kappa) * sin(kappa * x(2));
|
||||
f(2) = (-K2_VALUE + kappa * kappa) * sin(kappa * x(0));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
@@ -1,848 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "gsl_sf_airy.h"
|
||||
|
||||
#include "multigrid.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
//#define DIRECT_SOLVER
|
||||
|
||||
// Define exact solution
|
||||
void E_exact(const Vector & x, Vector & E);
|
||||
void H_exact(const Vector & x, Vector & H);
|
||||
void f_exact_H(const Vector & x, Vector & f_H);
|
||||
void get_maxwell_solution(const Vector & x, double E[], double curlE[],
|
||||
double curl2E[]);
|
||||
void epsilon_func(const Vector &x, Vector &M);
|
||||
void epsilon2_func(const Vector &x, Vector &M);
|
||||
void epsilon_func_mat(const Vector &x, DenseMatrix &M);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
int sol = 3;
|
||||
|
||||
|
||||
class FOSLSSolver : public Solver
|
||||
{
|
||||
public:
|
||||
FOSLSSolver(ParFiniteElementSpace *fespace_, std::vector<HypreParMatrix*>& P,
|
||||
const int myid_)
|
||||
: Solver(2 * fespace_->GetTrueVSize()), M_inv(MPI_COMM_WORLD),
|
||||
fespace(fespace_),
|
||||
n(fespace_->GetTrueVSize()), nfull(fespace_->GetVSize()), LSpcg(MPI_COMM_WORLD),
|
||||
myid(myid_)
|
||||
{
|
||||
z.SetSize(n);
|
||||
Minv_x.SetSize(n);
|
||||
|
||||
ParMesh *pmesh = fespace->GetParMesh();
|
||||
int dim = pmesh->Dimension();
|
||||
int sdim = pmesh->SpaceDimension();
|
||||
|
||||
VectorFunctionCoefficient epsilon(dim, epsilon_func);
|
||||
VectorFunctionCoefficient epsilonT(epsilon); // transpose of epsilon
|
||||
VectorFunctionCoefficient epsilon2(dim,epsilon2_func);
|
||||
ConstantCoefficient pos(omega);
|
||||
ConstantCoefficient sigma(omega*omega);
|
||||
ScalarVectorProductCoefficient coeff(pos,epsilon);
|
||||
ScalarVectorProductCoefficient coeffT(pos,epsilonT);
|
||||
ScalarVectorProductCoefficient coeff2(sigma,epsilon2);
|
||||
|
||||
bM = new ParBilinearForm(fespace);
|
||||
bM->AddDomainIntegrator(new VectorFEMassIntegrator());
|
||||
bM->Assemble();
|
||||
bM->Finalize();
|
||||
|
||||
bM_eps = new ParBilinearForm(fespace);
|
||||
bM_eps->AddDomainIntegrator(new VectorFEMassIntegrator(epsilonT));
|
||||
bM_eps->Assemble();
|
||||
bM_eps->Finalize();
|
||||
|
||||
bM_curl = new ParMixedBilinearForm(fespace,fespace);
|
||||
bM_curl->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator());
|
||||
bM_curl->Assemble();
|
||||
bM_curl->Finalize();
|
||||
|
||||
Array<int> ess_tdof_list; // empty
|
||||
|
||||
bM->FormSystemMatrix(ess_tdof_list, M);
|
||||
bM_eps->FormSystemMatrix(ess_tdof_list, M_eps);
|
||||
//bM_curl->FormColSystemMatrix(ess_tdof_list, M_curl);
|
||||
{
|
||||
OperatorPtr M_curl_ptr;
|
||||
bM_curl->FormRectangularSystemMatrix(ess_tdof_list, ess_tdof_list, M_curl_ptr);
|
||||
M_curl = M_curl_ptr.As<HypreParMatrix>();
|
||||
}
|
||||
|
||||
M_inv.SetAbsTol(1.0e-12);
|
||||
M_inv.SetRelTol(1.0e-12);
|
||||
M_inv.SetMaxIter(100);
|
||||
M_inv.SetOperator(M);
|
||||
M_inv.SetPrintLevel(0);
|
||||
|
||||
block_trueOffsets.SetSize(5);
|
||||
|
||||
block_trueOffsets[0] = 0;
|
||||
block_trueOffsets[1] = n;
|
||||
block_trueOffsets[2] = n;
|
||||
block_trueOffsets[3] = n;
|
||||
block_trueOffsets[4] = n;
|
||||
block_trueOffsets.PartialSum();
|
||||
|
||||
trueRhs = new BlockVector(block_trueOffsets);
|
||||
trueSol = new BlockVector(block_trueOffsets);
|
||||
|
||||
// _ _ _ _ _ _
|
||||
// | | | | | |
|
||||
// | A00 A01 | | E | |F_E |
|
||||
// | | | | = | |
|
||||
// | A10 A11 | | H | |F_G |
|
||||
// |_ _| |_ _| |_ _|
|
||||
//
|
||||
// A00 = (curl E, curl F) + \omega^2 (E,F)
|
||||
// A01 = - \omega *( (curl E, F) + (E,curl F)
|
||||
// A10 = - \omega *( (curl H, G) + (H,curl G)
|
||||
// A11 = (curl H, curl G) + \omega^2 (H,G)
|
||||
|
||||
ParBilinearForm *a_EE = new ParBilinearForm(fespace);
|
||||
a_EE->AddDomainIntegrator(new CurlCurlIntegrator());
|
||||
a_EE->AddDomainIntegrator(new VectorFEMassIntegrator(coeff2));
|
||||
a_EE->AddBoundaryIntegrator(new VectorFEMassIntegrator());
|
||||
a_EE->Assemble();
|
||||
a_EE->Finalize();
|
||||
HypreParMatrix *A_EE = new HypreParMatrix;
|
||||
a_EE->FormSystemMatrix(ess_tdof_list, *A_EE);
|
||||
|
||||
ParBilinearForm *a_HH = new ParBilinearForm(fespace);
|
||||
a_HH->AddDomainIntegrator(new CurlCurlIntegrator());
|
||||
a_HH->AddDomainIntegrator(new VectorFEMassIntegrator(sigma));
|
||||
a_HH->AddBoundaryIntegrator(new VectorFEMassIntegrator());
|
||||
a_HH->Assemble();
|
||||
a_HH->Finalize();
|
||||
HypreParMatrix *A_HH = new HypreParMatrix;
|
||||
a_HH->FormSystemMatrix(ess_tdof_list, *A_HH);
|
||||
|
||||
ParBilinearForm *a_tang = new ParBilinearForm(fespace);
|
||||
a_tang->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(1.0));
|
||||
a_tang->Assemble();
|
||||
a_tang->Finalize();
|
||||
OperatorHandle A_tang_ptr;
|
||||
a_tang->FormSystemMatrix(ess_tdof_list, A_tang_ptr);
|
||||
HypreParMatrix *A_tang = A_tang_ptr.As<HypreParMatrix>();
|
||||
|
||||
// (k curl u, eps v) + (k u, curl v)
|
||||
ParMixedBilinearForm *a_mix1 = new ParMixedBilinearForm(fespace,fespace);
|
||||
a_mix1->AddDomainIntegrator(new MixedVectorCurlIntegrator(coeffT));
|
||||
a_mix1->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(pos));
|
||||
a_mix1->Assemble();
|
||||
a_mix1->Finalize();
|
||||
HypreParMatrix *A_mix1 = NULL; // new HypreParMatrix;
|
||||
//a_mix1->FormColSystemMatrix(ess_tdof_list, *A_mix1);
|
||||
{
|
||||
OperatorPtr A_mix1_ptr;
|
||||
a_mix1->FormRectangularSystemMatrix(ess_tdof_list, ess_tdof_list, A_mix1_ptr);
|
||||
A_mix1 = A_mix1_ptr.As<HypreParMatrix>();
|
||||
}
|
||||
|
||||
// (k curl u, v) + (k eps u, curl v)
|
||||
ParMixedBilinearForm *a_mix2 = new ParMixedBilinearForm(fespace,fespace);
|
||||
a_mix2->AddDomainIntegrator(new MixedVectorCurlIntegrator(pos));
|
||||
a_mix2->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(coeff));
|
||||
a_mix2->Assemble();
|
||||
a_mix2->Finalize();
|
||||
HypreParMatrix *A_mix2 = NULL; // new HypreParMatrix;
|
||||
//a_mix2->FormColSystemMatrix(ess_tdof_list, *A_mix2);
|
||||
{
|
||||
OperatorPtr A_mix2_ptr;
|
||||
a_mix2->FormRectangularSystemMatrix(ess_tdof_list, ess_tdof_list, A_mix2_ptr);
|
||||
A_mix2 = A_mix2_ptr.As<HypreParMatrix>();
|
||||
}
|
||||
|
||||
BlockOperator *LS_Maxwellop = new BlockOperator(block_trueOffsets);
|
||||
const int numBlocks = 4;
|
||||
|
||||
LS_Maxwellop->SetBlock(0, 0, A_EE);
|
||||
LS_Maxwellop->SetBlock(1, 0, A_mix2, -1.0); // no bc
|
||||
LS_Maxwellop->SetBlock(3, 0, A_tang, -1.0);
|
||||
LS_Maxwellop->SetBlock(0, 1, A_mix1, -1.0); // no bc
|
||||
LS_Maxwellop->SetBlock(1, 1, A_HH);
|
||||
LS_Maxwellop->SetBlock(2, 1, A_tang, -1.0); // other rotation
|
||||
LS_Maxwellop->SetBlock(1, 2, A_tang);
|
||||
LS_Maxwellop->SetBlock(2, 2, A_EE);
|
||||
LS_Maxwellop->SetBlock(3, 2, A_mix2, -1.0); // no bc
|
||||
LS_Maxwellop->SetBlock(0, 3, A_tang); // other rotation
|
||||
LS_Maxwellop->SetBlock(2, 3, A_mix1, -1.0); // no bc
|
||||
LS_Maxwellop->SetBlock(3, 3, A_HH);
|
||||
|
||||
// Set up the preconditioner
|
||||
Array2D<HypreParMatrix*> blockA(numBlocks, numBlocks);
|
||||
Array2D<double> blockAcoef(numBlocks, numBlocks);
|
||||
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
if (LS_Maxwellop->IsZeroBlock(i,j) == 0)
|
||||
{
|
||||
blockA(i,j) = static_cast<HypreParMatrix *>(&LS_Maxwellop->GetBlock(i,j));
|
||||
blockAcoef(i,j) = LS_Maxwellop->GetBlockCoef(i,j);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockA(i,j) = NULL;
|
||||
blockAcoef(i,j) = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LSpcg.SetAbsTol(1.0e-12);
|
||||
LSpcg.SetRelTol(1.0e-8);
|
||||
LSpcg.SetMaxIter(2000);
|
||||
LSpcg.SetOperator(*LS_Maxwellop);
|
||||
LSpcg.SetPrintLevel(1);
|
||||
|
||||
BlockMGSolver * precMG = NULL;
|
||||
|
||||
#ifdef DIRECT_SOLVER
|
||||
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
|
||||
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
|
||||
Array2D<SparseMatrix*> Asp;
|
||||
Asp.SetSize(numBlocks,numBlocks);
|
||||
|
||||
{
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
|
||||
std::vector<int> allnumrows(nprocs);
|
||||
const int blockNumRows = n;
|
||||
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT,
|
||||
MPI_COMM_WORLD);
|
||||
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b].resize(nprocs);
|
||||
all_block_num_loc_rows[b].resize(nprocs);
|
||||
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
Asp(b,j) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
blockProcOffsets[0][0] = 0;
|
||||
for (int i=0; i<nprocs-1; ++i)
|
||||
{
|
||||
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
|
||||
}
|
||||
|
||||
for (int i=0; i<nprocs; ++i)
|
||||
{
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
all_block_num_loc_rows[b][i] = allnumrows[i];
|
||||
}
|
||||
|
||||
for (int b=1; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b][i] = blockProcOffsets[0][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LSH = CreateHypreParMatrixFromBlocks(MPI_COMM_WORLD, block_trueOffsets, blockA,
|
||||
Asp,
|
||||
blockAcoef, blockProcOffsets, all_block_num_loc_rows);
|
||||
|
||||
invLSH = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*LSH), MPI_COMM_WORLD);
|
||||
#else
|
||||
precMG = new BlockMGSolver(LS_Maxwellop->Height(), LS_Maxwellop->Width(),
|
||||
blockA, blockAcoef, P);
|
||||
precMG->SetTheta(0.5);
|
||||
LSpcg.SetPreconditioner(*precMG);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetOperator(const Operator &op) { }
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Solve (curl E, curl v) - k^2 (eps E, v) + ik <pi(u), pi(v)> = (x, v), with no BC,
|
||||
// where x is complex, using FOSLS. This is the Galerkin discretization of
|
||||
// curl curl u - k^2 eps u = x, with ik n x u x n - n x curl u = 0 on the boundary.
|
||||
|
||||
MFEM_VERIFY(x.Size() == 2*n, "");
|
||||
|
||||
(*trueRhs) = 0.0;
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
z[i] = x[i]; // Set z = x_Re
|
||||
}
|
||||
|
||||
M_inv.Mult(z, Minv_x);
|
||||
M_eps.Mult(Minv_x, z);
|
||||
|
||||
trueRhs->GetBlock(0) -= z;
|
||||
|
||||
M_curl->Mult(Minv_x, z);
|
||||
z *= 1.0 / omega;
|
||||
|
||||
trueRhs->GetBlock(1) = z;
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
z[i] = x[n + i]; // Set z = x_Im
|
||||
}
|
||||
|
||||
M_inv.Mult(z, Minv_x);
|
||||
M_eps.Mult(Minv_x, z);
|
||||
|
||||
trueRhs->GetBlock(2) -= z;
|
||||
|
||||
M_curl->Mult(Minv_x, z);
|
||||
z *= 1.0 / omega;
|
||||
|
||||
trueRhs->GetBlock(3) += z;
|
||||
|
||||
#ifdef DIRECT_SOLVER
|
||||
invLSH->Mult(*trueRhs, *trueSol);
|
||||
#else
|
||||
LSpcg.Mult(*trueRhs, *trueSol);
|
||||
#endif
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
y[i] = trueSol->GetBlock(0)[i]; // Set y_Re = E_Re
|
||||
}
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
y[n + i] = trueSol->GetBlock(2)[i]; // Set y_Im = E_Im
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexFOSLSTest()
|
||||
{
|
||||
Array<int> block_offsets;
|
||||
|
||||
block_offsets.SetSize(5);
|
||||
|
||||
block_offsets[0] = 0;
|
||||
block_offsets[1] = nfull;
|
||||
block_offsets[2] = nfull;
|
||||
block_offsets[3] = nfull;
|
||||
block_offsets[4] = nfull;
|
||||
block_offsets.PartialSum();
|
||||
|
||||
BlockVector rhs(block_offsets);
|
||||
BlockVector rhsIm(block_offsets);
|
||||
|
||||
rhs = 0.0;
|
||||
rhsIm = 0.0;
|
||||
|
||||
const double ci = 0.0; // 3.3;
|
||||
|
||||
// Exact complex solution: E = Er + i Ei = Epw + ci i Epw, where Epw is E_exact.
|
||||
|
||||
// Set up the linear form with the real part Fr only.
|
||||
const int sdim = 3;
|
||||
VectorFunctionCoefficient Eex(sdim, E_exact);
|
||||
VectorFunctionCoefficient Hex(sdim, H_exact);
|
||||
ConstantCoefficient negOne(-1.0);
|
||||
VectorFunctionCoefficient mEex(sdim, E_exact, &negOne);
|
||||
VectorFunctionCoefficient mHex(sdim, H_exact, &negOne);
|
||||
|
||||
ConstantCoefficient neg(-omega);
|
||||
ConstantCoefficient pos(omega);
|
||||
VectorFunctionCoefficient f_H(3,f_exact_H); // f / omega
|
||||
ScalarVectorProductCoefficient sf_H(neg,f_H);
|
||||
ScalarVectorProductCoefficient spf_H(pos,f_H);
|
||||
ScalarVectorProductCoefficient mf_H(negOne,f_H);
|
||||
|
||||
VectorFunctionCoefficient epsilon(3, epsilon_func);
|
||||
VectorFunctionCoefficient epsilonT(3, epsilon_func); // transpose of epsilon
|
||||
MatrixFunctionCoefficient epsilonTmat(3,
|
||||
epsilon_func_mat); // transpose of epsilon
|
||||
|
||||
MatVecCoefficient epsT_spf_H(epsilonTmat, spf_H);
|
||||
MatVecCoefficient epsT_sf_H(epsilonTmat, sf_H);
|
||||
|
||||
ParLinearForm *b_E = new ParLinearForm;
|
||||
b_E->Update(fespace, rhs.GetBlock(0), 0);
|
||||
b_E->AddDomainIntegrator(new VectorFEDomainLFIntegrator(
|
||||
epsT_sf_H)); // (k^{-1} Fr, -k eps Qr)
|
||||
b_E->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
|
||||
Eex)); // <n x E_Re x n, n x Q_Re x n>
|
||||
b_E->Assemble();
|
||||
|
||||
ParLinearForm *b_H = new ParLinearForm;
|
||||
b_H->Update(fespace, rhs.GetBlock(1), 0);
|
||||
b_H->AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(
|
||||
f_H)); // (k^{-1} Fr, curl Rr)
|
||||
b_H->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
|
||||
Hex)); // <n x H_Re, n x R_Re>
|
||||
b_H->Assemble();
|
||||
|
||||
ParLinearForm *b_E_Im = new ParLinearForm;
|
||||
b_E_Im->Update(fespace, rhs.GetBlock(2), 0);
|
||||
b_E_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
|
||||
mHex)); // <n x H_Re, n x Q_Im x n>
|
||||
b_E_Im->Assemble();
|
||||
|
||||
ParLinearForm *b_H_Im = new ParLinearForm;
|
||||
b_H_Im->Update(fespace, rhs.GetBlock(3), 0);
|
||||
b_H_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
|
||||
mEex)); // -<n x E_Re x n, n x R_Im>
|
||||
b_H_Im->Assemble();
|
||||
|
||||
// Add the imaginary part Fi.
|
||||
|
||||
ParLinearForm *b_Ei = new ParLinearForm;
|
||||
b_Ei->Update(fespace, rhsIm.GetBlock(0), 0);
|
||||
b_Ei->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
|
||||
Hex)); // -<n x H_Im, n x Q_Re x n>
|
||||
b_Ei->Assemble();
|
||||
|
||||
ParLinearForm *b_Hi = new ParLinearForm;
|
||||
b_Hi->Update(fespace, rhsIm.GetBlock(1), 0);
|
||||
b_Hi->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
|
||||
Eex)); // <n x E_Im x n, n x R_Re>
|
||||
b_Hi->Assemble();
|
||||
|
||||
ParLinearForm *b_Ei_Im = new ParLinearForm;
|
||||
b_Ei_Im->Update(fespace, rhsIm.GetBlock(2), 0);
|
||||
b_Ei_Im->AddDomainIntegrator(new VectorFEDomainLFIntegrator(
|
||||
epsT_sf_H)); // -(k^{-1} Fi, k eps Qi)
|
||||
b_Ei_Im->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
|
||||
Eex)); // <n x E_Im x n, n x Q_Im x n>
|
||||
b_Ei_Im->Assemble();
|
||||
|
||||
ParLinearForm *b_Hi_Im = new ParLinearForm;
|
||||
b_Hi_Im->Update(fespace, rhsIm.GetBlock(3), 0);
|
||||
b_Hi_Im->AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(
|
||||
f_H)); // (k^{-1} Fi, curl Ri)
|
||||
b_Hi_Im->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
|
||||
Hex)); // <n x H_Im, n x R_Im>
|
||||
b_Hi_Im->Assemble();
|
||||
|
||||
rhsIm *= ci;
|
||||
rhs += rhsIm;
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
fespace->GetProlongationMatrix()->MultTranspose(rhs.GetBlock(i),
|
||||
trueRhs->GetBlock(i));
|
||||
}
|
||||
|
||||
#ifdef DIRECT_SOLVER
|
||||
invLSH->Mult(*trueRhs, *trueSol);
|
||||
#else
|
||||
LSpcg.Mult(*trueRhs, *trueSol);
|
||||
#endif
|
||||
|
||||
// Check error
|
||||
ParGridFunction E_gf(fespace);
|
||||
int order = 2;
|
||||
int order_quad = std::max(2, 2*order+1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
ParMesh *pmesh = fespace->GetParMesh();
|
||||
|
||||
// Check error of real part
|
||||
|
||||
E_gf.SetFromTrueDofs(trueSol->GetBlock(0));
|
||||
double Error_E = E_gf.ComputeL2Error(Eex, irs);
|
||||
double norm_E = ComputeGlobalLpNorm(2, Eex, *pmesh, irs);
|
||||
|
||||
cout << myid << ": real error " << Error_E << " relative to " << norm_E << endl;
|
||||
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
E_gf.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// Check error of imaginary part
|
||||
E_gf.SetFromTrueDofs(trueSol->GetBlock(2));
|
||||
const double iml2 = E_gf.Norml2();
|
||||
E_gf *= ci == 0.0 ? 0.0 : (1.0 / ci);
|
||||
Error_E = E_gf.ComputeL2Error(Eex, irs);
|
||||
|
||||
cout << myid << ": imag error " << Error_E << " relative to " << norm_E <<
|
||||
", l2 norm " << iml2 << endl;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
BlockVector *trueRhs, *trueSol;
|
||||
|
||||
Array<int> block_trueOffsets;
|
||||
|
||||
ParBilinearForm *bM, *bM_eps;
|
||||
ParMixedBilinearForm *bM_curl;
|
||||
|
||||
HypreParMatrix M, M_eps;
|
||||
HypreParMatrix *M_curl;
|
||||
|
||||
CGSolver M_inv;
|
||||
|
||||
const int n;
|
||||
const int nfull;
|
||||
const int myid;
|
||||
|
||||
mutable Vector z, Minv_x;
|
||||
|
||||
CGSolver LSpcg;
|
||||
|
||||
STRUMPACKSolver *invLSH;
|
||||
HypreParMatrix *LSH;
|
||||
|
||||
ParFiniteElementSpace *fespace;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
StopWatch chrono;
|
||||
|
||||
// 1. Initialize MPI
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv); // Initialize MPI
|
||||
MPI_Comm_size(MPI_COMM_WORLD,
|
||||
&num_procs); //total number of processors available
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // Determine process identifier
|
||||
// 1. Parse command-line options.
|
||||
// geometry file
|
||||
const char *mesh_file = "../data/inline-hex.mesh";
|
||||
// finite element order of approximation
|
||||
int order = 1;
|
||||
// static condensation flag
|
||||
bool static_cond = false;
|
||||
// visualization flag
|
||||
bool visualization = 1;
|
||||
// number of wavelengths
|
||||
double k = 1.0;
|
||||
// number of mg levels
|
||||
int ref_levels = 1;
|
||||
// number of initial ref
|
||||
int initref = 1;
|
||||
|
||||
// optional command line inputs
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&k, "-k", "--wavelengths",
|
||||
"Number of wavelengths.");
|
||||
args.AddOption(&ref_levels, "-ref", "--ref_levels",
|
||||
"Number of Refinements.");
|
||||
args.AddOption(&initref, "-initref", "--initref",
|
||||
"Number of initial refinements.");
|
||||
args.AddOption(&sol, "-sol", "--exact",
|
||||
"Exact solution flag - "
|
||||
" 1:sinusoidal, 2: point source, 3: plane wave");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
// check if the inputs are correct
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// Angular frequency
|
||||
//omega = 2.0*k*M_PI;
|
||||
omega = k;
|
||||
|
||||
// 2. Read the mesh from the given mesh file.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
|
||||
if (sol == 4)
|
||||
{
|
||||
mesh->EnsureNodes();
|
||||
GridFunction *nodes = mesh->GetNodes();
|
||||
(*nodes) *= 0.5;
|
||||
}
|
||||
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 3. Executing uniform h-refinement
|
||||
for (int i = 0; i < initref; i++ )
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
|
||||
// 4. Define a finite element space on the mesh.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
std::vector<ParFiniteElementSpace * > fespaces(ref_levels+1);
|
||||
std::vector<ParMesh * > ParMeshes(ref_levels+1);
|
||||
std::vector<HypreParMatrix*> P(ref_levels);
|
||||
|
||||
for (int i = 0; i < ref_levels; i++)
|
||||
{
|
||||
ParMeshes[i] =new ParMesh(*pmesh);
|
||||
fespaces[i] = new ParFiniteElementSpace(*fespace, *ParMeshes[i]);
|
||||
pmesh->UniformRefinement();
|
||||
// Update fespace
|
||||
fespace->Update();
|
||||
OperatorHandle Tr(Operator::Hypre_ParCSR);
|
||||
fespace->GetTrueTransferOperator(*fespaces[i], Tr);
|
||||
Tr.SetOperatorOwner(false);
|
||||
Tr.Get(P[i]);
|
||||
}
|
||||
fespaces[ref_levels] = new ParFiniteElementSpace(*fespace);
|
||||
|
||||
FOSLSSolver fosls(fespace, P, myid);
|
||||
|
||||
fosls.ComplexFOSLSTest();
|
||||
|
||||
for (auto p: ParMeshes) { delete p; }
|
||||
for (auto p: fespaces) { delete p; }
|
||||
for (auto p: P) { delete p; }
|
||||
ParMeshes.clear();
|
||||
fespaces.clear();
|
||||
P.clear();
|
||||
delete fec;
|
||||
delete fespace;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//define exact solution
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
double curlE[3], curl2E[3];
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
}
|
||||
|
||||
void H_exact(const Vector &x, Vector &H)
|
||||
{
|
||||
double E[3], curlE[3], curl2E[3];
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
for (int i = 0; i<3; i++) { H(i) = curlE[i]/omega; }
|
||||
}
|
||||
|
||||
|
||||
void f_exact_H(const Vector &x, Vector &f)
|
||||
{
|
||||
// curl H - omega E = f
|
||||
// = curl (curl E / omega) - omega E
|
||||
f = 0.0;
|
||||
if (sol !=4)
|
||||
{
|
||||
double E[3], curlE[3], curl2E[3];
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
f(0) = curl2E[0] / omega - omega * E[0];
|
||||
f(1) = curl2E[1] / omega - omega * E[1];
|
||||
f(2) = curl2E[2] / omega - omega * E[2];
|
||||
}
|
||||
}
|
||||
|
||||
void get_maxwell_solution(const Vector &X, double E[], double curlE[],
|
||||
double curl2E[])
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
double z = X[2];
|
||||
|
||||
if (sol ==-1)
|
||||
{
|
||||
E[0] = y * z * (1.0 - y) * (1.0 - z);
|
||||
E[1] = x * y * z * (1.0 - x) * (1.0 - z);
|
||||
E[2] = x * y * (1.0 - x) * (1.0 - y);
|
||||
|
||||
curlE[0] = -(x-1.0) * x * (y*(2.0*z-3.0)+1.0);
|
||||
curlE[1] = -2.0*(y-1.0)*y*(x-z);
|
||||
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
|
||||
|
||||
curl2E[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
|
||||
curl2E[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
|
||||
curl2E[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
|
||||
}
|
||||
else if (sol == 0) // polynomial
|
||||
{
|
||||
// Polynomial vanishing on the boundary
|
||||
E[0] = y * z * (1.0 - y) * (1.0 - z);
|
||||
E[1] = (1.0 - x) * x * y * (1.0 - z) * z;
|
||||
E[2] = (1.0 - x) * x * (1.0 - y) * y;
|
||||
//
|
||||
curlE[0] = -(-1.0 + x) * x * (1.0 + y * (-3.0 + 2.0 * z));
|
||||
curlE[1] = -2.0 * (-1.0 + y) * y * (x - z);
|
||||
curlE[2] = (1.0 + (-3.0 + 2.0 * x) * y) * (-1.0 + z) * z;
|
||||
|
||||
curl2E[0] = -2.0 * (-1.0 + y) * y + (-3.0 + 2.0 * x) * (-1.0 + z) * z;
|
||||
curl2E[1] = -2.0 * y * (-x + x * x + (-1.0 + z) * z);
|
||||
curl2E[2] = -2.0 * (-1.0 + y) * y + (-1.0 + x) * x * (-3.0 + 2.0 * z);
|
||||
}
|
||||
else if (sol == 1) // sinusoidal
|
||||
{
|
||||
E[0] = sin(omega * y);
|
||||
E[1] = sin(omega * z);
|
||||
E[2] = sin(omega * x);
|
||||
|
||||
curlE[0] = -omega * cos(omega * z);
|
||||
curlE[1] = -omega * cos(omega * x);
|
||||
curlE[2] = -omega * cos(omega * y);
|
||||
|
||||
curl2E[0] = omega * omega * E[0];
|
||||
curl2E[1] = omega * omega * E[1];
|
||||
curl2E[2] = omega * omega * E[2];
|
||||
}
|
||||
else if (sol == 2) // point source
|
||||
{
|
||||
// shift to avoid singularity
|
||||
double x0 = x + 0.1;
|
||||
double x1 = y + 0.1;
|
||||
double x2 = z + 0.1;
|
||||
double r = sqrt(x0 * x0 + x1 * x1 + x2 * x2);
|
||||
|
||||
E[0] = cos(omega * r);
|
||||
E[1] = 0.0;
|
||||
E[2] = 0.0;
|
||||
|
||||
double r_x = x0 / r;
|
||||
double r_y = x1 / r;
|
||||
double r_z = x2 / r;
|
||||
double r_xy = -(r_x / r) * r_y;
|
||||
double r_xz = -(r_x / r) * r_z;
|
||||
double r_yx = r_xy;
|
||||
double r_yy = (1.0 / r) * (1.0 - r_y * r_y);
|
||||
double r_zx = r_xz;
|
||||
double r_zz = (1.0 / r) * (1.0 - r_z * r_z);
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = -omega * r_z * sin(omega * r);
|
||||
curlE[2] = omega * r_y * sin(omega * r);
|
||||
|
||||
curl2E[0] = omega * ((r_yy + r_zz) * sin(omega * r) +
|
||||
(omega * r_y * r_y + omega * r_z * r_z) * cos(omega * r));
|
||||
curl2E[1] = -omega * (r_yx * sin(omega * r) + omega * r_y * r_x * cos(
|
||||
omega * r));
|
||||
curl2E[2] = -omega * (r_zx * sin(omega * r) + omega * r_z * r_x * cos(
|
||||
omega * r));
|
||||
}
|
||||
else if (sol == 3) // plane wave
|
||||
{
|
||||
double coeff = omega / sqrt(3.0);
|
||||
E[0] = cos(coeff * (x + y + z));
|
||||
E[1] = 0.0;
|
||||
E[2] = 0.0;
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = -coeff * sin(coeff * (x + y + z));
|
||||
curlE[2] = coeff * sin(coeff * (x + y + z));
|
||||
|
||||
curl2E[0] = 2.0 * coeff * coeff * E[0];
|
||||
curl2E[1] = -coeff * coeff * E[0];
|
||||
curl2E[2] = -coeff * coeff * E[0];
|
||||
}
|
||||
else if (sol == -1)
|
||||
{
|
||||
E[0] = cos(omega * y);
|
||||
E[1] = 0.0;
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = 0.0;
|
||||
curlE[2] = -omega * sin(omega * y);
|
||||
|
||||
curl2E[0] = omega*omega * cos(omega*y);
|
||||
curl2E[1] = 0.0;
|
||||
curl2E[2] = 0.0;
|
||||
}
|
||||
else if (sol == 4) // Airy function
|
||||
{
|
||||
E[0] = 0;
|
||||
E[1] = 0;
|
||||
// double b = -pow(omega/4.0,2.0/3.0)*(4.0*x(0)-1.0);
|
||||
double b = -pow(omega/4.0,2.0/3.0)*(4.0*x-1.0);
|
||||
//E[2] = boost::math::airy_ai(b);
|
||||
E[2] = gsl_sf_airy_Ai(b, GSL_PREC_DOUBLE);
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = 4.0 * pow(omega/4.0,2.0/3.0) * gsl_sf_airy_Ai_deriv(b,
|
||||
GSL_PREC_DOUBLE);
|
||||
curlE[2] = 0.0;
|
||||
|
||||
// not used
|
||||
curl2E[0] = 0.0;
|
||||
curl2E[1] = 0.0;
|
||||
curl2E[2] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void epsilon_func(const Vector &x, Vector &M)
|
||||
{
|
||||
M.SetSize(3);
|
||||
|
||||
M = 1.0;
|
||||
if (sol == 4)
|
||||
{
|
||||
M[2] = 4.0*x(0)-1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void epsilon2_func(const Vector &x, Vector &M)
|
||||
{
|
||||
M.SetSize(3);
|
||||
|
||||
M = 1.0;
|
||||
if (sol == 4)
|
||||
{
|
||||
M[2] = (4.0*x(0)-1.0) * (4.0*x(0)-1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void epsilon_func_mat(const Vector &x, DenseMatrix &M)
|
||||
{
|
||||
M.SetSize(3);
|
||||
|
||||
M = 0.0;
|
||||
M(0,0) = 1.0;
|
||||
M(1,1) = 1.0;
|
||||
if (sol != 4)
|
||||
{
|
||||
M(2,2) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
M(2,2) = 4.0*x(0)-1.0;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,764 +0,0 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "gsl_sf_airy.h"
|
||||
|
||||
#include "multigrid.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
#define COMPLEX_IMPEDANCE // Whether to use impedance BC and solve a complex system
|
||||
|
||||
// Define exact solution
|
||||
void E_exact(const Vector & x, Vector & E);
|
||||
void H_exact(const Vector & x, Vector & H);
|
||||
void f_exact_H(const Vector & x, Vector & f_H);
|
||||
void get_maxwell_solution(const Vector & x, double E[], double curlE[],
|
||||
double curl2E[]);
|
||||
void epsilon_func(const Vector &x, DenseMatrix &M);
|
||||
void epsilon2_func(const Vector &x, DenseMatrix &M);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
int sol = 4;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
StopWatch chrono;
|
||||
|
||||
// 1. Initialize MPI
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv); // Initialize MPI
|
||||
MPI_Comm_size(MPI_COMM_WORLD,
|
||||
&num_procs); //total number of processors available
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // Determine process identifier
|
||||
// 1. Parse command-line options.
|
||||
// geometry file
|
||||
const char *mesh_file = "../data/inline-hex.mesh";
|
||||
// finite element order of approximation
|
||||
int order = 1;
|
||||
// static condensation flag
|
||||
bool static_cond = false;
|
||||
// visualization flag
|
||||
bool visualization = 1;
|
||||
// number of wavelengths
|
||||
double k = 1.0;
|
||||
// number of mg levels
|
||||
int ref_levels = 1;
|
||||
// number of initial ref
|
||||
int initref = 1;
|
||||
|
||||
// optional command line inputs
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&k, "-k", "--wavelengths",
|
||||
"Number of wavelengths.");
|
||||
args.AddOption(&ref_levels, "-ref", "--ref_levels",
|
||||
"Number of Refinements.");
|
||||
args.AddOption(&initref, "-initref", "--initref",
|
||||
"Number of initial refinements.");
|
||||
args.AddOption(&sol, "-sol", "--exact",
|
||||
"Exact solution flag - "
|
||||
" 1:sinusoidal, 2: point source, 3: plane wave");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
// check if the inputs are correct
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// Angular frequency
|
||||
//omega = 2.0*k*M_PI;
|
||||
omega = k;
|
||||
|
||||
// 2. Read the mesh from the given mesh file.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
|
||||
if (sol == 4)
|
||||
{
|
||||
mesh->EnsureNodes();
|
||||
GridFunction *nodes = mesh->GetNodes();
|
||||
(*nodes) *= 0.5;
|
||||
}
|
||||
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 3. Executing uniform h-refinement
|
||||
for (int i = 0; i < initref; i++ )
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
|
||||
// 4. Define a finite element space on the mesh.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
std::vector<ParFiniteElementSpace * > fespaces(ref_levels+1);
|
||||
std::vector<ParMesh * > ParMeshes(ref_levels+1);
|
||||
std::vector<HypreParMatrix*> P(ref_levels);
|
||||
|
||||
for (int i = 0; i < ref_levels; i++)
|
||||
{
|
||||
ParMeshes[i] =new ParMesh(*pmesh);
|
||||
fespaces[i] = new ParFiniteElementSpace(*fespace, *ParMeshes[i]);
|
||||
pmesh->UniformRefinement();
|
||||
// Update fespace
|
||||
fespace->Update();
|
||||
OperatorHandle Tr(Operator::Hypre_ParCSR);
|
||||
fespace->GetTrueTransferOperator(*fespaces[i], Tr);
|
||||
Tr.SetOperatorOwner(false);
|
||||
Tr.Get(P[i]);
|
||||
}
|
||||
fespaces[ref_levels] = new ParFiniteElementSpace(*fespace);
|
||||
|
||||
Array<int> ess_tdof_listE;
|
||||
Array<int> ess_tdof_listH;
|
||||
Array<int> ess_bdrE(pmesh->bdr_attributes.Max());
|
||||
Array<int> ess_bdrH(pmesh->bdr_attributes.Max());
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
ess_bdrE = 0;
|
||||
ess_bdrH = 0; // Neumann
|
||||
#else
|
||||
ess_bdrE = 1;
|
||||
ess_bdrH = 0; // Neumann
|
||||
#endif
|
||||
fespace->GetEssentialTrueDofs(ess_bdrE, ess_tdof_listE);
|
||||
fespace->GetEssentialTrueDofs(ess_bdrH, ess_tdof_listH);
|
||||
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
Array<int> bdr_attr(pmesh->bdr_attributes.Max());
|
||||
bdr_attr = 1;
|
||||
ConstantCoefficient impedance(omega);
|
||||
RestrictedCoefficient imp_rest(impedance, bdr_attr);
|
||||
Array<int> block_offsets(5);
|
||||
Array<int> block_trueOffsets(5);
|
||||
#else
|
||||
Array<int> block_offsets(3);
|
||||
Array<int> block_trueOffsets(3);
|
||||
#endif
|
||||
block_offsets[0] = 0;
|
||||
block_offsets[1] = fespace->GetVSize();
|
||||
block_offsets[2] = fespace->GetVSize();
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
block_offsets[3] = fespace->GetVSize();
|
||||
block_offsets[4] = fespace->GetVSize();
|
||||
#endif
|
||||
block_offsets.PartialSum();
|
||||
|
||||
block_trueOffsets[0] = 0;
|
||||
block_trueOffsets[1] = fespace->TrueVSize();
|
||||
block_trueOffsets[2] = fespace->TrueVSize();
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
block_trueOffsets[3] = fespace->TrueVSize();
|
||||
block_trueOffsets[4] = fespace->TrueVSize();
|
||||
#endif
|
||||
block_trueOffsets.PartialSum();
|
||||
|
||||
// _ _ _ _ _ _
|
||||
// | | | | | |
|
||||
// | A00 A01 | | E | |F_E |
|
||||
// | | | | = | |
|
||||
// | A10 A11 | | H | |F_G |
|
||||
// |_ _| |_ _| |_ _|
|
||||
//
|
||||
// A00 = (curl E, curl F) + \omega^2 (E,F)
|
||||
// A01 = - \omega *( (curl E, F) + (E,curl F)
|
||||
// A10 = - \omega *( (curl H, G) + (H,curl G)
|
||||
// A11 = (curl H, curl G) + \omega^2 (H,G)
|
||||
|
||||
BlockVector x(block_offsets), rhs(block_offsets);
|
||||
BlockVector trueX(block_trueOffsets), trueRhs(block_trueOffsets);
|
||||
|
||||
x = 0.0;
|
||||
rhs = 0.0;
|
||||
trueX = 0.0;
|
||||
trueRhs = 0.0;
|
||||
|
||||
VectorFunctionCoefficient Eex(sdim, E_exact);
|
||||
ConstantCoefficient negOne(-1.0);
|
||||
VectorFunctionCoefficient mEex(sdim, E_exact, &negOne);
|
||||
|
||||
ParGridFunction * E_gf = new ParGridFunction;
|
||||
ParGridFunction * Exact_gf = new ParGridFunction(fespace);
|
||||
E_gf->MakeRef(fespace, x.GetBlock(0));
|
||||
E_gf->ProjectCoefficient(Eex);
|
||||
Exact_gf->ProjectCoefficient(Eex);
|
||||
|
||||
VectorFunctionCoefficient Hex(sdim, H_exact);
|
||||
ParGridFunction * H_gf = new ParGridFunction;
|
||||
H_gf->MakeRef(fespace, x.GetBlock(1));
|
||||
H_gf->ProjectCoefficient(Hex);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient sigma(pow(omega, 2));
|
||||
ConstantCoefficient neg(-abs(omega));
|
||||
ConstantCoefficient pos(abs(omega));
|
||||
|
||||
MatrixFunctionCoefficient epsilon(dim,epsilon_func);
|
||||
TransposeMatrixCoefficient epsilonT(epsilon); // transpose of epsilon
|
||||
MatrixFunctionCoefficient epsilon2(dim,epsilon2_func);
|
||||
ScalarMatrixProductCoefficient coeff(neg,epsilon);
|
||||
ScalarMatrixProductCoefficient coeff2(sigma,epsilon2);
|
||||
|
||||
// 6. Set up the linear form
|
||||
VectorFunctionCoefficient f_H(sdim,f_exact_H);
|
||||
ScalarVectorProductCoefficient sf_H(neg,f_H);
|
||||
|
||||
MatVecCoefficient epsT_sf_H(epsilonT, sf_H);
|
||||
|
||||
ParLinearForm *b_E = new ParLinearForm;
|
||||
b_E->Update(fespace, rhs.GetBlock(0), 0);
|
||||
b_E->AddDomainIntegrator(new VectorFEDomainLFIntegrator(epsT_sf_H));
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
b_E->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
|
||||
Eex)); // <g_Im, n x Q x n> = <n x E_Re x n, n x Q x n>
|
||||
|
||||
ParLinearForm *b_E_Im = new ParLinearForm;
|
||||
b_E_Im->Update(fespace, rhs.GetBlock(2), 0);
|
||||
b_E_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
|
||||
Hex)); // -<g_Re, n x Q x n> = <n x H_Re, n x Q x n>
|
||||
b_E_Im->Assemble();
|
||||
#endif
|
||||
b_E->Assemble();
|
||||
|
||||
ParLinearForm *b_H = new ParLinearForm;
|
||||
b_H->Update(fespace, rhs.GetBlock(1), 0);
|
||||
b_H->AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(f_H));
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
b_H->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
|
||||
Hex)); // -<g_Re, n x R> = <n x H_Re, n x R>
|
||||
|
||||
ParLinearForm *b_H_Im = new ParLinearForm;
|
||||
b_H_Im->Update(fespace, rhs.GetBlock(3), 0);
|
||||
b_H_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
|
||||
mEex)); // -<g_Im, n x R> = -<n x E_Re x n, n x R>
|
||||
b_H_Im->Assemble();
|
||||
#endif
|
||||
b_H->Assemble();
|
||||
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
fespace->GetProlongationMatrix()->MultTranspose(rhs.GetBlock(2),
|
||||
trueRhs.GetBlock(2));
|
||||
fespace->GetProlongationMatrix()->MultTranspose(rhs.GetBlock(3),
|
||||
trueRhs.GetBlock(3));
|
||||
#endif
|
||||
|
||||
// 7. Bilinear form a(.,.) on the finite element space
|
||||
ParBilinearForm *a_EE = new ParBilinearForm(fespace);
|
||||
a_EE->AddDomainIntegrator(new CurlCurlIntegrator(one));
|
||||
a_EE->AddDomainIntegrator(new VectorFEMassIntegrator(coeff2));
|
||||
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
a_EE->AddBoundaryIntegrator(new VectorFEMassIntegrator());
|
||||
//a_EE->AddBoundaryIntegrator(new VectorFEMassIntegrator(imp_rest));
|
||||
//a_EE->AddBoundaryIntegrator(new BoundaryMassIntegrator(imp_rest));
|
||||
//a_EE->AddBdrFaceIntegrator(new BoundaryMassIntegrator(imp_rest));
|
||||
#endif
|
||||
|
||||
a_EE->Assemble();
|
||||
a_EE->Finalize();
|
||||
HypreParMatrix *A_EE = new HypreParMatrix;
|
||||
a_EE->FormLinearSystem(ess_tdof_listE, x.GetBlock(0), rhs.GetBlock(0), *A_EE,
|
||||
trueX.GetBlock(0), trueRhs.GetBlock(0));
|
||||
|
||||
ParBilinearForm *a_HH = new ParBilinearForm(fespace);
|
||||
a_HH->AddDomainIntegrator(new CurlCurlIntegrator(one)); // one is the coeff
|
||||
a_HH->AddDomainIntegrator(new VectorFEMassIntegrator(sigma));
|
||||
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
a_HH->AddBoundaryIntegrator(new VectorFEMassIntegrator());
|
||||
//a_HH->AddBoundaryIntegrator(new VectorFEMassIntegrator(imp_rest));
|
||||
//a_HH->AddBoundaryIntegrator(new BoundaryMassIntegrator(imp_rest));
|
||||
#endif
|
||||
|
||||
a_HH->Assemble();
|
||||
a_HH->Finalize();
|
||||
HypreParMatrix *A_HH = new HypreParMatrix;
|
||||
a_HH->FormLinearSystem(ess_tdof_listH, x.GetBlock(1), rhs.GetBlock(1), *A_HH,
|
||||
trueX.GetBlock(1), trueRhs.GetBlock(1));
|
||||
|
||||
ParMixedBilinearForm *a_HE = new ParMixedBilinearForm(fespace,fespace);
|
||||
a_HE->AddDomainIntegrator(new MixedVectorCurlIntegrator(neg));
|
||||
a_HE->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(coeff));
|
||||
a_HE->Assemble();
|
||||
a_HE->Finalize();
|
||||
HypreParMatrix *A_HE = new HypreParMatrix;
|
||||
a_HE->FormColLinearSystem(ess_tdof_listE,x.GetBlock(0),rhs.GetBlock(1),*A_HE,
|
||||
trueX.GetBlock(0),trueRhs.GetBlock(1));
|
||||
|
||||
HypreParMatrix *A_EH = A_HE->Transpose();
|
||||
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
ParBilinearForm *a_EH_Im = new ParBilinearForm(fespace);
|
||||
//a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(imp_rest));
|
||||
//a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(impedance));
|
||||
//a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(omega));
|
||||
a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator());
|
||||
a_EH_Im->Assemble();
|
||||
a_EH_Im->Finalize();
|
||||
|
||||
OperatorHandle A_EH_Im_ptr;
|
||||
a_EH_Im->FormSystemMatrix(ess_tdof_listE,
|
||||
A_EH_Im_ptr); // empty ess_tdof_list for impedance
|
||||
|
||||
HypreParMatrix *A_EH_Im = A_EH_Im_ptr.As<HypreParMatrix>();
|
||||
|
||||
HypreParMatrix *A_HE_Im = A_EH_Im->Transpose();
|
||||
#endif
|
||||
|
||||
BlockOperator *LS_Maxwellop = new BlockOperator(block_trueOffsets);
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
const int numBlocks = 4;
|
||||
#else
|
||||
const int numBlocks = 2;
|
||||
#endif
|
||||
|
||||
LS_Maxwellop->SetBlock(0, 0, A_EE);
|
||||
LS_Maxwellop->SetBlock(0, 1, A_EH);
|
||||
LS_Maxwellop->SetBlock(1, 0, A_HE);
|
||||
LS_Maxwellop->SetBlock(1, 1, A_HH);
|
||||
|
||||
#ifdef COMPLEX_IMPEDANCE
|
||||
LS_Maxwellop->SetBlock(0, 3, A_EH_Im);
|
||||
LS_Maxwellop->SetBlock(1, 2, A_HE_Im, -1.0);
|
||||
|
||||
LS_Maxwellop->SetBlock(2, 1, A_EH_Im, -1.0);
|
||||
LS_Maxwellop->SetBlock(3, 0, A_HE_Im);
|
||||
|
||||
LS_Maxwellop->SetBlock(2, 2, A_EE);
|
||||
LS_Maxwellop->SetBlock(2, 3, A_EH);
|
||||
LS_Maxwellop->SetBlock(3, 2, A_HE);
|
||||
LS_Maxwellop->SetBlock(3, 3, A_HH);
|
||||
#endif
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of fine grid system: "
|
||||
<< 2.0 * A_EE->GetGlobalNumRows() << " x " << 2.0* A_EE->GetGlobalNumCols() <<
|
||||
endl;
|
||||
}
|
||||
|
||||
// Set up the preconditioner
|
||||
Array2D<HypreParMatrix*> blockA(numBlocks, numBlocks);
|
||||
Array2D<double> blockAcoef(numBlocks, numBlocks);
|
||||
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
if (LS_Maxwellop->IsZeroBlock(i,j) == 0)
|
||||
{
|
||||
blockA(i,j) = static_cast<HypreParMatrix *>(&LS_Maxwellop->GetBlock(i,j));
|
||||
blockAcoef(i,j) = LS_Maxwellop->GetBlockCoef(i,j);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockA(i,j) = NULL;
|
||||
blockAcoef(i,j) = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // double nnz = A_HH->NNZ();
|
||||
// // double ndof = A_HH->GetGlobalNumRows();
|
||||
// // double est_mem_b = nnz*12.0 + (ndof+1.0)*4;
|
||||
// // double gb = est_mem_b*4.0/pow(1024.0,3);
|
||||
|
||||
// // mfem::out << "Estimated memory taken by the global matrix: " << gb << endl;
|
||||
|
||||
int maxit(2000);
|
||||
double rtol(1.e-8);
|
||||
double atol(1.e-12);
|
||||
|
||||
// trueX = 0.0;
|
||||
CGSolver pcg(MPI_COMM_WORLD);
|
||||
pcg.SetAbsTol(atol);
|
||||
pcg.SetRelTol(rtol);
|
||||
pcg.SetMaxIter(maxit);
|
||||
pcg.SetOperator(*LS_Maxwellop);
|
||||
pcg.SetPrintLevel(1);
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
BlockMGSolver * precMG = new BlockMGSolver(LS_Maxwellop->Height(),
|
||||
LS_Maxwellop->Width(), blockA, blockAcoef, P);
|
||||
//precMG->SetTheta(0.5);
|
||||
// // int lv_coarse = min(ref_levels,ref_levels-1);
|
||||
// // int levels = ref_levels - lv_coarse;
|
||||
// // BlkParSchwarzSmoother * precAS = new BlkParSchwarzSmoother(fespaces[lv_coarse]->GetParMesh(),levels,fespaces[ref_levels],LS_Maxwellop);
|
||||
chrono.Stop();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "MG Setup time: " << chrono.RealTime() << endl;
|
||||
}
|
||||
|
||||
// Randomize H RHS
|
||||
//trueRhs = 1.0;
|
||||
/*
|
||||
for (int i=block_trueOffsets[1]; i<block_trueOffsets[2]; ++i)
|
||||
trueRhs[i] = i % 53;
|
||||
*/
|
||||
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
pcg.SetPreconditioner(*precMG);
|
||||
// // pcg.SetPreconditioner(*precAS);
|
||||
pcg.Mult(trueRhs, trueX);
|
||||
chrono.Stop();
|
||||
delete precMG;
|
||||
// // delete precAS;
|
||||
|
||||
// // trueX = 0.0;
|
||||
// // invA->Mult(trueRhs,trueX);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "MG Solution time time: " << chrono.RealTime() << endl;
|
||||
}
|
||||
// // cin.get();
|
||||
// // if(myid == 0)
|
||||
// // cout << "MG prec Solution time: " << chrono.RealTime() << endl;
|
||||
|
||||
|
||||
// // chrono.Clear();
|
||||
// // chrono.Start();
|
||||
// // Block_AMSSolver * precAMS = new Block_AMSSolver(block_trueOffsets,fespaces);
|
||||
// // precAMS->SetSmootherType(Block_AMS::BlkSmootherType::SCHWARZ);
|
||||
// // precAMS->SetSmootherType(Block_AMS::BlkSmootherType::HYPRE);
|
||||
// // precAMS->SetOperator(LS_Maxwellop);
|
||||
// // precAMS->SetTheta(1.0/5.0);
|
||||
// // // 0-Smoother, 1-Grad, 2,3,4-Pix,Piy,Piz
|
||||
// // precAMS->SetCycleType("023414320");
|
||||
// // precAMS->SetNumberofCycles(1);
|
||||
// // chrono.Stop();
|
||||
// // if(myid == 0)
|
||||
// // cout << "BlkAMS Setup time: " << chrono.RealTime() << endl;
|
||||
|
||||
// // // resolve with block AMS
|
||||
// // trueX = 0;
|
||||
// // chrono.Clear();
|
||||
// // chrono.Start();
|
||||
// // pcg.SetPreconditioner(*precAMS);
|
||||
// // pcg.Mult(trueRhs, trueX);
|
||||
// // chrono.Stop();
|
||||
// // delete precAMS;
|
||||
|
||||
// // if(myid == 0)
|
||||
// // cout << "BlockAMS Solution time: " << chrono.RealTime() << endl;
|
||||
|
||||
|
||||
a_EE->RecoverFEMSolution(trueX.GetBlock(0), *b_E, *E_gf);
|
||||
a_HH->RecoverFEMSolution(trueX.GetBlock(1), *b_H, *H_gf);
|
||||
|
||||
|
||||
int order_quad = max(2, 2*order+1);
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
double Error_E = E_gf->ComputeL2Error(Eex, irs);
|
||||
double norm_E = ComputeGlobalLpNorm(2, Eex, *pmesh, irs);
|
||||
|
||||
double Error_H = H_gf->ComputeL2Error(Hex, irs);
|
||||
double norm_H = ComputeGlobalLpNorm(2, Hex , *pmesh, irs);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "|| E_h - E || = " << Error_E << "\n";
|
||||
cout << "|| E_h - E ||/||E|| = " << Error_E/norm_E << "\n";
|
||||
cout << "|| H_h - H || = " << Error_H << "\n";
|
||||
cout << "|| H_h - H ||/||H|| = " << Error_H/norm_H << "\n";
|
||||
cout << "Total error = " << setprecision(15) << sqrt(Error_H*Error_H
|
||||
+Error_E*Error_E) << "\n";
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
E_gf->Save(sol_ofs);
|
||||
}
|
||||
|
||||
//ParGridFunction ExactE(fespace);
|
||||
/*
|
||||
if (visualization)
|
||||
{
|
||||
// 8. Connect to GLVis.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
// socketstream E_sock(vishost, visport);
|
||||
// E_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
// E_sock.precision(8);
|
||||
// E_sock << "solution\n" << *pmesh << *E_gf << "window_title 'Electric field'" << endl;
|
||||
socketstream Exact_sock(vishost, visport);
|
||||
Exact_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
Exact_sock.precision(8);
|
||||
Exact_sock << "solution\n" << *pmesh << *Exact_gf << "window_title 'Electric field'" << endl;
|
||||
|
||||
// MPI_Barrier(pmesh->GetComm());
|
||||
// socketstream Eex_sock(vishost, visport);
|
||||
// Eex_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
// Eex_sock.precision(8);
|
||||
// Eex_sock << "solution\n" << *pmesh << *Exact_gf << "window_title 'Exact Electric field'" << endl;
|
||||
}
|
||||
*/
|
||||
// delete A_EE;
|
||||
// delete A_HE;
|
||||
// delete A_EH;
|
||||
// delete A_HH;
|
||||
// delete LS_Maxwellop;
|
||||
// delete a_EE;
|
||||
// delete a_HE;
|
||||
// delete a_HH;
|
||||
// delete b_E;
|
||||
// delete b_H;
|
||||
delete E_gf;
|
||||
delete Exact_gf;
|
||||
for (auto p: ParMeshes) { delete p; }
|
||||
for (auto p: fespaces) { delete p; }
|
||||
for (auto p: P) { delete p; }
|
||||
ParMeshes.clear();
|
||||
fespaces.clear();
|
||||
P.clear();
|
||||
delete fec;
|
||||
delete fespace;
|
||||
delete pmesh;
|
||||
|
||||
// cout << "Freed memory: " << endl;
|
||||
// cin.get();
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//define exact solution
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
double curlE[3], curl2E[3];
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
}
|
||||
|
||||
void H_exact(const Vector &x, Vector &H)
|
||||
{
|
||||
double E[3], curlE[3], curl2E[3];
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
for (int i = 0; i<3; i++) { H(i) = curlE[i]/omega; }
|
||||
}
|
||||
|
||||
|
||||
void f_exact_H(const Vector &x, Vector &f)
|
||||
{
|
||||
// curl H - omega E = f
|
||||
// = curl (curl E / omega) - omega E
|
||||
f = 0.0;
|
||||
if (sol !=4)
|
||||
{
|
||||
double E[3], curlE[3], curl2E[3];
|
||||
get_maxwell_solution(x, E, curlE, curl2E);
|
||||
f(0) = curl2E[0] / omega - omega * E[0];
|
||||
f(1) = curl2E[1] / omega - omega * E[1];
|
||||
f(2) = curl2E[2] / omega - omega * E[2];
|
||||
}
|
||||
}
|
||||
|
||||
void get_maxwell_solution(const Vector &X, double E[], double curlE[],
|
||||
double curl2E[])
|
||||
{
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
double z = X[2];
|
||||
|
||||
|
||||
|
||||
if (sol ==-1)
|
||||
{
|
||||
E[0] = y * z * (1.0 - y) * (1.0 - z);
|
||||
E[1] = x * y * z * (1.0 - x) * (1.0 - z);
|
||||
E[2] = x * y * (1.0 - x) * (1.0 - y);
|
||||
|
||||
curlE[0] = -(x-1.0) * x * (y*(2.0*z-3.0)+1.0);
|
||||
curlE[1] = -2.0*(y-1.0)*y*(x-z);
|
||||
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
|
||||
|
||||
curl2E[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
|
||||
curl2E[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
|
||||
curl2E[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
|
||||
}
|
||||
else if (sol == 0) // polynomial
|
||||
{
|
||||
// Polynomial vanishing on the boundary
|
||||
E[0] = y * z * (1.0 - y) * (1.0 - z);
|
||||
E[1] = (1.0 - x) * x * y * (1.0 - z) * z;
|
||||
E[2] = (1.0 - x) * x * (1.0 - y) * y;
|
||||
//
|
||||
curlE[0] = -(-1.0 + x) * x * (1.0 + y * (-3.0 + 2.0 * z));
|
||||
curlE[1] = -2.0 * (-1.0 + y) * y * (x - z);
|
||||
curlE[2] = (1.0 + (-3.0 + 2.0 * x) * y) * (-1.0 + z) * z;
|
||||
|
||||
curl2E[0] = -2.0 * (-1.0 + y) * y + (-3.0 + 2.0 * x) * (-1.0 + z) * z;
|
||||
curl2E[1] = -2.0 * y * (-x + x * x + (-1.0 + z) * z);
|
||||
curl2E[2] = -2.0 * (-1.0 + y) * y + (-1.0 + x) * x * (-3.0 + 2.0 * z);
|
||||
}
|
||||
else if (sol == 1) // sinusoidal
|
||||
{
|
||||
E[0] = sin(omega * y);
|
||||
E[1] = sin(omega * z);
|
||||
E[2] = sin(omega * x);
|
||||
|
||||
curlE[0] = -omega * cos(omega * z);
|
||||
curlE[1] = -omega * cos(omega * x);
|
||||
curlE[2] = -omega * cos(omega * y);
|
||||
|
||||
curl2E[0] = omega * omega * E[0];
|
||||
curl2E[1] = omega * omega * E[1];
|
||||
curl2E[2] = omega * omega * E[2];
|
||||
}
|
||||
else if (sol == 2) // point source
|
||||
{
|
||||
// shift to avoid singularity
|
||||
double x0 = x + 0.1;
|
||||
double x1 = y + 0.1;
|
||||
double x2 = z + 0.1;
|
||||
//
|
||||
double r = sqrt(x0 * x0 + x1 * x1 + x2 * x2);
|
||||
|
||||
E[0] = cos(omega * r);
|
||||
E[1] = 0.0;
|
||||
E[2] = 0.0;
|
||||
|
||||
double r_x = x0 / r;
|
||||
double r_y = x1 / r;
|
||||
double r_z = x2 / r;
|
||||
double r_xy = -(r_x / r) * r_y;
|
||||
double r_xz = -(r_x / r) * r_z;
|
||||
double r_yx = r_xy;
|
||||
double r_yy = (1.0 / r) * (1.0 - r_y * r_y);
|
||||
double r_zx = r_xz;
|
||||
double r_zz = (1.0 / r) * (1.0 - r_z * r_z);
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = -omega * r_z * sin(omega * r);
|
||||
curlE[2] = omega * r_y * sin(omega * r);
|
||||
|
||||
curl2E[0] = omega * ((r_yy + r_zz) * sin(omega * r) +
|
||||
(omega * r_y * r_y + omega * r_z * r_z) * cos(omega * r));
|
||||
curl2E[1] = -omega * (r_yx * sin(omega * r) + omega * r_y * r_x * cos(
|
||||
omega * r));
|
||||
curl2E[2] = -omega * (r_zx * sin(omega * r) + omega * r_z * r_x * cos(
|
||||
omega * r));
|
||||
}
|
||||
else if (sol == 3) // plane wave
|
||||
{
|
||||
double coeff = omega / sqrt(3.0);
|
||||
E[0] = cos(coeff * (x + y + z));
|
||||
E[1] = 0.0;
|
||||
E[2] = 0.0;
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = -coeff * sin(coeff * (x + y + z));
|
||||
curlE[2] = coeff * sin(coeff * (x + y + z));
|
||||
|
||||
curl2E[0] = 2.0 * coeff * coeff * E[0];
|
||||
curl2E[1] = -coeff * coeff * E[0];
|
||||
curl2E[2] = -coeff * coeff * E[0];
|
||||
}
|
||||
else if (sol == -1)
|
||||
{
|
||||
E[0] = cos(omega * y);
|
||||
E[1] = 0.0;
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = 0.0;
|
||||
curlE[2] = -omega * sin(omega * y);
|
||||
|
||||
curl2E[0] = omega*omega * cos(omega*y);
|
||||
curl2E[1] = 0.0;
|
||||
curl2E[2] = 0.0;
|
||||
}
|
||||
else if (sol == 4) // Airy function
|
||||
{
|
||||
E[0] = 0;
|
||||
E[1] = 0;
|
||||
// double b = -pow(omega/4.0,2.0/3.0)*(4.0*x(0)-1.0);
|
||||
double b = -pow(omega/4.0,2.0/3.0)*(4.0*x-1.0);
|
||||
//E[2] = boost::math::airy_ai(b);
|
||||
E[2] = gsl_sf_airy_Ai(b, GSL_PREC_DOUBLE);
|
||||
|
||||
curlE[0] = 0.0;
|
||||
curlE[1] = 4.0 * pow(omega/4.0,2.0/3.0) * gsl_sf_airy_Ai_deriv(b,
|
||||
GSL_PREC_DOUBLE);
|
||||
curlE[2] = 0.0;
|
||||
|
||||
// not used
|
||||
curl2E[0] = 0.0;
|
||||
curl2E[1] = 0.0;
|
||||
curl2E[2] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void epsilon_func(const Vector &x, DenseMatrix &M)
|
||||
{
|
||||
M.SetSize(3);
|
||||
|
||||
M = 0.0;
|
||||
M(0,0) = 1.0;
|
||||
M(1,1) = 1.0;
|
||||
if (sol != 4)
|
||||
{
|
||||
M(2,2) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
M(2,2) = 4.0*x(0)-1.0;
|
||||
// M(2,2) = 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
void epsilon2_func(const Vector &x, DenseMatrix &M)
|
||||
{
|
||||
M.SetSize(3);
|
||||
|
||||
M = 0.0;
|
||||
M(0,0) = 1.0;
|
||||
M(1,1) = 1.0;
|
||||
if (sol != 4)
|
||||
{
|
||||
M(2,2) = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
M(2,2) = (4.0*x(0)-1.0) * (4.0*x(0)-1.0);
|
||||
// M(2,2) = 4.0;
|
||||
}
|
||||
}
|
||||
@@ -1,915 +0,0 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic diffusion
|
||||
// problem corresponding to the second order definite Maxwell
|
||||
// equation curl curl E + E = f with boundary condition
|
||||
// E x n = <given tangential field>. Here, we use a given exact
|
||||
// solution E and compute the corresponding r.h.s. f.
|
||||
// We discretize with Nedelec finite elements in 2D or 3D.
|
||||
//
|
||||
// The example demonstrates the use of H(curl) finite element
|
||||
// spaces with the curl-curl and the (vector finite element) mass
|
||||
// bilinear form, as well as the computation of discretization
|
||||
// error when the exact solution is known. Static condensation is
|
||||
// also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 1-2 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
void E_exact(const Vector &, Vector &);
|
||||
void f_exact(const Vector &, Vector &);
|
||||
double freq = 1.0, kappa;
|
||||
int dim;
|
||||
|
||||
#define SIGMAVAL -250.0
|
||||
//#define FORM_DEFINITE
|
||||
//#define SOLVE_A2
|
||||
//#define ITER_A2
|
||||
|
||||
//#define USE_CSL
|
||||
|
||||
//#define USE_HELMHOLTZ
|
||||
|
||||
//#define TEST_MULTIPLE_SP
|
||||
|
||||
#ifdef USE_HELMHOLTZ
|
||||
void GetHelmholtzMatrix(ParMesh *pmesh, const int dir, HypreParMatrix *A)
|
||||
{
|
||||
const int order = 1;
|
||||
FiniteElementCollection *fec;
|
||||
fec = new H1_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
|
||||
const bool homogeneousBCeverywhere = false;
|
||||
if (homogeneousBCeverywhere)
|
||||
{
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set boundary conditions, depending on dir.
|
||||
MFEM_VERIFY(dim == 3, "");
|
||||
for (int i=0; i<pmesh->GetNBE(); ++i)
|
||||
{
|
||||
Element *elem = pmesh->GetBdrElement(i);
|
||||
MFEM_VERIFY(elem->GetNVertices() >= 3, "");
|
||||
const int *vertices = elem->GetVertices();
|
||||
double *v[3];
|
||||
for (int j=0; j<3; ++j)
|
||||
{
|
||||
v[j] = pmesh->GetVertex(vertices[j]);
|
||||
}
|
||||
|
||||
double u[3];
|
||||
double w[3];
|
||||
for (int j=0; j<3; ++j)
|
||||
{
|
||||
u[j] = v[1][j] - v[0][j]; // An edge tangent
|
||||
w[j] = v[2][j] - v[1][j]; // Another edge tangent, not parallel to u.
|
||||
}
|
||||
|
||||
double n[3]; // normal vector, taken as the cross product u x v
|
||||
n[0] = (u[1]*w[2]) - (u[2]*w[1]);
|
||||
n[1] = (u[2]*w[0]) - (u[0]*w[2]);
|
||||
n[2] = (u[0]*w[1]) - (u[1]*w[0]);
|
||||
|
||||
double t = sqrt((n[0]*n[0]) + (n[1]*n[1]) + (n[2]*n[2]));
|
||||
|
||||
int d = -1;
|
||||
for (int j=0; j<3; ++j)
|
||||
{
|
||||
n[j] /= t; // normalize
|
||||
if (fabs(fabs(n[j]) - 1.0) < 1.0e-8)
|
||||
{
|
||||
d = j;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(d >= 0, "");
|
||||
|
||||
if (d != dir) // face has essential BC at all DOF's.
|
||||
{
|
||||
elem->SetAttribute(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem->SetAttribute(0);
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> ess_bdr(2);
|
||||
ess_bdr = 0;
|
||||
ess_bdr[1] = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient neg(SIGMAVAL);
|
||||
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddDomainIntegrator(new MassIntegrator(neg));
|
||||
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
ConstantCoefficient zero(0.0);
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(zero));
|
||||
b->Assemble();
|
||||
|
||||
bool static_cond = false;
|
||||
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, *A, X, B);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
int order = 2;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
bool use_strumpack = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
args.AddOption(&use_strumpack, "-strumpack", "--strumpack-solver",
|
||||
"-no-strumpack", "--no-strumpack-solver",
|
||||
"Use STRUMPACK's double complex linear solver.");
|
||||
#endif
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
//(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
{
|
||||
double minsize = pmesh->GetElementSize(0);
|
||||
double maxsize = minsize;
|
||||
for (int i=1; i<pmesh->GetNE(); ++i)
|
||||
{
|
||||
const double size_i = pmesh->GetElementSize(i);
|
||||
minsize = std::min(minsize, size_i);
|
||||
maxsize = std::max(maxsize, size_i);
|
||||
}
|
||||
|
||||
cout << myid << ": Element size range: (" << minsize << ", " << maxsize << ")"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
long globalNE = pmesh->GetGlobalNE();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of mesh elements: " << globalNE << endl;
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
cout << "Root local number of finite element unknowns: " << fespace->TrueVSize()
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
VectorFunctionCoefficient f(sdim, f_exact);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
ParGridFunction x(fespace);
|
||||
VectorFunctionCoefficient E(sdim, E_exact);
|
||||
x.ProjectCoefficient(E);
|
||||
|
||||
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
Coefficient *sigma = new ConstantCoefficient(SIGMAVAL);
|
||||
Coefficient *sigmaAbs = new ConstantCoefficient(fabs(SIGMAVAL));
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
|
||||
|
||||
//cout << myid << ": NBE " << pmesh->GetNBE() << endl;
|
||||
|
||||
#ifdef FORM_DEFINITE
|
||||
ParBilinearForm *adef = new ParBilinearForm(fespace);
|
||||
adef->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
adef->AddDomainIntegrator(new VectorFEMassIntegrator(*sigmaAbs));
|
||||
|
||||
if (static_cond) { adef->EnableStaticCondensation(); }
|
||||
adef->Assemble();
|
||||
|
||||
HypreParMatrix Adef;
|
||||
Vector Bdef, Xdef;
|
||||
adef->FormLinearSystem(ess_tdof_list, x, *b, Adef, Xdef, Bdef);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CSL
|
||||
Vector Bdef, Xdef;
|
||||
|
||||
ParBilinearForm *Mform = new ParBilinearForm(fespace);
|
||||
Mform->AddDomainIntegrator(new VectorFEMassIntegrator(*sigmaAbs));
|
||||
Mform->Assemble();
|
||||
|
||||
// Mform->Finalize();
|
||||
|
||||
HypreParMatrix Mmat, Smat, Mcopy;
|
||||
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mmat, Xdef, Bdef);
|
||||
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mcopy, Xdef,
|
||||
Bdef); // There must be a better way than creating two identical matrices.
|
||||
|
||||
ParBilinearForm *Sform = new ParBilinearForm(fespace);
|
||||
Sform->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
Sform->Assemble();
|
||||
|
||||
Sform->FormLinearSystem(ess_tdof_list, x, *b, Smat, Xdef, Bdef);
|
||||
|
||||
ParBilinearForm *agrad = new ParBilinearForm(fespace);
|
||||
//agrad->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
agrad->AddDomainIntegrator(new VectorFEMassIntegrator(*muinv));
|
||||
|
||||
if (static_cond) { agrad->EnableStaticCondensation(); }
|
||||
agrad->Assemble();
|
||||
HypreParMatrix Agrad;
|
||||
agrad->FormLinearSystem(ess_tdof_list, x, *b, Agrad, Xdef, Bdef);
|
||||
#endif
|
||||
|
||||
#ifdef ITER_A2
|
||||
Vector Bdef, Xdef;
|
||||
|
||||
ParBilinearForm *Mform = new ParBilinearForm(fespace);
|
||||
Mform->AddDomainIntegrator(new VectorFEMassIntegrator(*sigmaAbs));
|
||||
Mform->Assemble();
|
||||
|
||||
Mform->Finalize();
|
||||
|
||||
HypreParMatrix Mmat, Mcopy;
|
||||
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mmat, Xdef, Bdef);
|
||||
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mcopy, Xdef,
|
||||
Bdef); // There must be a better way to implement M^2.
|
||||
|
||||
/*
|
||||
HypreParMatrix *Mmat = Mform->ParallelAssemble();
|
||||
HypreParMatrix *Mcopy = Mform->ParallelAssemble(); // There must be a better way to implement M^2.
|
||||
*/
|
||||
|
||||
ParBilinearForm *Sform = new ParBilinearForm(fespace);
|
||||
Sform->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
Sform->Assemble();
|
||||
|
||||
HypreParMatrix Smat, Scopy;
|
||||
Sform->FormLinearSystem(ess_tdof_list, x, *b, Smat, Xdef, Bdef);
|
||||
Sform->FormLinearSystem(ess_tdof_list, x, *b, Scopy, Xdef, Bdef);
|
||||
#endif
|
||||
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
#ifdef SOLVE_A2
|
||||
HypreParMatrix Acopy;
|
||||
{
|
||||
Vector Bdum, Xdum;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, Acopy, Xdum, Bdum);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
StopWatch chrono;
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
|
||||
//A.Print("maxwell1000_2");
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (use_strumpack)
|
||||
{
|
||||
const bool fullDirect = true;
|
||||
|
||||
#ifdef USE_CSL
|
||||
const double beta1 = 1.0;
|
||||
const double beta2 = 0.5;
|
||||
|
||||
Mmat *= -beta1;
|
||||
|
||||
// HypreParMatrix *cslRe = Add(1.0, Smat, -beta1, Mmat);
|
||||
HypreParMatrix * cslRe = ParAdd(&Smat, &Mmat);
|
||||
|
||||
Mcopy *= beta2;
|
||||
|
||||
//ComplexHypreParMatrix chpm(cslRe, &Mcopy, false, false);
|
||||
ComplexHypreParMatrix chpm(&A, &Mcopy, false,
|
||||
false); // For the case beta1 = 1.
|
||||
|
||||
HypreParMatrix *cSysMat = chpm.GetSystemMatrix();
|
||||
|
||||
Array<int> block_offsets(3); // number of variables + 1
|
||||
block_offsets[0] = 0;
|
||||
block_offsets[1] = fespace->GetVSize();
|
||||
block_offsets[2] = fespace->GetVSize();
|
||||
block_offsets.PartialSum();
|
||||
|
||||
Array<int> block_trueOffsets(3); // number of variables + 1
|
||||
block_trueOffsets[0] = 0;
|
||||
block_trueOffsets[1] = fespace->TrueVSize();
|
||||
block_trueOffsets[2] = fespace->TrueVSize();
|
||||
block_trueOffsets.PartialSum();
|
||||
|
||||
//cout << myid << ": V size " << fespace->GetVSize() << ", true " << fespace->TrueVSize() << ", global true " << size << ", B size "
|
||||
//<< B.Size() << ", X size " << X.Size() << endl;
|
||||
|
||||
// Note that B is of true size.
|
||||
BlockVector trueY(block_trueOffsets), trueX(block_trueOffsets),
|
||||
trueRhs(block_trueOffsets);
|
||||
|
||||
trueRhs.GetBlock(0) = B;
|
||||
trueRhs.GetBlock(1) = 0.0;
|
||||
|
||||
Operator * Arow = new STRUMPACKRowLocMatrix(*cSysMat);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
|
||||
// strumpack->SetSymmetricPattern(true);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
#endif
|
||||
|
||||
if (fullDirect)
|
||||
{
|
||||
#ifdef USE_CSL
|
||||
//Solver * precond = strumpack;
|
||||
|
||||
// strumpack->Mult(B, X);
|
||||
|
||||
BlockOperator blockDiagA(block_trueOffsets);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
blockDiagA.SetDiagonalBlock(i, &A);
|
||||
}
|
||||
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
HypreSolver *amsgrad = new HypreAMS(Agrad, prec_fespace);
|
||||
|
||||
#ifdef HYPRE_DYLAN
|
||||
{
|
||||
Vector Xtmp(X);
|
||||
amsgrad->Mult(B,
|
||||
Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
|
||||
}
|
||||
|
||||
HypreAMSG *amsg = new HypreAMSG((HypreAMS*) amsgrad, argc, argv);
|
||||
BlockOperator blockDiagP(block_trueOffsets);
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
blockDiagP.SetDiagonalBlock(i, amsg);
|
||||
}
|
||||
|
||||
TripleProductOperator strumpackProj(&blockDiagP, strumpack, &blockDiagP, false,
|
||||
false, false);
|
||||
ProductOperator prod(&blockDiagA, &strumpackProj, false, false);
|
||||
#else
|
||||
ProductOperator prod(&blockDiagA, strumpack, false, false);
|
||||
#endif
|
||||
|
||||
GMRESSolver *gmres = new GMRESSolver(fespace->GetComm());
|
||||
//BiCGSTABSolver *gmres = new BiCGSTABSolver(fespace->GetComm());
|
||||
|
||||
gmres->SetOperator(prod);
|
||||
gmres->SetRelTol(1e-12);
|
||||
gmres->SetMaxIter(1000);
|
||||
gmres->SetPrintLevel(1);
|
||||
|
||||
gmres->Mult(trueRhs, trueY);
|
||||
strumpack->Mult(trueY, trueX);
|
||||
|
||||
X = trueX.GetBlock(0);
|
||||
double xim2 = trueX.GetBlock(1).Norml2();
|
||||
xim2 *= xim2;
|
||||
double sumxim2 = 0.0;
|
||||
|
||||
MPI_Allreduce(&xim2, &sumxim2, 1, MPI_DOUBLE, MPI_SUM, fespace->GetComm());
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << myid << ": norm of Xim " << trueX.GetBlock(1).Norml2() << ", global " <<
|
||||
sqrt(sumxim2) << endl;
|
||||
}
|
||||
|
||||
delete gmres;
|
||||
delete strumpack;
|
||||
delete Arow;
|
||||
#else
|
||||
cout << "Solving with STRUMPACK" << endl;
|
||||
|
||||
#ifdef TEST_MULTIPLE_SP
|
||||
const int Ns = 2;
|
||||
std::vector<Operator*> Arows(Ns);
|
||||
std::vector<STRUMPACKSolver*> strumpacks(Ns);
|
||||
|
||||
//Operator * Arow = new STRUMPACKRowLocMatrix(A);
|
||||
|
||||
for (int m=0; m<Ns; ++m)
|
||||
{
|
||||
Arows[m] = new STRUMPACKRowLocMatrix(A);
|
||||
|
||||
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpacks[m] = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpacks[m]->SetPrintFactorStatistics(true);
|
||||
strumpacks[m]->SetPrintSolveStatistics(false);
|
||||
strumpacks[m]->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpacks[m]->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
|
||||
// strumpack->SetSymmetricPattern(true);
|
||||
strumpacks[m]->SetOperator(*Arows[m]);
|
||||
strumpacks[m]->SetFromCommandLine();
|
||||
//Solver * precond = strumpack;
|
||||
|
||||
strumpacks[m]->Mult(B, X);
|
||||
|
||||
//delete strumpack;
|
||||
//delete Arow;
|
||||
}
|
||||
#else
|
||||
Operator * Arow = new STRUMPACKRowLocMatrix(A);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
|
||||
// strumpack->SetSymmetricPattern(true);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
//Solver * precond = strumpack;
|
||||
|
||||
cout << "Solving with strumpack one time" << endl;
|
||||
|
||||
strumpack->Mult(B, X);
|
||||
|
||||
delete strumpack;
|
||||
delete Arow;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
HypreSolver *ams = new HypreAMS(A, prec_fespace);
|
||||
|
||||
#ifdef HYPRE_DYLAN
|
||||
{
|
||||
Vector Xtmp(X);
|
||||
ams->Mult(B,
|
||||
Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
|
||||
}
|
||||
|
||||
HypreParMatrix H[3];
|
||||
#ifdef USE_HELMHOLTZ
|
||||
for (int i=0; i<3; ++i)
|
||||
{
|
||||
GetHelmholtzMatrix(pmesh, i, &(H[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CSL
|
||||
HypreIAMS *iams = new HypreIAMS(A, H, strumpack, &trueX, &trueY,
|
||||
(HypreAMS*) ams, argc, argv);
|
||||
#else
|
||||
HypreIAMS *iams = new HypreIAMS(A, H, strumpack, NULL, NULL, (HypreAMS*) ams,
|
||||
argc, argv);
|
||||
#endif
|
||||
|
||||
GMRESSolver *gmres = new GMRESSolver(fespace->GetComm());
|
||||
//FGMRESSolver *gmres = new FGMRESSolver(fespace->GetComm());
|
||||
//BiCGSTABSolver *gmres = new BiCGSTABSolver(fespace->GetComm());
|
||||
//MINRESSolver *gmres = new MINRESSolver(fespace->GetComm());
|
||||
|
||||
gmres->SetOperator(A);
|
||||
gmres->SetRelTol(1e-16);
|
||||
gmres->SetMaxIter(1000);
|
||||
gmres->SetPrintLevel(1);
|
||||
|
||||
#ifdef SOLVE_A2
|
||||
{
|
||||
StopWatch chronoA2;
|
||||
chronoA2.Clear();
|
||||
chronoA2.Start();
|
||||
|
||||
HypreParMatrix * A2 = ParMult(&A, &Acopy);
|
||||
|
||||
chronoA2.Stop();
|
||||
cout << "A2 setup time " << chronoA2.RealTime() << endl;
|
||||
|
||||
Vector AB(B);
|
||||
A.Mult(B, AB);
|
||||
gmres->SetOperator(*A2);
|
||||
|
||||
HypreSolver *ams2 = new HypreAMS(A, prec_fespace);
|
||||
{
|
||||
Vector Xtmp(X);
|
||||
ams2->Mult(B,
|
||||
Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
|
||||
}
|
||||
|
||||
#ifdef ITER_A2
|
||||
// Iteratively solve 0.5 (A^2 + S^2 + M^2) u^{k+1} = 0.5 (SM + MS) u^k + Ab
|
||||
|
||||
StopWatch chronoIterA2;
|
||||
chronoIterA2.Clear();
|
||||
chronoIterA2.Start();
|
||||
|
||||
HypreParMatrix * M2 = ParMult(&Mmat, &Mcopy);
|
||||
HypreParMatrix * S2 = ParMult(&Smat, &Scopy);
|
||||
|
||||
HypreParMatrix * MS = ParMult(&Mmat, &Scopy);
|
||||
HypreParMatrix * SM = ParMult(&Smat, &Mcopy);
|
||||
|
||||
HypreParMatrix * Bmat = ParAdd(SM, MS);
|
||||
(*Bmat) *= 0.5;
|
||||
|
||||
// TODO: there must be a better way to form a sum of three matrices. Of course, we could define an operator that does 3 mat-vecs.
|
||||
//HypreParMatrix * S2M2 = ParAdd(S2, M2);
|
||||
//HypreParMatrix * iterMat = ParAdd(A2, S2M2);
|
||||
HypreParMatrix * iterMat = ParAdd(A2, Bmat);
|
||||
|
||||
chronoIterA2.Stop();
|
||||
cout << "Iter A2 setup time " << chronoIterA2.RealTime() << endl;
|
||||
|
||||
/*
|
||||
HypreSolver *ams3 = new HypreAMS(*iterMat, prec_fespace);
|
||||
{
|
||||
Vector Xtmp(X);
|
||||
ams3->Mult(B, Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// GMRES
|
||||
gmres->SetOperator(*iterMat);
|
||||
gmres->SetPreconditioner(*ams2);
|
||||
*/
|
||||
|
||||
|
||||
//HypreBoomerAMG *amg = new HypreBoomerAMG(*iterMat);
|
||||
HypreBoomerAMG *amg = new HypreBoomerAMG(*A2);
|
||||
|
||||
// PCG
|
||||
HyprePCG *pcg = new HyprePCG(*iterMat);
|
||||
//HyprePCG *pcg = new HyprePCG(*A2);
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(10);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
|
||||
/*
|
||||
// Strumpack linear solver
|
||||
Operator * Arow = new STRUMPACKRowLocMatrix(*iterMat);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
*/
|
||||
|
||||
Vector iterRHS(AB);
|
||||
Vector iterU(AB);
|
||||
Vector iterU0(AB);
|
||||
|
||||
iterU = 0.0;
|
||||
iterU0 = 0.0;
|
||||
|
||||
bool iterate = true;
|
||||
int numIter = 0;
|
||||
while (iterate)
|
||||
{
|
||||
iterRHS = iterU;
|
||||
iterRHS.Add(-1.0, iterU0);
|
||||
|
||||
cout << "Iteration " << numIter + 1 << ": diff norm " << iterRHS.Norml2() <<
|
||||
endl;
|
||||
|
||||
iterU0 = iterU;
|
||||
|
||||
Bmat->Mult(iterU0, iterRHS);
|
||||
//iterRHS.Add(2.0, AB);
|
||||
iterRHS.Add(1.0, AB);
|
||||
|
||||
//gmres->Mult(iterRHS, iterU);
|
||||
pcg->Mult(iterRHS, iterU);
|
||||
//strumpack->Mult(iterRHS, iterU);
|
||||
|
||||
numIter++;
|
||||
|
||||
if (numIter > 100)
|
||||
{
|
||||
iterate = false;
|
||||
}
|
||||
}
|
||||
|
||||
//delete strumpack;
|
||||
//delete Arow;
|
||||
|
||||
delete pcg;
|
||||
|
||||
X = iterU;
|
||||
#else
|
||||
//HypreIAMS *iams2 = new HypreIAMS(*A2, (HypreAMS*) ams2, argc, argv);
|
||||
//gmres->SetPreconditioner(*iams2);
|
||||
cout << myid << ": Solving" << endl;
|
||||
gmres->SetPreconditioner(*ams2);
|
||||
gmres->Mult(AB, X);
|
||||
cout << myid << ": Solved" << endl;
|
||||
return 3;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
gmres->SetPreconditioner(*iams);
|
||||
gmres->Mult(B, X);
|
||||
#endif
|
||||
#else
|
||||
HypreGMRES *gmres = new HypreGMRES(A);
|
||||
gmres->SetTol(1e-12);
|
||||
gmres->SetMaxIter(100);
|
||||
gmres->SetPrintLevel(10);
|
||||
|
||||
#ifdef FORM_DEFINITE
|
||||
HypreSolver *amsdef = new HypreAMS(Adef, prec_fespace);
|
||||
gmres->SetPreconditioner(*amsdef);
|
||||
#else
|
||||
gmres->SetPreconditioner(*ams);
|
||||
#endif
|
||||
gmres->Mult(B, X);
|
||||
#endif
|
||||
|
||||
delete gmres;
|
||||
//delete iams;
|
||||
//delete ams;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
|
||||
// preconditioner from hypre.
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
HypreSolver *ams = new HypreAMS(A, prec_fespace);
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-12);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*ams);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
delete pcg;
|
||||
delete ams;
|
||||
}
|
||||
|
||||
chrono.Stop();
|
||||
cout << myid << ": Solver time " << chrono.RealTime() << endl;
|
||||
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(E);
|
||||
Vector zeroVec(3);
|
||||
zeroVec = 0.0;
|
||||
VectorConstantCoefficient vzero(zeroVec);
|
||||
ParGridFunction zerogf(fespace);
|
||||
zerogf = 0.0;
|
||||
double normE = zerogf.ComputeL2Error(E);
|
||||
double normX = x.ComputeL2Error(vzero);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "|| E_h - E ||_{L^2} = " << err << endl;
|
||||
cout << "|| E_h ||_{L^2} = " << normX << endl;
|
||||
cout << "|| E ||_{L^2} = " << normE << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete a;
|
||||
delete sigma;
|
||||
delete muinv;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(2));
|
||||
E(2) = sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { E(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
f(0) = (SIGMAVAL + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (SIGMAVAL + kappa * kappa) * sin(kappa * x(2));
|
||||
f(2) = (SIGMAVAL + kappa * kappa) * sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
+26
-60
@@ -6,7 +6,6 @@
|
||||
// ex4 -m ../data/star.mesh
|
||||
// ex4 -m ../data/beam-tet.mesh
|
||||
// ex4 -m ../data/beam-hex.mesh
|
||||
// ex4 -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// ex4 -m ../data/escher.mesh
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb
|
||||
// ex4 -m ../data/fichera-q2.vtk
|
||||
@@ -21,12 +20,6 @@
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/star-surf.mesh -o 1
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex4 -m ../data/star.mesh -pa -d cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-omp
|
||||
// ex4 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code solves a simple 2D/3D H(div) diffusion
|
||||
// problem corresponding to the second order definite equation
|
||||
// -grad(alpha div F) + beta F = f with boundary condition F dot n
|
||||
@@ -62,8 +55,6 @@ int main(int argc, char *argv[])
|
||||
bool set_bc = true;
|
||||
bool static_cond = false;
|
||||
bool hybridization = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -79,10 +70,6 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
|
||||
"--no-hybridization", "Enable hybridization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -95,19 +82,14 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume, as well as
|
||||
// periodic meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 3. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
// largest number that gives a final mesh with no more than 25,000
|
||||
// elements.
|
||||
@@ -120,14 +102,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use the
|
||||
// 4. Define a finite element space on the mesh. Here we use the
|
||||
// Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
cout << "Number of finite element unknowns: "
|
||||
<< fespace->GetTrueVSize() << endl;
|
||||
|
||||
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// In this example, the boundary conditions are defined by marking all
|
||||
// the boundary attributes from the mesh as essential (Dirichlet) and
|
||||
// converting them to a list of true dofs.
|
||||
@@ -139,7 +121,7 @@ int main(int argc, char *argv[])
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 7. Set up the linear form b(.) which corresponds to the right-hand side
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side
|
||||
// of the FEM linear system, which in this case is (f,phi_i) where f is
|
||||
// given by the function f_exact and phi_i are the basis functions in the
|
||||
// finite element fespace.
|
||||
@@ -148,7 +130,7 @@ int main(int argc, char *argv[])
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 8. Define the solution vector x as a finite element grid function
|
||||
// 7. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary faces will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
@@ -157,17 +139,16 @@ int main(int argc, char *argv[])
|
||||
VectorFunctionCoefficient F(sdim, F_exact);
|
||||
x.ProjectCoefficient(F);
|
||||
|
||||
// 9. Set up the bilinear form corresponding to the H(div) diffusion operator
|
||||
// 8. Set up the bilinear form corresponding to the H(div) diffusion operator
|
||||
// grad alpha div + beta I, by adding the div-div and the mass domain
|
||||
// integrators.
|
||||
Coefficient *alpha = new ConstantCoefficient(1.0);
|
||||
Coefficient *beta = new ConstantCoefficient(1.0);
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
// 10. Assemble the bilinear form and the corresponding linear system,
|
||||
// 9. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: eliminating boundary
|
||||
// conditions, applying conforming constraints for non-conforming AMR,
|
||||
// static condensation, hybridization, etc.
|
||||
@@ -186,47 +167,32 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
a->Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
SparseMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
cout << "Size of linear system: " << A.Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 10000, 1e-20, 0.0);
|
||||
// 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
|
||||
// solve the system A X = B with PCG.
|
||||
GSSmoother M(A);
|
||||
PCG(A, M, B, X, 1, 10000, 1e-20, 0.0);
|
||||
#else
|
||||
// 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);
|
||||
// 10. If compiled with SuiteSparse support, 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
|
||||
}
|
||||
else // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
if (UsesTensorBasis(*fespace))
|
||||
{
|
||||
OperatorJacobiSmoother M(*a, ess_tdof_list);
|
||||
PCG(*A, M, B, X, 1, 10000, 1e-20, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
CG(*A, B, X, 1, 10000, 1e-20, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// 12. Recover the solution as a finite element grid function.
|
||||
// 11. Recover the solution as a finite element grid function.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 13. Compute and print the L^2 norm of the error.
|
||||
// 12. Compute and print the L^2 norm of the error.
|
||||
cout << "\n|| F_h - F ||_{L^2} = " << x.ComputeL2Error(F) << '\n' << endl;
|
||||
|
||||
// 14. Save the refined mesh and the solution. This output can be viewed
|
||||
// 13. 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");
|
||||
@@ -237,7 +203,7 @@ int main(int argc, char *argv[])
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
// 14. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -247,7 +213,7 @@ int main(int argc, char *argv[])
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
}
|
||||
|
||||
// 16. Free the used memory.
|
||||
// 15. Free the used memory.
|
||||
delete hfes;
|
||||
delete hfec;
|
||||
delete a;
|
||||
@@ -269,7 +235,7 @@ void F_exact(const Vector &p, Vector &F)
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
F(0) = cos(kappa*x)*sin(kappa*y);
|
||||
F(1) = cos(kappa*y)*sin(kappa*x);
|
||||
@@ -286,7 +252,7 @@ void f_exact(const Vector &p, Vector &f)
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
double temp = 1 + 2*kappa*kappa;
|
||||
|
||||
|
||||
+29
-51
@@ -6,7 +6,6 @@
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
@@ -16,17 +15,10 @@
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-square.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code solves a simple 2D/3D H(div) diffusion
|
||||
// problem corresponding to the second order definite equation
|
||||
// -grad(alpha div F) + beta F = f with boundary condition F dot n
|
||||
@@ -68,8 +60,6 @@ int main(int argc, char *argv[])
|
||||
bool set_bc = true;
|
||||
bool static_cond = false;
|
||||
bool hybridization = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -85,10 +75,6 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
|
||||
"--no-hybridization", "Enable hybridization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -108,19 +94,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume, as well as periodic meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
@@ -133,7 +114,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
@@ -149,7 +130,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
@@ -159,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
@@ -171,7 +152,7 @@ int main(int argc, char *argv[])
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
@@ -180,7 +161,7 @@ int main(int argc, char *argv[])
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
b->Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// 9. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary faces will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
@@ -189,17 +170,16 @@ int main(int argc, char *argv[])
|
||||
VectorFunctionCoefficient F(sdim, F_exact);
|
||||
x.ProjectCoefficient(F);
|
||||
|
||||
// 11. Set up the parallel bilinear form corresponding to the H(div)
|
||||
// 10. Set up the parallel bilinear form corresponding to the H(div)
|
||||
// diffusion operator grad alpha div + beta I, by adding the div-div and
|
||||
// the mass domain integrators.
|
||||
Coefficient *alpha = new ConstantCoefficient(1.0);
|
||||
Coefficient *beta = new ConstantCoefficient(1.0);
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// 11. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation,
|
||||
@@ -219,43 +199,41 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
a->Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
if (myid == 0 && !pa)
|
||||
HYPRE_Int glob_size = A.GetGlobalNumRows();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: "
|
||||
<< A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
|
||||
cout << "Size of linear system: " << glob_size << endl;
|
||||
}
|
||||
|
||||
// 13. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
|
||||
// 12. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
|
||||
// the 3D ADS preconditioners from hypre. If using hybridization, the
|
||||
// system is preconditioned with hypre's BoomerAMG. In the partial
|
||||
// assembly case, use Jacobi preconditioning.
|
||||
Solver *prec = NULL;
|
||||
CGSolver *pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetOperator(*A);
|
||||
// system is preconditioned with hypre's BoomerAMG.
|
||||
HypreSolver *prec = NULL;
|
||||
CGSolver *pcg = new CGSolver(A.GetComm());
|
||||
pcg->SetOperator(A);
|
||||
pcg->SetRelTol(1e-12);
|
||||
pcg->SetMaxIter(2000);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(1);
|
||||
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
|
||||
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
if (hybridization) { prec = new HypreBoomerAMG(A); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
|
||||
if (dim == 2) { prec = new HypreAMS(*A.As<HypreParMatrix>(), prec_fespace); }
|
||||
else { prec = new HypreADS(*A.As<HypreParMatrix>(), prec_fespace); }
|
||||
if (dim == 2) { prec = new HypreAMS(A, prec_fespace); }
|
||||
else { prec = new HypreADS(A, prec_fespace); }
|
||||
}
|
||||
pcg->SetPreconditioner(*prec);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// 13. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 15. Compute and print the L^2 norm of the error.
|
||||
// 14. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
double err = x.ComputeL2Error(F);
|
||||
if (myid == 0)
|
||||
@@ -264,7 +242,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 16. Save the refined mesh and the solution in parallel. This output can
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
@@ -280,7 +258,7 @@ int main(int argc, char *argv[])
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 17. Send the solution by socket to a GLVis server.
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -291,7 +269,7 @@ int main(int argc, char *argv[])
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 18. Free the used memory.
|
||||
// 17. Free the used memory.
|
||||
delete pcg;
|
||||
delete prec;
|
||||
delete hfes;
|
||||
@@ -317,7 +295,7 @@ void F_exact(const Vector &p, Vector &F)
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
F(0) = cos(kappa*x)*sin(kappa*y);
|
||||
F(1) = cos(kappa*y)*sin(kappa*x);
|
||||
@@ -334,7 +312,7 @@ void f_exact(const Vector &p, Vector &f)
|
||||
|
||||
double x = p(0);
|
||||
double y = p(1);
|
||||
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
|
||||
// double z = (dim == 3) ? p(2) : 0.0;
|
||||
|
||||
double temp = 1 + 2*kappa*kappa;
|
||||
|
||||
|
||||
+40
-111
@@ -4,19 +4,11 @@
|
||||
//
|
||||
// Sample runs: ex5 -m ../data/square-disc.mesh
|
||||
// ex5 -m ../data/star.mesh
|
||||
// ex5 -m ../data/star.mesh -pa
|
||||
// ex5 -m ../data/beam-tet.mesh
|
||||
// ex5 -m ../data/beam-hex.mesh
|
||||
// ex5 -m ../data/beam-hex.mesh -pa
|
||||
// ex5 -m ../data/escher.mesh
|
||||
// ex5 -m ../data/fichera.mesh
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex5 -m ../data/star.mesh -pa -d cuda
|
||||
// ex5 -m ../data/star.mesh -pa -d raja-cuda
|
||||
// ex5 -m ../data/star.mesh -pa -d raja-omp
|
||||
// ex5 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code solves a simple 2D/3D mixed Darcy problem
|
||||
// corresponding to the saddle point system
|
||||
// k*u + grad p = f
|
||||
@@ -55,8 +47,6 @@ int main(int argc, char *argv[])
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -64,10 +54,6 @@ int main(int argc, char *argv[])
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -79,18 +65,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 3. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
// largest number that gives a final mesh with no more than 10,000
|
||||
// elements.
|
||||
@@ -103,7 +84,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use the
|
||||
// 4. Define a finite element space on the mesh. Here we use the
|
||||
// Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *hdiv_coll(new RT_FECollection(order, dim));
|
||||
FiniteElementCollection *l2_coll(new L2_FECollection(order, dim));
|
||||
@@ -111,7 +92,7 @@ int main(int argc, char *argv[])
|
||||
FiniteElementSpace *R_space = new FiniteElementSpace(mesh, hdiv_coll);
|
||||
FiniteElementSpace *W_space = new FiniteElementSpace(mesh, l2_coll);
|
||||
|
||||
// 6. Define the BlockStructure of the problem, i.e. define the array of
|
||||
// 5. Define the BlockStructure of the problem, i.e. define the array of
|
||||
// offsets for each variable. The last component of the Array is the sum
|
||||
// of the dimensions of each block.
|
||||
Array<int> block_offsets(3); // number of variables + 1
|
||||
@@ -126,7 +107,7 @@ int main(int argc, char *argv[])
|
||||
std::cout << "dim(R+W) = " << block_offsets.Last() << "\n";
|
||||
std::cout << "***********************************************************\n";
|
||||
|
||||
// 7. Define the coefficients, analytical solution, and rhs of the PDE.
|
||||
// 6. Define the coefficients, analytical solution, and rhs of the PDE.
|
||||
ConstantCoefficient k(1.0);
|
||||
|
||||
VectorFunctionCoefficient fcoeff(dim, fFun);
|
||||
@@ -136,28 +117,25 @@ int main(int argc, char *argv[])
|
||||
VectorFunctionCoefficient ucoeff(dim, uFun_ex);
|
||||
FunctionCoefficient pcoeff(pFun_ex);
|
||||
|
||||
// 8. Allocate memory (x, rhs) for the analytical solution and the right hand
|
||||
// 7. Allocate memory (x, rhs) for the analytical solution and the right hand
|
||||
// side. Define the GridFunction u,p for the finite element solution and
|
||||
// linear forms fform and gform for the right hand side. The data
|
||||
// allocated by x and rhs are passed as a reference to the grid functions
|
||||
// (u,p) and the linear forms (fform, gform).
|
||||
MemoryType mt = device.GetMemoryType();
|
||||
BlockVector x(block_offsets, mt), rhs(block_offsets, mt);
|
||||
BlockVector x(block_offsets), rhs(block_offsets);
|
||||
|
||||
LinearForm *fform(new LinearForm);
|
||||
fform->Update(R_space, rhs.GetBlock(0), 0);
|
||||
fform->AddDomainIntegrator(new VectorFEDomainLFIntegrator(fcoeff));
|
||||
fform->AddBoundaryIntegrator(new VectorFEBoundaryFluxLFIntegrator(fnatcoeff));
|
||||
fform->Assemble();
|
||||
fform->SyncAliasMemory(rhs);
|
||||
|
||||
LinearForm *gform(new LinearForm);
|
||||
gform->Update(W_space, rhs.GetBlock(1), 0);
|
||||
gform->AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
|
||||
gform->Assemble();
|
||||
gform->SyncAliasMemory(rhs);
|
||||
|
||||
// 9. Assemble the finite element matrices for the Darcy operator
|
||||
// 8. Assemble the finite element matrices for the Darcy operator
|
||||
//
|
||||
// D = [ M B^T ]
|
||||
// [ B 0 ]
|
||||
@@ -168,103 +146,55 @@ int main(int argc, char *argv[])
|
||||
BilinearForm *mVarf(new BilinearForm(R_space));
|
||||
MixedBilinearForm *bVarf(new MixedBilinearForm(R_space, W_space));
|
||||
|
||||
if (pa) { mVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
mVarf->AddDomainIntegrator(new VectorFEMassIntegrator(k));
|
||||
mVarf->Assemble();
|
||||
if (!pa) { mVarf->Finalize(); }
|
||||
mVarf->Finalize();
|
||||
SparseMatrix &M(mVarf->SpMat());
|
||||
|
||||
if (pa) { bVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
bVarf->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
||||
bVarf->Assemble();
|
||||
if (!pa) { bVarf->Finalize(); }
|
||||
bVarf->Finalize();
|
||||
SparseMatrix & B(bVarf->SpMat());
|
||||
B *= -1.;
|
||||
SparseMatrix *BT = Transpose(B);
|
||||
|
||||
BlockOperator darcyOp(block_offsets);
|
||||
BlockMatrix darcyMatrix(block_offsets);
|
||||
darcyMatrix.SetBlock(0,0, &M);
|
||||
darcyMatrix.SetBlock(0,1, BT);
|
||||
darcyMatrix.SetBlock(1,0, &B);
|
||||
|
||||
TransposeOperator *Bt = NULL;
|
||||
|
||||
if (pa)
|
||||
{
|
||||
Bt = new TransposeOperator(bVarf);
|
||||
|
||||
darcyOp.SetBlock(0,0, mVarf);
|
||||
darcyOp.SetBlock(0,1, Bt, -1.0);
|
||||
darcyOp.SetBlock(1,0, bVarf, -1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SparseMatrix &M(mVarf->SpMat());
|
||||
SparseMatrix &B(bVarf->SpMat());
|
||||
B *= -1.;
|
||||
Bt = new TransposeOperator(&B);
|
||||
|
||||
darcyOp.SetBlock(0,0, &M);
|
||||
darcyOp.SetBlock(0,1, Bt);
|
||||
darcyOp.SetBlock(1,0, &B);
|
||||
}
|
||||
|
||||
// 10. Construct the operators for preconditioner
|
||||
// 9. Construct the operators for preconditioner
|
||||
//
|
||||
// P = [ diag(M) 0 ]
|
||||
// [ 0 B diag(M)^-1 B^T ]
|
||||
//
|
||||
// Here we use Symmetric Gauss-Seidel to approximate the inverse of the
|
||||
// pressure Schur Complement
|
||||
SparseMatrix *MinvBt = NULL;
|
||||
Vector Md(mVarf->Height());
|
||||
SparseMatrix *MinvBt = Transpose(B);
|
||||
Vector Md(M.Height());
|
||||
M.GetDiag(Md);
|
||||
for (int i = 0; i < Md.Size(); i++)
|
||||
{
|
||||
MinvBt->ScaleRow(i, 1./Md(i));
|
||||
}
|
||||
SparseMatrix *S = Mult(B, *MinvBt);
|
||||
|
||||
BlockDiagonalPreconditioner darcyPrec(block_offsets);
|
||||
Solver *invM, *invS;
|
||||
SparseMatrix *S = NULL;
|
||||
|
||||
if (pa)
|
||||
{
|
||||
mVarf->AssembleDiagonal(Md);
|
||||
auto Md_host = Md.HostRead();
|
||||
Vector invMd(mVarf->Height());
|
||||
for (int i=0; i<mVarf->Height(); ++i)
|
||||
{
|
||||
invMd(i) = 1.0 / Md_host[i];
|
||||
}
|
||||
|
||||
Vector BMBt_diag(bVarf->Height());
|
||||
bVarf->AssembleDiagonal_ADAt(invMd, BMBt_diag);
|
||||
|
||||
Array<int> ess_tdof_list; // empty
|
||||
|
||||
invM = new OperatorJacobiSmoother(Md, ess_tdof_list);
|
||||
invS = new OperatorJacobiSmoother(BMBt_diag, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
SparseMatrix &M(mVarf->SpMat());
|
||||
M.GetDiag(Md);
|
||||
|
||||
SparseMatrix &B(bVarf->SpMat());
|
||||
MinvBt = Transpose(B);
|
||||
|
||||
for (int i = 0; i < Md.Size(); i++)
|
||||
{
|
||||
MinvBt->ScaleRow(i, 1./Md(i));
|
||||
}
|
||||
|
||||
S = Mult(B, *MinvBt);
|
||||
|
||||
invM = new DSmoother(M);
|
||||
|
||||
invM = new DSmoother(M);
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
invS = new GSSmoother(*S);
|
||||
invS = new GSSmoother(*S);
|
||||
#else
|
||||
invS = new UMFPackSolver(*S);
|
||||
invS = new UMFPackSolver(*S);
|
||||
#endif
|
||||
}
|
||||
|
||||
invM->iterative_mode = false;
|
||||
invS->iterative_mode = false;
|
||||
|
||||
BlockDiagonalPreconditioner darcyPrec(block_offsets);
|
||||
darcyPrec.SetDiagonalBlock(0, invM);
|
||||
darcyPrec.SetDiagonalBlock(1, invS);
|
||||
|
||||
// 11. Solve the linear system with MINRES.
|
||||
// 10. Solve the linear system with MINRES.
|
||||
// Check the norm of the unpreconditioned residual.
|
||||
int maxIter(1000);
|
||||
double rtol(1.e-6);
|
||||
@@ -276,12 +206,11 @@ int main(int argc, char *argv[])
|
||||
solver.SetAbsTol(atol);
|
||||
solver.SetRelTol(rtol);
|
||||
solver.SetMaxIter(maxIter);
|
||||
solver.SetOperator(darcyOp);
|
||||
solver.SetOperator(darcyMatrix);
|
||||
solver.SetPreconditioner(darcyPrec);
|
||||
solver.SetPrintLevel(1);
|
||||
x = 0.0;
|
||||
solver.Mult(rhs, x);
|
||||
if (device.IsEnabled()) { x.HostRead(); }
|
||||
chrono.Stop();
|
||||
|
||||
if (solver.GetConverged())
|
||||
@@ -292,7 +221,7 @@ int main(int argc, char *argv[])
|
||||
<< " iterations. Residual norm is " << solver.GetFinalNorm() << ".\n";
|
||||
std::cout << "MINRES solver took " << chrono.RealTime() << "s. \n";
|
||||
|
||||
// 12. Create the grid functions u and p. Compute the L2 error norms.
|
||||
// 11. Create the grid functions u and p. Compute the L2 error norms.
|
||||
GridFunction u, p;
|
||||
u.MakeRef(R_space, x.GetBlock(0), 0);
|
||||
p.MakeRef(W_space, x.GetBlock(1), 0);
|
||||
@@ -312,7 +241,7 @@ int main(int argc, char *argv[])
|
||||
std::cout << "|| u_h - u_ex || / || u_ex || = " << err_u / norm_u << "\n";
|
||||
std::cout << "|| p_h - p_ex || / || p_ex || = " << err_p / norm_p << "\n";
|
||||
|
||||
// 13. Save the mesh and the solution. This output can be viewed later using
|
||||
// 12. Save the mesh and the solution. This output can be viewed later using
|
||||
// GLVis: "glvis -m ex5.mesh -g sol_u.gf" or "glvis -m ex5.mesh -g
|
||||
// sol_p.gf".
|
||||
{
|
||||
@@ -329,13 +258,13 @@ int main(int argc, char *argv[])
|
||||
p.Save(p_ofs);
|
||||
}
|
||||
|
||||
// 14. Save data in the VisIt format
|
||||
// 13. Save data in the VisIt format
|
||||
VisItDataCollection visit_dc("Example5", mesh);
|
||||
visit_dc.RegisterField("velocity", &u);
|
||||
visit_dc.RegisterField("pressure", &p);
|
||||
visit_dc.Save();
|
||||
|
||||
// 15. Save data in the ParaView format
|
||||
// 14. Save data in the ParaView format
|
||||
ParaViewDataCollection paraview_dc("Example5", mesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(order);
|
||||
@@ -347,7 +276,7 @@ int main(int argc, char *argv[])
|
||||
paraview_dc.RegisterField("pressure",&p);
|
||||
paraview_dc.Save();
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -360,14 +289,14 @@ int main(int argc, char *argv[])
|
||||
p_sock << "solution\n" << *mesh << p << "window_title 'Pressure'" << endl;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
// 16. Free the used memory.
|
||||
delete fform;
|
||||
delete gform;
|
||||
delete invM;
|
||||
delete invS;
|
||||
delete S;
|
||||
delete Bt;
|
||||
delete MinvBt;
|
||||
delete BT;
|
||||
delete mVarf;
|
||||
delete bVarf;
|
||||
delete W_space;
|
||||
|
||||
+45
-125
@@ -4,19 +4,11 @@
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex5p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex5p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex5p -m ../data/star.mesh -r 2 -pa
|
||||
// mpirun -np 4 ex5p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex5p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex5p -m ../data/beam-hex.mesh -pa
|
||||
// mpirun -np 4 ex5p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex5p -m ../data/fichera.mesh
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex5p -m ../data/star.mesh -r 2 -pa -d cuda
|
||||
// mpirun -np 4 ex5p -m ../data/star.mesh -r 2 -pa -d raja-cuda
|
||||
// mpirun -np 4 ex5p -m ../data/star.mesh -r 2 -pa -d raja-omp
|
||||
// mpirun -np 4 ex5p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code solves a simple 2D/3D mixed Darcy problem
|
||||
// corresponding to the saddle point system
|
||||
// k*u + grad p = f
|
||||
@@ -62,28 +54,19 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int ref_levels = -1;
|
||||
int order = 1;
|
||||
bool par_format = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
bool adios2 = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&par_format, "-pf", "--parallel-format", "-sf",
|
||||
"--serial-format",
|
||||
"Format to use when saving the results for VisIt.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -105,34 +88,26 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements, unless the user specifies it as input.
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
if (ref_levels == -1)
|
||||
{
|
||||
ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
}
|
||||
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
@@ -145,7 +120,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
FiniteElementCollection *hdiv_coll(new RT_FECollection(order, dim));
|
||||
FiniteElementCollection *l2_coll(new L2_FECollection(order, dim));
|
||||
@@ -165,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
std::cout << "***********************************************************\n";
|
||||
}
|
||||
|
||||
// 8. Define the two BlockStructure of the problem. block_offsets is used
|
||||
// 7. Define the two BlockStructure of the problem. block_offsets is used
|
||||
// for Vector based on dof (like ParGridFunction or ParLinearForm),
|
||||
// block_trueOffstes is used for Vector based on trueDof (HypreParVector
|
||||
// for the rhs and solution of the linear system). The offsets computed
|
||||
@@ -182,7 +157,7 @@ int main(int argc, char *argv[])
|
||||
block_trueOffsets[2] = W_space->TrueVSize();
|
||||
block_trueOffsets.PartialSum();
|
||||
|
||||
// 9. Define the coefficients, analytical solution, and rhs of the PDE.
|
||||
// 8. Define the coefficients, analytical solution, and rhs of the PDE.
|
||||
ConstantCoefficient k(1.0);
|
||||
|
||||
VectorFunctionCoefficient fcoeff(dim, fFun);
|
||||
@@ -192,30 +167,25 @@ int main(int argc, char *argv[])
|
||||
VectorFunctionCoefficient ucoeff(dim, uFun_ex);
|
||||
FunctionCoefficient pcoeff(pFun_ex);
|
||||
|
||||
// 10. Define the parallel grid function and parallel linear forms, solution
|
||||
// vector and rhs.
|
||||
MemoryType mt = device.GetMemoryType();
|
||||
BlockVector x(block_offsets, mt), rhs(block_offsets, mt);
|
||||
BlockVector trueX(block_trueOffsets, mt), trueRhs(block_trueOffsets, mt);
|
||||
// 9. Define the parallel grid function and parallel linear forms, solution
|
||||
// vector and rhs.
|
||||
BlockVector x(block_offsets), rhs(block_offsets);
|
||||
BlockVector trueX(block_trueOffsets), trueRhs(block_trueOffsets);
|
||||
|
||||
ParLinearForm *fform(new ParLinearForm);
|
||||
fform->Update(R_space, rhs.GetBlock(0), 0);
|
||||
fform->AddDomainIntegrator(new VectorFEDomainLFIntegrator(fcoeff));
|
||||
fform->AddBoundaryIntegrator(new VectorFEBoundaryFluxLFIntegrator(fnatcoeff));
|
||||
fform->Assemble();
|
||||
fform->SyncAliasMemory(rhs);
|
||||
fform->ParallelAssemble(trueRhs.GetBlock(0));
|
||||
trueRhs.GetBlock(0).SyncAliasMemory(trueRhs);
|
||||
|
||||
ParLinearForm *gform(new ParLinearForm);
|
||||
gform->Update(W_space, rhs.GetBlock(1), 0);
|
||||
gform->AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
|
||||
gform->Assemble();
|
||||
gform->SyncAliasMemory(rhs);
|
||||
gform->ParallelAssemble(trueRhs.GetBlock(1));
|
||||
trueRhs.GetBlock(1).SyncAliasMemory(trueRhs);
|
||||
|
||||
// 11. Assemble the finite element matrices for the Darcy operator
|
||||
// 10. Assemble the finite element matrices for the Darcy operator
|
||||
//
|
||||
// D = [ M B^T ]
|
||||
// [ B 0 ]
|
||||
@@ -226,93 +196,44 @@ int main(int argc, char *argv[])
|
||||
ParBilinearForm *mVarf(new ParBilinearForm(R_space));
|
||||
ParMixedBilinearForm *bVarf(new ParMixedBilinearForm(R_space, W_space));
|
||||
|
||||
HypreParMatrix *M = NULL;
|
||||
HypreParMatrix *B = NULL;
|
||||
HypreParMatrix *M, *B;
|
||||
|
||||
if (pa) { mVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
mVarf->AddDomainIntegrator(new VectorFEMassIntegrator(k));
|
||||
mVarf->Assemble();
|
||||
if (!pa) { mVarf->Finalize(); }
|
||||
mVarf->Finalize();
|
||||
M = mVarf->ParallelAssemble();
|
||||
|
||||
if (pa) { bVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
bVarf->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
||||
bVarf->Assemble();
|
||||
if (!pa) { bVarf->Finalize(); }
|
||||
bVarf->Finalize();
|
||||
B = bVarf->ParallelAssemble();
|
||||
(*B) *= -1;
|
||||
|
||||
HypreParMatrix *BT = B->Transpose();
|
||||
|
||||
BlockOperator *darcyOp = new BlockOperator(block_trueOffsets);
|
||||
darcyOp->SetBlock(0,0, M);
|
||||
darcyOp->SetBlock(0,1, BT);
|
||||
darcyOp->SetBlock(1,0, B);
|
||||
|
||||
Array<int> empty_tdof_list; // empty
|
||||
OperatorPtr opM, opB;
|
||||
|
||||
TransposeOperator *Bt = NULL;
|
||||
|
||||
if (pa)
|
||||
{
|
||||
mVarf->FormSystemMatrix(empty_tdof_list, opM);
|
||||
bVarf->FormRectangularSystemMatrix(empty_tdof_list, empty_tdof_list, opB);
|
||||
Bt = new TransposeOperator(opB.Ptr());
|
||||
|
||||
darcyOp->SetBlock(0,0, opM.Ptr());
|
||||
darcyOp->SetBlock(0,1, Bt, -1.0);
|
||||
darcyOp->SetBlock(1,0, opB.Ptr(), -1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
M = mVarf->ParallelAssemble();
|
||||
B = bVarf->ParallelAssemble();
|
||||
(*B) *= -1;
|
||||
Bt = new TransposeOperator(B);
|
||||
|
||||
darcyOp->SetBlock(0,0, M);
|
||||
darcyOp->SetBlock(0,1, Bt);
|
||||
darcyOp->SetBlock(1,0, B);
|
||||
}
|
||||
|
||||
// 12. Construct the operators for preconditioner
|
||||
// 11. Construct the operators for preconditioner
|
||||
//
|
||||
// P = [ diag(M) 0 ]
|
||||
// [ 0 B diag(M)^-1 B^T ]
|
||||
//
|
||||
// Here we use Symmetric Gauss-Seidel to approximate the inverse of the
|
||||
// pressure Schur Complement.
|
||||
HypreParMatrix *MinvBt = NULL;
|
||||
HypreParVector *Md = NULL;
|
||||
HypreParMatrix *S = NULL;
|
||||
Vector Md_PA;
|
||||
Solver *invM, *invS;
|
||||
HypreParMatrix *MinvBt = B->Transpose();
|
||||
HypreParVector *Md = new HypreParVector(MPI_COMM_WORLD, M->GetGlobalNumRows(),
|
||||
M->GetRowStarts());
|
||||
M->GetDiag(*Md);
|
||||
|
||||
if (pa)
|
||||
{
|
||||
Md_PA.SetSize(R_space->GetTrueVSize());
|
||||
mVarf->AssembleDiagonal(Md_PA);
|
||||
auto Md_host = Md_PA.HostRead();
|
||||
Vector invMd(Md_PA.Size());
|
||||
for (int i=0; i<Md_PA.Size(); ++i)
|
||||
{
|
||||
invMd(i) = 1.0 / Md_host[i];
|
||||
}
|
||||
MinvBt->InvScaleRows(*Md);
|
||||
HypreParMatrix *S = ParMult(B, MinvBt);
|
||||
|
||||
Vector BMBt_diag(W_space->GetTrueVSize());
|
||||
bVarf->AssembleDiagonal_ADAt(invMd, BMBt_diag);
|
||||
|
||||
Array<int> ess_tdof_list; // empty
|
||||
|
||||
invM = new OperatorJacobiSmoother(Md_PA, ess_tdof_list);
|
||||
invS = new OperatorJacobiSmoother(BMBt_diag, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
Md = new HypreParVector(MPI_COMM_WORLD, M->GetGlobalNumRows(),
|
||||
M->GetRowStarts());
|
||||
M->GetDiag(*Md);
|
||||
|
||||
MinvBt = B->Transpose();
|
||||
MinvBt->InvScaleRows(*Md);
|
||||
S = ParMult(B, MinvBt);
|
||||
|
||||
invM = new HypreDiagScale(*M);
|
||||
invS = new HypreBoomerAMG(*S);
|
||||
}
|
||||
HypreSolver *invM, *invS;
|
||||
invM = new HypreDiagScale(*M);
|
||||
invS = new HypreBoomerAMG(*S);
|
||||
|
||||
invM->iterative_mode = false;
|
||||
invS->iterative_mode = false;
|
||||
@@ -322,9 +243,9 @@ int main(int argc, char *argv[])
|
||||
darcyPr->SetDiagonalBlock(0, invM);
|
||||
darcyPr->SetDiagonalBlock(1, invS);
|
||||
|
||||
// 13. Solve the linear system with MINRES.
|
||||
// 12. Solve the linear system with MINRES.
|
||||
// Check the norm of the unpreconditioned residual.
|
||||
int maxIter(pa ? 1000 : 500);
|
||||
int maxIter(500);
|
||||
double rtol(1.e-6);
|
||||
double atol(1.e-10);
|
||||
|
||||
@@ -339,7 +260,6 @@ int main(int argc, char *argv[])
|
||||
solver.SetPrintLevel(verbose);
|
||||
trueX = 0.0;
|
||||
solver.Mult(trueRhs, trueX);
|
||||
if (device.IsEnabled()) { trueX.HostRead(); }
|
||||
chrono.Stop();
|
||||
|
||||
if (verbose)
|
||||
@@ -353,7 +273,7 @@ int main(int argc, char *argv[])
|
||||
std::cout << "MINRES solver took " << chrono.RealTime() << "s. \n";
|
||||
}
|
||||
|
||||
// 14. Extract the parallel grid function corresponding to the finite element
|
||||
// 13. Extract the parallel grid function corresponding to the finite element
|
||||
// approximation X. This is the local solution on each processor. Compute
|
||||
// L2 error norms.
|
||||
ParGridFunction *u(new ParGridFunction);
|
||||
@@ -381,7 +301,7 @@ int main(int argc, char *argv[])
|
||||
std::cout << "|| p_h - p_ex || / || p_ex || = " << err_p / norm_p << "\n";
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can be
|
||||
// 14. Save the refined mesh and the solution in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol_*".
|
||||
{
|
||||
ostringstream mesh_name, u_name, p_name;
|
||||
@@ -402,7 +322,7 @@ int main(int argc, char *argv[])
|
||||
p->Save(p_ofs);
|
||||
}
|
||||
|
||||
// 16. Save data in the VisIt format
|
||||
// 15. Save data in the VisIt format
|
||||
VisItDataCollection visit_dc("Example5-Parallel", pmesh);
|
||||
visit_dc.RegisterField("velocity", u);
|
||||
visit_dc.RegisterField("pressure", p);
|
||||
@@ -411,7 +331,7 @@ int main(int argc, char *argv[])
|
||||
DataCollection::PARALLEL_FORMAT);
|
||||
visit_dc.Save();
|
||||
|
||||
// 17. Save data in the ParaView format
|
||||
// 16. Save data in the ParaView format
|
||||
ParaViewDataCollection paraview_dc("Example5P", pmesh);
|
||||
paraview_dc.SetPrefixPath("ParaView");
|
||||
paraview_dc.SetLevelsOfDetail(order);
|
||||
@@ -423,7 +343,7 @@ int main(int argc, char *argv[])
|
||||
paraview_dc.RegisterField("pressure",p);
|
||||
paraview_dc.Save();
|
||||
|
||||
// 18. Optionally output a BP (binary pack) file using ADIOS2. This can be
|
||||
// 17. Optionally output a BP (binary pack) file using ADIOS2. This can be
|
||||
// visualized with the ParaView VTX reader.
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
if (adios2)
|
||||
@@ -443,7 +363,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
|
||||
// 19. Send the solution by socket to a GLVis server.
|
||||
// 18. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -463,7 +383,7 @@ int main(int argc, char *argv[])
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// 20. Free the used memory.
|
||||
// 19. Free the used memory.
|
||||
delete fform;
|
||||
delete gform;
|
||||
delete u;
|
||||
@@ -475,7 +395,7 @@ int main(int argc, char *argv[])
|
||||
delete S;
|
||||
delete Md;
|
||||
delete MinvBt;
|
||||
delete Bt;
|
||||
delete BT;
|
||||
delete B;
|
||||
delete M;
|
||||
delete mVarf;
|
||||
|
||||
+4
-9
@@ -20,7 +20,7 @@
|
||||
// ex6 -pa -d occa-cuda
|
||||
// ex6 -pa -d raja-omp
|
||||
// ex6 -pa -d ceed-cpu
|
||||
// * ex6 -pa -d ceed-cuda
|
||||
// * ex6 -pa -d ceed-cuda
|
||||
// ex6 -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
@@ -108,11 +108,7 @@ int main(int argc, char *argv[])
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
BilinearForm a(&fespace);
|
||||
if (pa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.SetDiagonalPolicy(Operator::DIAG_ONE);
|
||||
}
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
LinearForm b(&fespace);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
@@ -203,10 +199,9 @@ int main(int argc, char *argv[])
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
else // Diagonal preconditioning in partial assembly mode.
|
||||
else // No preconditioning for now in partial assembly mode.
|
||||
{
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
PCG(*A, M, B, X, 3, 2000, 1e-12, 0.0);
|
||||
CG(*A, B, X, 3, 2000, 1e-12, 0.0);
|
||||
}
|
||||
|
||||
// 18. After solving the linear system, reconstruct the solution as a
|
||||
|
||||
+7
-20
@@ -20,7 +20,7 @@
|
||||
// mpirun -np 4 ex6p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex6p -pa -d raja-omp
|
||||
// mpirun -np 4 ex6p -pa -d ceed-cpu
|
||||
// * mpirun -np 4 ex6p -pa -d ceed-cuda
|
||||
// * mpirun -np 4 ex6p -pa -d ceed-cuda
|
||||
// mpirun -np 4 ex6p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
//
|
||||
// Description: This is a version of Example 1 with a simple adaptive mesh
|
||||
@@ -129,11 +129,7 @@ int main(int argc, char *argv[])
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a.SetDiagonalPolicy(Operator::DIAG_ONE);
|
||||
}
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
ParLinearForm b(&fespace);
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
@@ -224,26 +220,17 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 17. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use a diagonal preconditioner.
|
||||
Solver *M = NULL;
|
||||
if (pa)
|
||||
{
|
||||
M = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreBoomerAMG *amg = new HypreBoomerAMG;
|
||||
amg->SetPrintLevel(0);
|
||||
M = amg;
|
||||
}
|
||||
// * With partial assembly, use no preconditioner, for now.
|
||||
HypreBoomerAMG *amg = NULL;
|
||||
if (!pa) { amg = new HypreBoomerAMG; amg->SetPrintLevel(0); }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-6);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(3); // print the first and the last iterations only
|
||||
cg.SetPreconditioner(*M);
|
||||
if (amg) { cg.SetPreconditioner(*amg); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete M;
|
||||
delete amg;
|
||||
|
||||
// 18. Switch back to the host and extract the parallel grid function
|
||||
// corresponding to the finite element approximation X. This is the
|
||||
|
||||
+8
-27
@@ -19,12 +19,8 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex9 -pa
|
||||
// ex9 -ea
|
||||
// ex9 -fa
|
||||
// ex9 -pa -m ../data/periodic-cube.mesh
|
||||
// ex9 -pa -m ../data/periodic-cube.mesh -d cuda
|
||||
// ex9 -ea -m ../data/periodic-cube.mesh -d cuda
|
||||
// ex9 -fa -m ../data/periodic-cube.mesh -d cuda
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection equation
|
||||
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
|
||||
@@ -146,8 +142,6 @@ int main(int argc, char *argv[])
|
||||
int ref_levels = 2;
|
||||
int order = 3;
|
||||
bool pa = false;
|
||||
bool ea = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
int ode_solver_type = 4;
|
||||
double t_final = 10.0;
|
||||
@@ -172,10 +166,6 @@ int main(int argc, char *argv[])
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
|
||||
"--no-element-assembly", "Enable Element Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
@@ -279,16 +269,6 @@ int main(int argc, char *argv[])
|
||||
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
}
|
||||
else if (ea)
|
||||
{
|
||||
m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
|
||||
k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
|
||||
}
|
||||
else if (fa)
|
||||
{
|
||||
m.SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
k.SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
}
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
k.AddInteriorFaceIntegrator(
|
||||
@@ -448,19 +428,20 @@ int main(int argc, char *argv[])
|
||||
FE_Evolution::FE_Evolution(BilinearForm &_M, BilinearForm &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b), z(_M.Height())
|
||||
{
|
||||
bool pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
|
||||
Array<int> ess_tdof_list;
|
||||
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACYFULL)
|
||||
{
|
||||
M_prec = new DSmoother(M.SpMat());
|
||||
M_solver.SetOperator(M.SpMat());
|
||||
dg_solver = new DG_Solver(M.SpMat(), K.SpMat(), *M.FESpace());
|
||||
}
|
||||
else
|
||||
if (pa)
|
||||
{
|
||||
M_prec = new OperatorJacobiSmoother(M, ess_tdof_list);
|
||||
M_solver.SetOperator(M);
|
||||
dg_solver = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
M_prec = new DSmoother(M.SpMat());
|
||||
dg_solver = new DG_Solver(M.SpMat(), K.SpMat(), *M.FESpace());
|
||||
M_solver.SetOperator(M.SpMat());
|
||||
}
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
|
||||
+14
-34
@@ -16,16 +16,11 @@
|
||||
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 2 -rp 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 3 -rp 2 -dt 0.0025 -tf 9 -vs 20
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-cube.mesh -p 0 -o 2 -rp 1 -dt 0.01 -tf 8
|
||||
// mpirun -np 3 ex9p -m ../data/amr-hex.mesh -p 1 -rs 1 -rp 0 -dt 0.005 -tf 0.5
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex9p -pa
|
||||
// mpirun -np 4 ex9p -ea
|
||||
// mpirun -np 4 ex9p -fa
|
||||
// mpirun -np 4 ex9p -pa -m ../data/periodic-cube.mesh
|
||||
// mpirun -np 4 ex9p -pa -m ../data/periodic-cube.mesh -d cuda
|
||||
// mpirun -np 4 ex9p -ea -m ../data/periodic-cube.mesh -d cuda
|
||||
// mpirun -np 4 ex9p -fa -m ../data/periodic-cube.mesh -d cuda
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection equation
|
||||
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
|
||||
@@ -166,8 +161,6 @@ int main(int argc, char *argv[])
|
||||
int par_ref_levels = 0;
|
||||
int order = 3;
|
||||
bool pa = false;
|
||||
bool ea = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
int ode_solver_type = 4;
|
||||
double t_final = 10.0;
|
||||
@@ -195,10 +188,6 @@ int main(int argc, char *argv[])
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
|
||||
"--no-element-assembly", "Enable Element Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
@@ -330,17 +319,6 @@ int main(int argc, char *argv[])
|
||||
m->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
k->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
}
|
||||
else if (ea)
|
||||
{
|
||||
m->SetAssemblyLevel(AssemblyLevel::ELEMENT);
|
||||
k->SetAssemblyLevel(AssemblyLevel::ELEMENT);
|
||||
}
|
||||
else if (fa)
|
||||
{
|
||||
m->SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
k->SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
}
|
||||
|
||||
m->AddDomainIntegrator(new MassIntegrator);
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
k->AddInteriorFaceIntegrator(
|
||||
@@ -577,21 +555,28 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
|
||||
M_solver(_M.ParFESpace()->GetComm()),
|
||||
z(_M.Height())
|
||||
{
|
||||
if (_M.GetAssemblyLevel()==AssemblyLevel::LEGACYFULL)
|
||||
{
|
||||
M.Reset(_M.ParallelAssemble(), true);
|
||||
K.Reset(_K.ParallelAssemble(), true);
|
||||
}
|
||||
else
|
||||
bool pa = _M.GetAssemblyLevel()==AssemblyLevel::PARTIAL;
|
||||
|
||||
if (pa)
|
||||
{
|
||||
M.Reset(&_M, false);
|
||||
K.Reset(&_K, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
M.Reset(_M.ParallelAssemble(), true);
|
||||
K.Reset(_K.ParallelAssemble(), true);
|
||||
}
|
||||
|
||||
M_solver.SetOperator(*M);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
if (_M.GetAssemblyLevel()==AssemblyLevel::LEGACYFULL)
|
||||
if (pa)
|
||||
{
|
||||
M_prec = new OperatorJacobiSmoother(_M, ess_tdof_list);
|
||||
dg_solver = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix &M_mat = *M.As<HypreParMatrix>();
|
||||
HypreParMatrix &K_mat = *K.As<HypreParMatrix>();
|
||||
@@ -600,11 +585,6 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
|
||||
|
||||
dg_solver = new DG_Solver(M_mat, K_mat, *_M.FESpace());
|
||||
}
|
||||
else
|
||||
{
|
||||
M_prec = new OperatorJacobiSmoother(_M, ess_tdof_list);
|
||||
dg_solver = NULL;
|
||||
}
|
||||
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
|
||||
@@ -2,44 +2,36 @@
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/star-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../../data/star-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../../data/fichera-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh -o -1 -sc
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d cuda
|
||||
// mpirun -np 4 ex1p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex1p -pa -d raja-omp
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -m ../../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// AmgX sample runs:
|
||||
//
|
||||
// mpirun -n 40 ./ex1p --amgx-file amg_pcg.json
|
||||
// lrun -n 4 ./ex1p --amgx-file amg_pcg.json --amgx-mpi-gpu-exclusive
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
@@ -59,6 +51,7 @@
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "mpi.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
@@ -72,15 +65,15 @@ int main(int argc, char *argv[])
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
// const char *mesh_file = "../data/star.mesh";
|
||||
const char *mesh_file = "../data/square-disc.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool amgx_mpi_teams = true;
|
||||
const char* amgx_json_file = ""; // jason file for amgx
|
||||
int ndevices = 1;
|
||||
bool visualization = false;
|
||||
int nfiles = 1;
|
||||
// const char *out_file = "0_0.gf";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -92,17 +85,14 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&amgx_json_file, "--amgx-file", "--amgx-file",
|
||||
"AMGX solver config file (overrides --amgx-solver, --amgx-verbose)");
|
||||
args.AddOption(&amgx_mpi_teams, "--amgx-mpi-teams", "--amgx-mpi-teams",
|
||||
"--amgx-mpi-gpu-exclusive", "--amgx-mpi-gpu-exclusive",
|
||||
"Create MPI teams when using AMGX.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&ndevices, "-nd","--nd","Number of GPU devices.");
|
||||
args.AddOption(&nfiles, "-nf", "--num-files", "Number of files to write.");
|
||||
// args.AddOption(&out_file, "-o", "--outfile",
|
||||
// "Name of file to write.");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
@@ -117,9 +107,6 @@ int main(int argc, char *argv[])
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
|
||||
MFEM_VERIFY(!pa == true && strcmp(amgx_json_file,"") != 0,
|
||||
"An AmgX json file is needed for this example \n");
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
@@ -130,8 +117,8 @@ int main(int argc, char *argv[])
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
@@ -139,23 +126,23 @@ int main(int argc, char *argv[])
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,16 +150,13 @@ int main(int argc, char *argv[])
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
else if (pmesh->GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
fec = pmesh->GetNodes()->OwnFEC();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
@@ -181,10 +165,9 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_Int size = fespace.GlobalTrueVSize();
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec, 1, 0);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
@@ -195,127 +178,114 @@ int main(int argc, char *argv[])
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm b(&fespace);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
Solver *prec = NULL;
|
||||
if (!pa)
|
||||
if (pa)
|
||||
{
|
||||
|
||||
AmgXSolver amgx;
|
||||
|
||||
amgx.ReadParameters(amgx_json_file, AmgXSolver::EXTERNAL);
|
||||
|
||||
if (amgx_mpi_teams)
|
||||
if (UsesTensorBasis(*fespace))
|
||||
{
|
||||
//Forms MPI teams to load balance between mpi ranks and gpus
|
||||
amgx.InitMPITeams(MPI_COMM_WORLD, ndevices);
|
||||
prec = new OperatorJacobiSmoother(*a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Assumes MPI == number of devices
|
||||
amgx.InitExclusiveGPU(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
amgx.SetOperator(*A.As<HypreParMatrix>());
|
||||
|
||||
amgx.Mult(B, X);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
std::string filename = to_string(num_procs) + "_" + to_string(nfiles) + "_";
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
double t1;
|
||||
t1 = MPI_Wtime();
|
||||
x.Save(filename.c_str(), nfiles);
|
||||
double t2 = MPI_Wtime();
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh.Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
double write_time = t2 - t1;
|
||||
double average_write_time;
|
||||
MPI_Reduce(&write_time, &average_write_time, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
{
|
||||
std::cout << "Average write time: " << average_write_time / num_procs << " for "
|
||||
<< nfiles << " files and " << num_procs << " ranks\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << x << flush;
|
||||
double t1;
|
||||
t1 = MPI_Wtime();
|
||||
ParGridFunction temp_gf(fespace, filename.c_str());
|
||||
double t2 = MPI_Wtime();
|
||||
|
||||
double read_time = t2 - t1;
|
||||
double average_read_time;
|
||||
MPI_Reduce(&read_time, &average_read_time, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
{
|
||||
std::cout << "Average read time: " << average_read_time / num_procs << " for "
|
||||
<< nfiles << " files and " << num_procs << " ranks\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
if (order > 0) { delete fec; }
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
@@ -114,11 +114,6 @@ ex11p-test-strumpack: ex11p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), STRUMPACK example,--strumpack)
|
||||
test-par-YES: ex11p-test-strumpack
|
||||
endif
|
||||
ifeq ($(MFEM_USE_SUPERLU),YES)
|
||||
ex11p-test-superlu: ex11p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), SuperLU_DIST example,--superlu)
|
||||
test-par-YES: ex11p-test-superlu
|
||||
endif
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
|
||||
@@ -1,517 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
STRUMPACKSolver* CreateStrumpackSolver(Operator *Arow, MPI_Comm comm)
|
||||
{
|
||||
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, comm);
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, comm);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
return strumpack;
|
||||
}
|
||||
|
||||
hypre_CSRMatrix* GetHypreParMatrixData(const HypreParMatrix & hypParMat)
|
||||
{
|
||||
// First cast the parameter to a hypre_ParCSRMatrix
|
||||
hypre_ParCSRMatrix * parcsr_op =
|
||||
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(hypParMat);
|
||||
|
||||
MFEM_ASSERT(parcsr_op != NULL,"STRUMPACK: const_cast failed in SetOperator");
|
||||
|
||||
// Create the CSRMatrixMPI A_ by borrowing the internal data from a hypre_CSRMatrix.
|
||||
return hypre_MergeDiagAndOffd(parcsr_op);
|
||||
}
|
||||
|
||||
// Row and column offsets are assumed to be the same, for each process.
|
||||
// Array offsets stores process-local offsets with respect to the blocks. Process offsets are not included.
|
||||
HypreParMatrix* CreateHypreParMatrixFromBlocks(MPI_Comm comm,
|
||||
Array<int> const& offsets, Array2D<HypreParMatrix*> const& blocks,
|
||||
Array2D<SparseMatrix*> const& blocksSp,
|
||||
Array2D<double> const& coefficient,
|
||||
std::vector<std::vector<int> > const& blockProcOffsets,
|
||||
std::vector<std::vector<int> > const& all_block_num_loc_rows)
|
||||
{
|
||||
const int numBlocks = offsets.Size() - 1;
|
||||
const int num_loc_rows = offsets[numBlocks];
|
||||
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
MPI_Comm_size(comm, &nprocs);
|
||||
|
||||
std::vector<int> all_num_loc_rows(nprocs);
|
||||
std::vector<int> procOffsets(nprocs);
|
||||
std::vector<std::vector<int> > procBlockOffsets(nprocs);
|
||||
|
||||
MPI_Allgather(&num_loc_rows, 1, MPI_INT, all_num_loc_rows.data(), 1, MPI_INT,
|
||||
comm);
|
||||
|
||||
int first_loc_row = 0;
|
||||
int glob_nrows = 0;
|
||||
procOffsets[0] = 0;
|
||||
for (int i=0; i<nprocs; ++i)
|
||||
{
|
||||
glob_nrows += all_num_loc_rows[i];
|
||||
if (i < rank)
|
||||
{
|
||||
first_loc_row += all_num_loc_rows[i];
|
||||
}
|
||||
|
||||
if (i < nprocs-1)
|
||||
{
|
||||
procOffsets[i+1] = procOffsets[i] + all_num_loc_rows[i];
|
||||
}
|
||||
|
||||
if (numBlocks > 0)
|
||||
{
|
||||
procBlockOffsets[i].resize(numBlocks);
|
||||
procBlockOffsets[i][0] = 0;
|
||||
}
|
||||
|
||||
for (int j=1; j<numBlocks; ++j)
|
||||
{
|
||||
procBlockOffsets[i][j] = procBlockOffsets[i][j-1] + all_block_num_loc_rows[j
|
||||
-1][i];
|
||||
}
|
||||
}
|
||||
|
||||
const int glob_ncols = glob_nrows;
|
||||
|
||||
std::vector<int> opI(num_loc_rows+1);
|
||||
std::vector<int> cnt(num_loc_rows);
|
||||
|
||||
for (int i=0; i<num_loc_rows; ++i)
|
||||
{
|
||||
opI[i] = 0;
|
||||
cnt[i] = 0;
|
||||
}
|
||||
|
||||
opI[num_loc_rows] = 0;
|
||||
|
||||
Array2D<hypre_CSRMatrix*> csr_blocks(numBlocks, numBlocks);
|
||||
|
||||
// Loop over all blocks, to determine nnz for each row.
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
if (blocks(i, j) == NULL)
|
||||
{
|
||||
csr_blocks(i, j) = NULL;
|
||||
|
||||
if (blocksSp(i, j) != NULL)
|
||||
{
|
||||
const int nrows = blocksSp(i, j)->Height();
|
||||
for (int k=0; k<nrows; ++k)
|
||||
{
|
||||
const int rowg = offsets[i] + k;
|
||||
opI[rowg + 1] += blocksSp(i, j)->GetI()[k+1] - blocksSp(i, j)->GetI()[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(blocksSp(i, j) == NULL, "");
|
||||
|
||||
csr_blocks(i, j) = GetHypreParMatrixData(*(blocks(i, j)));
|
||||
|
||||
const int nrows = csr_blocks(i, j)->num_rows;
|
||||
|
||||
for (int k=0; k<nrows; ++k)
|
||||
{
|
||||
const int rowg = offsets[i] + k;
|
||||
//(*(leftInjection(i, j)))[k]
|
||||
opI[rowg + 1] += csr_blocks(i, j)->i[k+1] - csr_blocks(i, j)->i[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now opI[i] is nnz for row i-1. Do a partial sum to get offsets.
|
||||
for (int i=0; i<num_loc_rows; ++i)
|
||||
{
|
||||
opI[i+1] += opI[i];
|
||||
}
|
||||
|
||||
const int nnz = opI[num_loc_rows];
|
||||
|
||||
std::vector<HYPRE_Int> opJ(nnz);
|
||||
std::vector<double> data(nnz);
|
||||
|
||||
// Loop over all blocks, to set matrix data.
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
if (csr_blocks(i, j) != NULL || blocksSp(i, j) != NULL)
|
||||
{
|
||||
const bool useCSR = (csr_blocks(i, j) != NULL);
|
||||
|
||||
const int nrows = useCSR ? csr_blocks(i, j)->num_rows : blocksSp(i,
|
||||
j)->Height();
|
||||
const double coef = coefficient(i, j);
|
||||
|
||||
int *Iarray = useCSR ? csr_blocks(i, j)->i : blocksSp(i, j)->GetI();
|
||||
|
||||
//const bool failure = (nrows != offsets[i+1] - offsets[i]);
|
||||
|
||||
MFEM_VERIFY(nrows == offsets[i+1] - offsets[i], "");
|
||||
|
||||
for (int k=0; k<nrows; ++k)
|
||||
{
|
||||
const int rowg = offsets[i] + k; // process-local row
|
||||
const int nnz_k = Iarray[k+1] - Iarray[k];
|
||||
const int osk = Iarray[k];
|
||||
|
||||
for (int l=0; l<nnz_k; ++l)
|
||||
{
|
||||
// Find the column process offset for the block.
|
||||
const int bcol = useCSR ? csr_blocks(i, j)->j[osk + l] : blocksSp(i,
|
||||
j)->GetJ()[osk + l];
|
||||
int bcolproc = 0;
|
||||
|
||||
for (int p=1; p<nprocs; ++p)
|
||||
{
|
||||
if (blockProcOffsets[j][p] > bcol)
|
||||
{
|
||||
bcolproc = p-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockProcOffsets[j][nprocs - 1] <= bcol)
|
||||
{
|
||||
bcolproc = nprocs - 1;
|
||||
}
|
||||
|
||||
const int colg = procOffsets[bcolproc] + procBlockOffsets[bcolproc][j] +
|
||||
(bcol - blockProcOffsets[j][bcolproc]);
|
||||
|
||||
if (colg < 0)
|
||||
{
|
||||
cout << "BUG, negative global column index" << endl;
|
||||
}
|
||||
|
||||
opJ[opI[rowg] + cnt[rowg]] = colg;
|
||||
data[opI[rowg] + cnt[rowg]] = useCSR ? coef * csr_blocks(i,
|
||||
j)->data[osk + l] : coef * blocksSp(i, j)->GetData()[osk + l];
|
||||
cnt[rowg]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cntCheck = true;
|
||||
for (int i=0; i<num_loc_rows; ++i)
|
||||
{
|
||||
if (cnt[i] != opI[i+1] - opI[i])
|
||||
{
|
||||
cntCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(cntCheck, "");
|
||||
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
if (csr_blocks(i, j) != NULL)
|
||||
{
|
||||
hypre_CSRMatrixDestroy(csr_blocks(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<HYPRE_Int> rowStarts2(2);
|
||||
rowStarts2[0] = first_loc_row;
|
||||
rowStarts2[1] = first_loc_row + all_num_loc_rows[rank];
|
||||
|
||||
if (nnz > 0)
|
||||
{
|
||||
HYPRE_Int minJ = opJ[0];
|
||||
HYPRE_Int maxJ = opJ[0];
|
||||
for (int i=0; i<nnz; ++i)
|
||||
{
|
||||
minJ = std::min(minJ, opJ[i]);
|
||||
maxJ = std::max(maxJ, opJ[i]);
|
||||
|
||||
if (opJ[i] >= glob_ncols)
|
||||
{
|
||||
cout << "Column indices out of range" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HypreParMatrix *hmat = new HypreParMatrix(comm, num_loc_rows, glob_nrows,
|
||||
glob_ncols, (int*) opI.data(), (HYPRE_Int*) opJ.data(), (double*) data.data(),
|
||||
(HYPRE_Int*) rowStarts2.data(), (HYPRE_Int*) rowStarts2.data());
|
||||
|
||||
return hmat;
|
||||
}
|
||||
|
||||
class BlockMGSolver : public Solver
|
||||
{
|
||||
private:
|
||||
/// The linear system matrix
|
||||
Array2D<HypreParMatrix *>&
|
||||
Af; // TODO: remove this, as it is used only in the constructor
|
||||
Array2D<double>&
|
||||
Acoef; // TODO: remove this, as it is used only in the constructor
|
||||
vector<Array<int>> Aoffsets;
|
||||
vector<Array<int>> Poffsets_i;
|
||||
vector<Array<int>> Poffsets_j;
|
||||
std::vector<Array2D<HypreParMatrix *>> A;
|
||||
std::vector<HypreParMatrix *>& P;
|
||||
std::vector<BlockOperator *> BlkP;
|
||||
std::vector<BlockOperator *> BlkA;
|
||||
std::vector<BlockOperator *> S;
|
||||
HypreParMatrix * Ac;
|
||||
int numGrids, numBlocks;
|
||||
STRUMPACKSolver *invAc = nullptr;
|
||||
double theta = 0.5;
|
||||
|
||||
public:
|
||||
BlockMGSolver(const int height, const int width, Array2D<HypreParMatrix *>& Af_,
|
||||
Array2D<double>& Acoef_, std::vector<HypreParMatrix *>& P_);
|
||||
|
||||
virtual void SetOperator(const Operator &op) {}
|
||||
|
||||
virtual void SetTheta(const double a) { theta = a; }
|
||||
|
||||
virtual void Mult(const Vector &r, Vector &z) const;
|
||||
virtual ~BlockMGSolver();
|
||||
};
|
||||
|
||||
BlockMGSolver::BlockMGSolver(const int height, const int width,
|
||||
Array2D<HypreParMatrix *>& Af_, Array2D<double>& Acoef_,
|
||||
std::vector<HypreParMatrix *>& P_)
|
||||
: Solver(height, width), Af(Af_), Acoef(Acoef_), P(P_)
|
||||
{
|
||||
numBlocks = Af.NumRows();
|
||||
MFEM_VERIFY(Af.NumCols() == numBlocks, "");
|
||||
numGrids = P.size();
|
||||
BlkP.resize(numGrids);
|
||||
BlkA.resize(numGrids+1);
|
||||
S.resize(numGrids);
|
||||
A.resize(numGrids + 1);
|
||||
A[numGrids] = Af;
|
||||
Aoffsets.resize(numGrids+1);
|
||||
Poffsets_i.resize(numGrids);
|
||||
Poffsets_j.resize(numGrids);
|
||||
// Construct Bilinear form Matrices on each level
|
||||
for (int k = numGrids ; k > 0; k--)
|
||||
{
|
||||
A[k - 1].SetSize(numBlocks,numBlocks);
|
||||
Aoffsets[k].SetSize(numBlocks+1); Aoffsets[k][0] = 0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Aoffsets[k][i+1] = A[k](i,i)->Height();
|
||||
}
|
||||
|
||||
Aoffsets[k].PartialSum();
|
||||
BlkA[k] = new BlockOperator(Aoffsets[k]);
|
||||
S[k-1] = new BlockOperator(Aoffsets[k]); // Smoother
|
||||
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
if (A[k](i,j) == NULL)
|
||||
{
|
||||
A[k - 1](i,j) = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
A[k - 1](i,j) = RAP(A[k](i,j), P[k - 1]);
|
||||
BlkA[k]->SetBlock(i, j, A[k](i,j), Acoef(i,j));
|
||||
}
|
||||
}
|
||||
|
||||
HypreSmoother *S_i = new HypreSmoother;
|
||||
S_i->SetType(HypreSmoother::Jacobi);
|
||||
S_i->SetOperator(*(A[k](i,i)));
|
||||
|
||||
S[k - 1]->SetBlock(i,i,S_i);
|
||||
}
|
||||
|
||||
Poffsets_i[k-1].SetSize(numBlocks+1); Poffsets_i[k-1][0] = 0;
|
||||
Poffsets_j[k-1].SetSize(numBlocks+1); Poffsets_j[k-1][0] = 0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Poffsets_i[k-1][i+1] = P[k-1]->Height();
|
||||
Poffsets_j[k-1][i+1] = P[k-1]->Width();
|
||||
}
|
||||
Poffsets_i[k-1].PartialSum();
|
||||
Poffsets_j[k-1].PartialSum();
|
||||
|
||||
BlkP[k-1] = new BlockOperator(Poffsets_i[k-1],Poffsets_j[k-1]);
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
BlkP[k-1]->SetBlock(i,i,P[k-1]);
|
||||
}
|
||||
}
|
||||
// Set up coarse solve operator
|
||||
// Convert the coarse grid blockmatrix to a HypreParMatrix
|
||||
Array<int> offsets(numBlocks+1);
|
||||
offsets[0]=0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
offsets[i+1]=A[0](i,i)->Height();
|
||||
}
|
||||
|
||||
offsets.PartialSum();
|
||||
|
||||
BlkA[0] = new BlockOperator(offsets);
|
||||
|
||||
Array2D<SparseMatrix*> Asp;
|
||||
//Array2D<double> Acoef;
|
||||
Asp.SetSize(numBlocks,numBlocks);
|
||||
//Acoef.SetSize(numBlocks,numBlocks);
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
if (A[0](i,j) != NULL)
|
||||
{
|
||||
BlkA[0]->SetBlock(i, j, A[0](i,j), Acoef(i,j));
|
||||
}
|
||||
|
||||
Asp(i,j) = NULL;
|
||||
//Acoef(i,j) = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to HypreParMatrix
|
||||
HypreParMatrix * Ac;
|
||||
|
||||
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
|
||||
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
|
||||
|
||||
{
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
|
||||
|
||||
std::vector<int> allnumrows(nprocs);
|
||||
const int blockNumRows = A[0](0,0)->Height();
|
||||
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT,
|
||||
MPI_COMM_WORLD);
|
||||
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b].resize(nprocs);
|
||||
all_block_num_loc_rows[b].resize(nprocs);
|
||||
}
|
||||
|
||||
blockProcOffsets[0][0] = 0;
|
||||
for (int i=0; i<nprocs-1; ++i)
|
||||
{
|
||||
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
|
||||
}
|
||||
|
||||
for (int i=0; i<nprocs; ++i)
|
||||
{
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
all_block_num_loc_rows[b][i] = allnumrows[i];
|
||||
}
|
||||
|
||||
for (int b=1; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b][i] = blockProcOffsets[0][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ac = CreateHypreParMatrixFromBlocks(MPI_COMM_WORLD, offsets, A[0], Asp,
|
||||
Acoef, blockProcOffsets, all_block_num_loc_rows);
|
||||
|
||||
invAc = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*Ac), MPI_COMM_WORLD);
|
||||
|
||||
delete Ac;
|
||||
}
|
||||
|
||||
void BlockMGSolver::Mult(const Vector &r, Vector &z) const
|
||||
{
|
||||
// Residual vectors
|
||||
std::vector<Vector> rv(numGrids + 1);
|
||||
// correction vectors
|
||||
std::vector<Vector> zv(numGrids + 1);
|
||||
// allocation
|
||||
for (int i = 0; i <= numGrids ; i++)
|
||||
{
|
||||
int n = (i==0) ? invAc->Height(): BlkA[i]->Width();
|
||||
rv[i].SetSize(n);
|
||||
zv[i].SetSize(n);
|
||||
}
|
||||
// Initial residual
|
||||
rv[numGrids] = r;
|
||||
|
||||
// smooth and update residuals down to the coarsest level
|
||||
for (int i = numGrids; i > 0 ; i--)
|
||||
{
|
||||
// Pre smooth
|
||||
S[i - 1]->Mult(rv[i], zv[i]); zv[i] *= theta;
|
||||
// compute residual
|
||||
int n = BlkA[i]->Width();
|
||||
Vector w(n);
|
||||
BlkA[i]->Mult(zv[i], w);
|
||||
rv[i] -= w;
|
||||
// Restrict
|
||||
BlkP[i - 1]->MultTranspose(rv[i], rv[i - 1]);
|
||||
}
|
||||
|
||||
// Coarse grid Solve
|
||||
invAc->Mult(rv[0], zv[0]);
|
||||
//
|
||||
for (int i = 1; i <= numGrids ; i++)
|
||||
{
|
||||
// Prolong correction
|
||||
Vector u(BlkP[i - 1]->Height());
|
||||
BlkP[i - 1]->Mult(zv[i - 1], u);
|
||||
// Update correction
|
||||
zv[i] += u;
|
||||
// Update residual
|
||||
Vector v(BlkA[i]->Height());
|
||||
BlkA[i]->Mult(u, v); rv[i] -= v;
|
||||
// Post smooth
|
||||
S[i - 1]->Mult(rv[i], v); v *= theta;
|
||||
// Update correction
|
||||
zv[i] += v;
|
||||
}
|
||||
z = zv[numGrids];
|
||||
}
|
||||
|
||||
BlockMGSolver::~BlockMGSolver()
|
||||
{
|
||||
for (int i = numGrids - 1; i >= 0 ; i--)
|
||||
{
|
||||
delete S[i];
|
||||
delete BlkP[i];
|
||||
delete BlkA[i];
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
for (int k=0; k<numBlocks; k++)
|
||||
{
|
||||
delete A[i](j,k);
|
||||
}
|
||||
}
|
||||
A[i].DeleteAll();
|
||||
}
|
||||
delete BlkA[numGrids];
|
||||
delete invAc;
|
||||
A.clear();
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -1,869 +0,0 @@
|
||||
#ifndef BGMULTIGRID
|
||||
#define BGMULTIGRID
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//namespace mfem {
|
||||
namespace blockgmg
|
||||
{
|
||||
|
||||
// Row and column offsets are assumed to be the same, for each process.
|
||||
// Array offsets stores process-local offsets with respect to the blocks. Process offsets are not included.
|
||||
HypreParMatrix* CreateHypreParMatrixFromBlocks2(MPI_Comm comm,
|
||||
Array<int> const& offsets, Array2D<HypreParMatrix*> const& blocks,
|
||||
Array2D<SparseMatrix*> const& blocksSp,
|
||||
Array2D<double> const& coefficient,
|
||||
std::vector<std::vector<int> > const& blockProcOffsets,
|
||||
std::vector<std::vector<int> > const& all_block_num_loc_rows);
|
||||
|
||||
class BlockMGSolver : public Solver
|
||||
{
|
||||
private:
|
||||
/// The linear system matrix
|
||||
Array2D<HypreParMatrix *>&
|
||||
Af; // TODO: remove this, as it is used only in the constructor
|
||||
Array2D<double>&
|
||||
Acoef; // TODO: remove this, as it is used only in the constructor
|
||||
vector<Array<int>> Aoffsets;
|
||||
vector<Array<int>> Poffsets_i;
|
||||
vector<Array<int>> Poffsets_j;
|
||||
std::vector<Array2D<HypreParMatrix *>> A;
|
||||
std::vector<HypreParMatrix *> const& P;
|
||||
std::vector<BlockOperator *> BlkP;
|
||||
std::vector<BlockOperator *> BlkA;
|
||||
std::vector<BlockOperator *> S;
|
||||
HypreParMatrix * Ac;
|
||||
SparseMatrix AcSp;
|
||||
int numGrids, numBlocks;
|
||||
//STRUMPACKSolver *invAc = nullptr;
|
||||
Operator *invAc = nullptr;
|
||||
double theta = 0.5;
|
||||
|
||||
public:
|
||||
BlockMGSolver(MPI_Comm comm, const int height, const int width,
|
||||
Array2D<HypreParMatrix *>& Af_,
|
||||
Array2D<double>& Acoef_, std::vector<HypreParMatrix *> const& P_)
|
||||
: Solver(height, width), Af(Af_), Acoef(Acoef_), P(P_)
|
||||
{
|
||||
numBlocks = Af.NumRows();
|
||||
MFEM_VERIFY(Af.NumCols() == numBlocks, "");
|
||||
numGrids = P.size();
|
||||
BlkP.resize(numGrids);
|
||||
BlkA.resize(numGrids+1);
|
||||
S.resize(numGrids);
|
||||
A.resize(numGrids + 1);
|
||||
A[numGrids] = Af;
|
||||
Aoffsets.resize(numGrids+1);
|
||||
Poffsets_i.resize(numGrids);
|
||||
Poffsets_j.resize(numGrids);
|
||||
// Construct Bilinear form Matrices on each level
|
||||
for (int k = numGrids ; k > 0; k--)
|
||||
{
|
||||
A[k - 1].SetSize(numBlocks,numBlocks);
|
||||
Aoffsets[k].SetSize(numBlocks+1); Aoffsets[k][0] = 0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Aoffsets[k][i+1] = A[k](i,i)->Height();
|
||||
}
|
||||
|
||||
Aoffsets[k].PartialSum();
|
||||
BlkA[k] = new BlockOperator(Aoffsets[k]);
|
||||
S[k-1] = new BlockOperator(Aoffsets[k]); // Smoother
|
||||
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
if (A[k](i,j) == NULL)
|
||||
{
|
||||
A[k - 1](i,j) = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
A[k - 1](i,j) = RAP(A[k](i,j), P[k - 1]);
|
||||
BlkA[k]->SetBlock(i, j, A[k](i,j), Acoef(i,j));
|
||||
}
|
||||
}
|
||||
|
||||
HypreSmoother *S_i = new HypreSmoother;
|
||||
S_i->SetType(HypreSmoother::Jacobi);
|
||||
S_i->SetOperator(*(A[k](i,i)));
|
||||
|
||||
S[k - 1]->SetBlock(i,i,S_i);
|
||||
}
|
||||
|
||||
Poffsets_i[k-1].SetSize(numBlocks+1); Poffsets_i[k-1][0] = 0;
|
||||
Poffsets_j[k-1].SetSize(numBlocks+1); Poffsets_j[k-1][0] = 0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Poffsets_i[k-1][i+1] = P[k-1]->Height();
|
||||
Poffsets_j[k-1][i+1] = P[k-1]->Width();
|
||||
}
|
||||
Poffsets_i[k-1].PartialSum();
|
||||
Poffsets_j[k-1].PartialSum();
|
||||
|
||||
BlkP[k-1] = new BlockOperator(Poffsets_i[k-1],Poffsets_j[k-1]);
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
BlkP[k-1]->SetBlock(i,i,P[k-1]);
|
||||
}
|
||||
}
|
||||
// Set up coarse solve operator
|
||||
// Convert the coarse grid blockmatrix to a HypreParMatrix
|
||||
Array<int> offsets(numBlocks+1);
|
||||
offsets[0]=0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
offsets[i+1]=A[0](i,i)->Height();
|
||||
}
|
||||
|
||||
offsets.PartialSum();
|
||||
|
||||
BlkA[0] = new BlockOperator(offsets);
|
||||
|
||||
Array2D<SparseMatrix*> Asp;
|
||||
//Array2D<double> Acoef;
|
||||
Asp.SetSize(numBlocks,numBlocks);
|
||||
//Acoef.SetSize(numBlocks,numBlocks);
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
if (A[0](i,j) != NULL)
|
||||
{
|
||||
BlkA[0]->SetBlock(i, j, A[0](i,j), Acoef(i,j));
|
||||
}
|
||||
|
||||
Asp(i,j) = NULL;
|
||||
//Acoef(i,j) = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to HypreParMatrix
|
||||
//HypreParMatrix * Ac;
|
||||
|
||||
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
|
||||
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
|
||||
|
||||
{
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
MPI_Comm_size(comm, &nprocs);
|
||||
|
||||
std::vector<int> allnumrows(nprocs);
|
||||
const int blockNumRows = A[0](0,
|
||||
0)->Height(); // TODO: Not valid if blocks are of different size
|
||||
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT, comm);
|
||||
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b].resize(nprocs);
|
||||
all_block_num_loc_rows[b].resize(nprocs);
|
||||
}
|
||||
|
||||
blockProcOffsets[0][0] = 0;
|
||||
for (int i=0; i<nprocs-1; ++i)
|
||||
{
|
||||
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
|
||||
}
|
||||
|
||||
for (int i=0; i<nprocs; ++i)
|
||||
{
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
all_block_num_loc_rows[b][i] = allnumrows[i];
|
||||
}
|
||||
|
||||
for (int b=1; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b][i] = blockProcOffsets[0][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ac = CreateHypreParMatrixFromBlocks2(comm, offsets, A[0], Asp,
|
||||
Acoef, blockProcOffsets, all_block_num_loc_rows);
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
invAc = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*Ac), comm);
|
||||
delete Ac;
|
||||
#else
|
||||
Ac->GetDiag(AcSp); // AcSp does not own the data
|
||||
UMFPackSolver *umf_solver = new UMFPackSolver();
|
||||
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver->SetOperator(AcSp);
|
||||
invAc = umf_solver;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void SetOperator(const Operator &op) {}
|
||||
|
||||
virtual void SetTheta(const double a) { theta = a; }
|
||||
|
||||
virtual void Mult(const Vector &r, Vector &z) const
|
||||
{
|
||||
// Residual vectors
|
||||
std::vector<Vector> rv(numGrids + 1);
|
||||
// correction vectors
|
||||
std::vector<Vector> zv(numGrids + 1);
|
||||
// allocation
|
||||
for (int i = 0; i <= numGrids ; i++)
|
||||
{
|
||||
int n = (i==0) ? invAc->Height(): BlkA[i]->Width();
|
||||
rv[i].SetSize(n);
|
||||
zv[i].SetSize(n);
|
||||
}
|
||||
// Initial residual
|
||||
rv[numGrids] = r;
|
||||
|
||||
// smooth and update residuals down to the coarsest level
|
||||
for (int i = numGrids; i > 0 ; i--)
|
||||
{
|
||||
// Pre smooth
|
||||
S[i - 1]->Mult(rv[i], zv[i]); zv[i] *= theta;
|
||||
// compute residual
|
||||
int n = BlkA[i]->Width();
|
||||
Vector w(n);
|
||||
BlkA[i]->Mult(zv[i], w);
|
||||
rv[i] -= w;
|
||||
// Restrict
|
||||
BlkP[i - 1]->MultTranspose(rv[i], rv[i - 1]);
|
||||
}
|
||||
|
||||
// Coarse grid Solve
|
||||
invAc->Mult(rv[0], zv[0]);
|
||||
//
|
||||
for (int i = 1; i <= numGrids ; i++)
|
||||
{
|
||||
// Prolong correction
|
||||
Vector u(BlkP[i - 1]->Height());
|
||||
BlkP[i - 1]->Mult(zv[i - 1], u);
|
||||
// Update correction
|
||||
zv[i] += u;
|
||||
// Update residual
|
||||
Vector v(BlkA[i]->Height());
|
||||
BlkA[i]->Mult(u, v); rv[i] -= v;
|
||||
// Post smooth
|
||||
S[i - 1]->Mult(rv[i], v); v *= theta;
|
||||
// Update correction
|
||||
zv[i] += v;
|
||||
}
|
||||
z = zv[numGrids];
|
||||
}
|
||||
|
||||
virtual ~BlockMGSolver()
|
||||
{
|
||||
for (int i = numGrids - 1; i >= 0 ; i--)
|
||||
{
|
||||
delete S[i];
|
||||
delete BlkP[i];
|
||||
delete BlkA[i];
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
for (int k=0; k<numBlocks; k++)
|
||||
{
|
||||
delete A[i](j,k);
|
||||
}
|
||||
}
|
||||
A[i].DeleteAll();
|
||||
}
|
||||
delete BlkA[numGrids];
|
||||
delete invAc;
|
||||
A.clear();
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
STRUMPACKSolver* CreateStrumpackSolver(Operator *Arow, MPI_Comm comm)
|
||||
{
|
||||
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, comm);
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, comm);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
return strumpack;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#define SWTIMING
|
||||
#define SERIAL_PROLONGATION
|
||||
#define ITERATIVE_COARSE_SOLVE
|
||||
#define SPARSE_JACOBI
|
||||
//#define SPARSE_ICHOLESKY
|
||||
//#define SPARSE_ILU
|
||||
//#define COARSE_PA
|
||||
//#define BLOCK_DIAG
|
||||
|
||||
//#define COARSE_AMS
|
||||
|
||||
class BlockMGPASolver : public Solver
|
||||
{
|
||||
private:
|
||||
/// The linear system matrix
|
||||
Array2D<Operator*>&
|
||||
Af; // TODO: remove this, as it is used only in the constructor
|
||||
Array2D<double>&
|
||||
Acoef; // TODO: remove this, as it is used only in the constructor
|
||||
vector<Array<int>> Aoffsets;
|
||||
vector<Array<int>> Poffsets_i;
|
||||
vector<Array<int>> Poffsets_j;
|
||||
Array<int> Coffsets;
|
||||
|
||||
std::vector<Array2D<Operator*>> A;
|
||||
#ifdef SERIAL_PROLONGATION
|
||||
std::vector<SparseMatrix *> P;
|
||||
#else
|
||||
std::vector<HypreParMatrix *> const& P;
|
||||
#endif
|
||||
std::vector<Vector*> const& diag;
|
||||
std::vector<Operator*> Pt;
|
||||
std::vector<BlockOperator *> BlkP;
|
||||
std::vector<BlockOperator *> BlkA;
|
||||
std::vector<BlockOperator *> S;
|
||||
std::vector<OperatorJacobiSmoother*> Jacobi;
|
||||
|
||||
HypreParMatrix * Ac;
|
||||
SparseMatrix AcSp;
|
||||
int numGrids, numBlocks;
|
||||
//STRUMPACKSolver *invAc = nullptr;
|
||||
Operator *invAc = nullptr;
|
||||
double theta = 0.5;
|
||||
|
||||
mutable std::vector<Vector> rv, zv;
|
||||
mutable Vector u, v, w;
|
||||
|
||||
Vector diagAc;
|
||||
Array<int> emptyEssDof;
|
||||
OperatorJacobiSmoother *JacobiAc;
|
||||
#ifdef SPARSE_ICHOLESKY
|
||||
Solver *iCholAc;
|
||||
#endif
|
||||
#ifdef SPARSE_ILU
|
||||
Solver *iluAc;
|
||||
#endif
|
||||
|
||||
#ifdef BLOCK_DIAG
|
||||
HypreParMatrix *AcDiag;
|
||||
SparseMatrix AcDiagSp;
|
||||
#endif
|
||||
|
||||
public:
|
||||
BlockMGPASolver(MPI_Comm comm, const int height, const int width,
|
||||
Array2D<Operator*>& Af_, Array2D<double>& Acoef_,
|
||||
#ifdef COARSE_PA
|
||||
BlockOperator *BlkAc,
|
||||
#else
|
||||
Array2D<HypreParMatrix*> const& BlkAc,
|
||||
#endif
|
||||
#ifdef COARSE_AMS
|
||||
ParFiniteElementSpace* coarseFespace,
|
||||
#endif
|
||||
std::vector<HypreParMatrix *> const& P_, std::vector<Vector*> const& diag_,
|
||||
Array<int>& ess_tdof_list)
|
||||
#ifdef SERIAL_PROLONGATION
|
||||
: Solver(height, width), Af(Af_), Acoef(Acoef_), diag(diag_)
|
||||
#else
|
||||
: Solver(height, width), Af(Af_), Acoef(Acoef_), P(P_), diag(diag_)
|
||||
#endif
|
||||
{
|
||||
timeMult = 0.0;
|
||||
timeMultAc = 0.0;
|
||||
timeMultPresmooth = 0.0;
|
||||
timeMultResidual = 0.0;
|
||||
timeMultRestrict = 0.0;
|
||||
|
||||
#ifdef SERIAL_PROLONGATION
|
||||
P.resize(P_.size());
|
||||
for (int i=0; i<P_.size(); ++i)
|
||||
{
|
||||
P[i] = new SparseMatrix();
|
||||
P_[i]->GetDiag(*P[i]);
|
||||
P[i]->BuildTranspose();
|
||||
}
|
||||
#endif
|
||||
|
||||
numBlocks = Af.NumRows();
|
||||
MFEM_VERIFY(Af.NumCols() == numBlocks, "");
|
||||
#ifndef COARSE_PA
|
||||
MFEM_VERIFY(BlkAc.NumCols() == numBlocks && BlkAc.NumRows() == numBlocks, "");
|
||||
#endif
|
||||
numGrids = P.size();
|
||||
MFEM_VERIFY(diag.size() == numBlocks, "");
|
||||
BlkP.resize(numGrids);
|
||||
BlkA.resize(numGrids+1);
|
||||
Pt.resize(numGrids);
|
||||
S.resize(numGrids);
|
||||
A.resize(numGrids + 1);
|
||||
A[numGrids] = Af;
|
||||
Aoffsets.resize(numGrids+1);
|
||||
Poffsets_i.resize(numGrids);
|
||||
Poffsets_j.resize(numGrids);
|
||||
|
||||
Jacobi.resize(numBlocks);
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
Jacobi[i] = new OperatorJacobiSmoother(*(diag[i]), ess_tdof_list, 1.0);
|
||||
}
|
||||
|
||||
// Construct Bilinear form Matrices on each level
|
||||
for (int k = numGrids ; k > 0; k--)
|
||||
{
|
||||
A[k - 1].SetSize(numBlocks,numBlocks);
|
||||
Aoffsets[k].SetSize(numBlocks+1); Aoffsets[k][0] = 0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Aoffsets[k][i+1] = A[k](i,i)->Height();
|
||||
}
|
||||
|
||||
Aoffsets[k].PartialSum();
|
||||
BlkA[k] = new BlockOperator(Aoffsets[k]);
|
||||
S[k-1] = new BlockOperator(Aoffsets[k]); // Smoother
|
||||
Pt[k - 1] = new TransposeOperator(P[k - 1]);
|
||||
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
if (A[k](i,j) == NULL)
|
||||
{
|
||||
A[k - 1](i,j) = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
//A[k - 1](i,j) = RAP(A[k](i,j), P[k - 1]);
|
||||
A[k - 1](i,j) = new TripleProductOperator(Pt[k - 1], A[k](i,j), P[k - 1], false,
|
||||
false, false);
|
||||
BlkA[k]->SetBlock(i, j, A[k](i,j), Acoef(i,j));
|
||||
}
|
||||
}
|
||||
|
||||
if (k == numGrids) // finest level
|
||||
{
|
||||
S[k - 1]->SetBlock(i,i,Jacobi[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
S[k - 1]->SetBlock(i,i, new TripleProductOperator(Pt[k], &(S[k]->GetBlock(i,i)),
|
||||
P[k], false,
|
||||
false, false)); // TODO: get a diagonal on each level
|
||||
}
|
||||
}
|
||||
|
||||
Poffsets_i[k-1].SetSize(numBlocks+1); Poffsets_i[k-1][0] = 0;
|
||||
Poffsets_j[k-1].SetSize(numBlocks+1); Poffsets_j[k-1][0] = 0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Poffsets_i[k-1][i+1] = P[k-1]->Height();
|
||||
Poffsets_j[k-1][i+1] = P[k-1]->Width();
|
||||
}
|
||||
Poffsets_i[k-1].PartialSum();
|
||||
Poffsets_j[k-1].PartialSum();
|
||||
|
||||
BlkP[k-1] = new BlockOperator(Poffsets_i[k-1],Poffsets_j[k-1]);
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
BlkP[k-1]->SetBlock(i,i,P[k-1]);
|
||||
}
|
||||
}
|
||||
// Set up coarse solve operator
|
||||
// Convert the coarse grid blockmatrix to a HypreParMatrix
|
||||
Coffsets.SetSize(numBlocks+1);
|
||||
Coffsets[0]=0;
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
Coffsets[i+1]=A[0](i,i)->Height();
|
||||
#ifndef COARSE_PA
|
||||
MFEM_VERIFY(BlkAc(i,i)->Height() == A[0](i,i)->Height(), "");
|
||||
MFEM_VERIFY(BlkAc(i,i)->Width() == A[0](i,i)->Width(), "");
|
||||
#endif
|
||||
}
|
||||
Coffsets.PartialSum();
|
||||
|
||||
#ifdef COARSE_PA
|
||||
BlkA[0] = BlkAc;
|
||||
#else
|
||||
BlkA[0] = new BlockOperator(Coffsets);
|
||||
#endif
|
||||
|
||||
Array2D<SparseMatrix*> Asp;
|
||||
//Array2D<double> Acoef;
|
||||
Asp.SetSize(numBlocks,numBlocks);
|
||||
//Acoef.SetSize(numBlocks,numBlocks);
|
||||
for (int i=0; i<numBlocks; i++)
|
||||
{
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
#ifndef COARSE_PA
|
||||
MFEM_VERIFY((A[0](i,j) == NULL) == (BlkAc(i,j) == NULL), "");
|
||||
|
||||
if (BlkAc(i,j) != NULL)
|
||||
{
|
||||
BlkA[0]->SetBlock(i, j, BlkAc(i,j), Acoef(i,j));
|
||||
}
|
||||
#endif
|
||||
|
||||
Asp(i,j) = NULL;
|
||||
//Acoef(i,j) = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef COARSE_PA
|
||||
CGSolver *cg_solver = new CGSolver();
|
||||
cg_solver->SetAbsTol(1.0e-6);
|
||||
cg_solver->SetRelTol(1.0e-6);
|
||||
cg_solver->SetMaxIter(1000);
|
||||
cg_solver->SetOperator(*BlkAc);
|
||||
cg_solver->SetPrintLevel(0);
|
||||
cg_solver->iterative_mode = false;
|
||||
invAc = cg_solver;
|
||||
#else
|
||||
// Convert to HypreParMatrix
|
||||
HypreParMatrix * Ac;
|
||||
|
||||
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
|
||||
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
|
||||
|
||||
{
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
MPI_Comm_size(comm, &nprocs);
|
||||
|
||||
std::vector<int> allnumrows(nprocs);
|
||||
// TODO: Not valid if blocks are of different size
|
||||
const int blockNumRows = BlkAc(0,0)->Height();
|
||||
|
||||
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT, comm);
|
||||
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b].resize(nprocs);
|
||||
all_block_num_loc_rows[b].resize(nprocs);
|
||||
}
|
||||
|
||||
blockProcOffsets[0][0] = 0;
|
||||
for (int i=0; i<nprocs-1; ++i)
|
||||
{
|
||||
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
|
||||
}
|
||||
|
||||
for (int i=0; i<nprocs; ++i)
|
||||
{
|
||||
for (int b=0; b<numBlocks; ++b)
|
||||
{
|
||||
all_block_num_loc_rows[b][i] = allnumrows[i];
|
||||
}
|
||||
|
||||
for (int b=1; b<numBlocks; ++b)
|
||||
{
|
||||
blockProcOffsets[b][i] = blockProcOffsets[0][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ac = CreateHypreParMatrixFromBlocks2(comm, Coffsets, BlkAc, Asp,
|
||||
Acoef, blockProcOffsets, all_block_num_loc_rows);
|
||||
|
||||
#ifdef BLOCK_DIAG
|
||||
{
|
||||
Array2D<HypreParMatrix*> BlkAcDiag(numBlocks, numBlocks);
|
||||
Array2D<double> DiagCoef(numBlocks, numBlocks);
|
||||
DiagCoef = 0.0;
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
DiagCoef(i,i) = Acoef(i,i);
|
||||
for (int j=0; j<numBlocks; ++j)
|
||||
{
|
||||
BlkAcDiag(i,j) = NULL;
|
||||
}
|
||||
|
||||
BlkAcDiag(i,i) = BlkAc(i,i);
|
||||
}
|
||||
|
||||
AcDiag = CreateHypreParMatrixFromBlocks2(comm, Coffsets, BlkAcDiag, Asp,
|
||||
DiagCoef, blockProcOffsets, all_block_num_loc_rows);
|
||||
|
||||
AcDiag->GetDiag(AcDiagSp);
|
||||
//delete AcDiag;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
invAc = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*Ac), comm);
|
||||
delete Ac;
|
||||
#else
|
||||
Ac->GetDiag(AcSp); // AcSp does not own the data
|
||||
delete Ac;
|
||||
|
||||
#ifdef ITERATIVE_COARSE_SOLVE
|
||||
//CGSolver *cg_solver = new CGSolver(comm);
|
||||
CGSolver *cg_solver = new CGSolver();
|
||||
cg_solver->SetAbsTol(1.0e-6);
|
||||
cg_solver->SetRelTol(1.0e-6);
|
||||
cg_solver->SetMaxIter(1000);
|
||||
cg_solver->SetOperator(AcSp);
|
||||
cg_solver->SetPrintLevel(0);
|
||||
cg_solver->iterative_mode = false;
|
||||
#ifdef SPARSE_JACOBI
|
||||
AcSp.GetDiag(diagAc);
|
||||
JacobiAc = new OperatorJacobiSmoother(diagAc, emptyEssDof);
|
||||
cg_solver->SetPreconditioner(*JacobiAc);
|
||||
#endif
|
||||
#ifdef SPARSE_ICHOLESKY
|
||||
{
|
||||
Vector tmpX(AcSp.Height());
|
||||
Vector tmpY(AcSp.Height());
|
||||
tmpX = 1.0;
|
||||
tmpY = 0.0;
|
||||
#ifdef BLOCK_DIAG
|
||||
AcDiagSp.Finalize();
|
||||
AcDiagSp.SortColumnIndices();
|
||||
|
||||
AcDiagSp.Mult(tmpX, tmpY);
|
||||
#else
|
||||
AcSp.Finalize();
|
||||
AcSp.SortColumnIndices();
|
||||
|
||||
AcSp.Mult(tmpX, tmpY);
|
||||
#endif
|
||||
}
|
||||
#ifdef BLOCK_DIAG
|
||||
iCholAc = new IncompleteCholesky(AcDiagSp);
|
||||
#else
|
||||
iCholAc = new IncompleteCholesky(AcSp);
|
||||
#endif
|
||||
cg_solver->SetPreconditioner(*iCholAc);
|
||||
cg_solver->SetPrintLevel(-1);
|
||||
#endif // SPARSE_ICHOLESKY
|
||||
|
||||
#ifdef SPARSE_ILU
|
||||
{
|
||||
Vector tmpX(AcSp.Height());
|
||||
Vector tmpY(AcSp.Height());
|
||||
tmpX = 1.0;
|
||||
tmpY = 0.0;
|
||||
#ifdef BLOCK_DIAG
|
||||
AcDiagSp.Finalize();
|
||||
AcDiagSp.SortColumnIndices();
|
||||
|
||||
AcDiagSp.Mult(tmpX, tmpY);
|
||||
#else
|
||||
AcSp.Finalize();
|
||||
AcSp.SortColumnIndices();
|
||||
|
||||
AcSp.Mult(tmpX, tmpY);
|
||||
#endif
|
||||
}
|
||||
#ifdef BLOCK_DIAG
|
||||
iluAc = new ILUcusparse(AcDiagSp);
|
||||
#else
|
||||
iluAc = new ILUcusparse(AcSp);
|
||||
#endif
|
||||
cg_solver->SetPreconditioner(*iluAc);
|
||||
cg_solver->SetPrintLevel(0);
|
||||
#endif // SPARSE_ILU
|
||||
|
||||
#ifdef COARSE_AMS
|
||||
#ifndef COARSE_PA
|
||||
MFEM_VERIFY(numBlocks == 4, "");
|
||||
// TODO: just set 2 AMS solvers for E and H.
|
||||
|
||||
if (coarseFespace != NULL)
|
||||
{
|
||||
BlockDiagonalPreconditioner *blkAMS = new BlockDiagonalPreconditioner(Coffsets);
|
||||
|
||||
for (int i=0; i<numBlocks; ++i)
|
||||
{
|
||||
HypreAMS *ams = new HypreAMS(*BlkAc(i,i), coarseFespace);
|
||||
blkAMS->SetDiagonalBlock(i, ams);
|
||||
}
|
||||
|
||||
cg_solver->SetPreconditioner(*blkAMS);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
invAc = cg_solver;
|
||||
#else
|
||||
UMFPackSolver *umf_solver = new UMFPackSolver();
|
||||
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver->SetOperator(AcSp);
|
||||
invAc = umf_solver;
|
||||
#endif
|
||||
#endif
|
||||
#endif // COARSE_PA
|
||||
|
||||
// Residual vectors
|
||||
rv.resize(numGrids + 1);
|
||||
// correction vectors
|
||||
zv.resize(numGrids + 1);
|
||||
// allocation
|
||||
for (int i = 0; i <= numGrids ; i++)
|
||||
{
|
||||
int n = (i==0) ? invAc->Height(): BlkA[i]->Width();
|
||||
|
||||
rv[i].SetSize(n);
|
||||
zv[i].SetSize(n);
|
||||
rv[i].UseDevice(true);
|
||||
zv[i].UseDevice(true);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetOperator(const Operator &op) {}
|
||||
|
||||
virtual void SetTheta(const double a) { theta = a; }
|
||||
|
||||
virtual void Mult(const Vector &r, Vector &z) const
|
||||
{
|
||||
#ifdef SWTIMING
|
||||
StopWatch sw;
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
#endif
|
||||
|
||||
// Initial residual
|
||||
rv[numGrids] = r;
|
||||
|
||||
// smooth and update residuals down to the coarsest level
|
||||
for (int i = numGrids; i > 0 ; i--)
|
||||
{
|
||||
// Pre smooth
|
||||
#ifdef SWTIMING
|
||||
StopWatch sws;
|
||||
sws.Clear();
|
||||
sws.Start();
|
||||
#endif
|
||||
|
||||
S[i - 1]->Mult(rv[i], zv[i]); zv[i] *= theta;
|
||||
|
||||
#ifdef SWTIMING
|
||||
sws.Stop();
|
||||
timeMultPresmooth += sws.RealTime();
|
||||
#endif
|
||||
|
||||
// compute residual
|
||||
int n = BlkA[i]->Width();
|
||||
w.SetSize(n);
|
||||
w.UseDevice(true);
|
||||
#ifdef SWTIMING
|
||||
StopWatch swop;
|
||||
swop.Clear();
|
||||
swop.Start();
|
||||
#endif
|
||||
BlkA[i]->Mult(zv[i], w);
|
||||
rv[i] -= w;
|
||||
#ifdef SWTIMING
|
||||
swop.Stop();
|
||||
timeMultResidual += swop.RealTime();
|
||||
#endif
|
||||
// Restrict
|
||||
#ifdef SWTIMING
|
||||
StopWatch swr;
|
||||
swr.Clear();
|
||||
swr.Start();
|
||||
#endif
|
||||
BlkP[i - 1]->MultTranspose(rv[i], rv[i - 1]);
|
||||
#ifdef SWTIMING
|
||||
swr.Stop();
|
||||
timeMultRestrict += swr.RealTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SWTIMING
|
||||
StopWatch swAc;
|
||||
swAc.Clear();
|
||||
swAc.Start();
|
||||
#endif
|
||||
|
||||
// Coarse grid Solve
|
||||
invAc->Mult(rv[0], zv[0]);
|
||||
//
|
||||
|
||||
#ifdef SWTIMING
|
||||
swAc.Stop();
|
||||
timeMultAc += swAc.RealTime();
|
||||
#endif
|
||||
|
||||
for (int i = 1; i <= numGrids ; i++)
|
||||
{
|
||||
// Prolong correction
|
||||
u.SetSize(BlkP[i - 1]->Height());
|
||||
u.UseDevice(true);
|
||||
|
||||
BlkP[i - 1]->Mult(zv[i - 1], u);
|
||||
// Update correction
|
||||
zv[i] += u;
|
||||
// Update residual
|
||||
v.SetSize(BlkA[i]->Height());
|
||||
v.UseDevice(true);
|
||||
|
||||
BlkA[i]->Mult(u, v); rv[i] -= v;
|
||||
// Post smooth
|
||||
S[i - 1]->Mult(rv[i], v); v *= theta;
|
||||
// Update correction
|
||||
zv[i] += v;
|
||||
}
|
||||
z = zv[numGrids];
|
||||
|
||||
#ifdef SWTIMING
|
||||
sw.Stop();
|
||||
timeMult += sw.RealTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~BlockMGPASolver()
|
||||
{
|
||||
for (int i = numGrids - 1; i >= 0 ; i--)
|
||||
{
|
||||
delete S[i];
|
||||
delete BlkP[i];
|
||||
delete BlkA[i];
|
||||
for (int j=0; j<numBlocks; j++)
|
||||
{
|
||||
for (int k=0; k<numBlocks; k++)
|
||||
{
|
||||
delete A[i](j,k);
|
||||
}
|
||||
}
|
||||
A[i].DeleteAll();
|
||||
}
|
||||
delete BlkA[numGrids];
|
||||
delete invAc;
|
||||
A.clear();
|
||||
|
||||
#ifdef SPARSE_ICHOLESKY
|
||||
delete iCholAc;
|
||||
#endif
|
||||
#ifdef SPARSE_ILU
|
||||
delete iluAc;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
STRUMPACKSolver* CreateStrumpackSolver(Operator *Arow, MPI_Comm comm)
|
||||
{
|
||||
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, comm);
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, comm);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
return strumpack;
|
||||
}
|
||||
#endif
|
||||
|
||||
mutable double timeMult, timeMultAc, timeMultPresmooth, timeMultResidual,
|
||||
timeMultRestrict;
|
||||
};
|
||||
|
||||
}
|
||||
//} // namespace mfem
|
||||
|
||||
#endif // BGMULTIGRID
|
||||
@@ -0,0 +1,906 @@
|
||||
// MFEM Example 9
|
||||
//
|
||||
// Compile with: make serial_nogpu
|
||||
//
|
||||
// Description: This code solves the time-dependent advection-diffusion
|
||||
// equation:
|
||||
// \frac(\partial u}{\partial t}
|
||||
// = \mathbf{a} \cdot \Nabla u - \nu \Nabla^2 u
|
||||
// where a is a given advection velocity, \nu is the diffusion
|
||||
// parameter, and u0(x) = u(0,x) is a given initial condition.
|
||||
//
|
||||
// The demonstrates explicit time marching with H1 elements of
|
||||
// arbitrary order. Periodic boundary conditions are used through
|
||||
// periodic meshes. GLVis can be used for visualization of a
|
||||
// time-evolving solution.
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "mpi.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The weak
|
||||
form of du/dt = -a.grad(u) + nu Delta(u) is M du/dt = K u + b, where M and
|
||||
K are the mass and advection-diffusion matrices, and b describes the flow
|
||||
on the boundary. This can be written as a general ODE,
|
||||
du/dt = M^{-1} (K u + b), and this class is used to evaluate the right-hand
|
||||
side. */
|
||||
class AdvectionDiffusionEvolution : public mfem::TimeDependentOperator
|
||||
{
|
||||
public:
|
||||
/// \param[in] M - bilinear form for mass matrix
|
||||
/// \param[in] K - bilinear form for stiffness matrix
|
||||
/// \param[in] b - load vector
|
||||
AdvectionDiffusionEvolution(mfem::BilinearForm &M, mfem::BilinearForm &K,
|
||||
const mfem::Vector &b);
|
||||
|
||||
/// Perform the action of the operator: y = k = f(x, t), where k solves
|
||||
/// Compute k = M^-1(Kx + l)
|
||||
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
|
||||
|
||||
/// Solve the implicit equation: k = f(x + dt k, t), for the unknown k at
|
||||
/// the current time t.
|
||||
void ImplicitSolve(const double dt, const mfem::Vector &x,
|
||||
mfem::Vector &k) override;
|
||||
|
||||
virtual ~AdvectionDiffusionEvolution();
|
||||
|
||||
private:
|
||||
mfem::BilinearForm &M, &K;
|
||||
const mfem::Vector &b;
|
||||
/// solver for inverting mass matrix for explicit time-marching
|
||||
std::unique_ptr<mfem::Solver> M_prec;
|
||||
mfem::CGSolver M_solver;
|
||||
/// solver for implicit time-marching
|
||||
mfem::GSSmoother prec;
|
||||
mfem::GMRESSolver linear_solver;
|
||||
mfem::NewtonSolver newton;
|
||||
|
||||
mutable mfem::Vector z;
|
||||
|
||||
/// pointer-to-implementation idiom
|
||||
/// Hides implementation details of this operator
|
||||
class SystemOperator;
|
||||
/// Operator that combines the linear spatial discretization with
|
||||
/// the load vector into one operator used for implicit solves
|
||||
std::unique_ptr<SystemOperator> combined_oper;
|
||||
|
||||
/// sets the state and dt for the combined operator
|
||||
/// \param[in] dt - time increment
|
||||
/// \param[in] x - the current state
|
||||
void setOperParameters(double dt, const mfem::Vector *x);
|
||||
|
||||
};
|
||||
|
||||
class PAJacobianOperator : public mfem::Operator
|
||||
{
|
||||
public:
|
||||
PAJacobianOperator(mfem::ParBilinearForm &_mass,
|
||||
mfem::ParBilinearForm &_stiff);
|
||||
|
||||
/// Compute r = J@k = M@k + dt*K@k
|
||||
/// \param[in] k - dx/dt
|
||||
/// \param[out] r - J@k = M@k + dt*K@k
|
||||
void Mult(const mfem::Vector &k, mfem::Vector &r) const override;
|
||||
|
||||
/// Set current dt values - needed to compute action of Jacobian.
|
||||
void setParameters(double dt);
|
||||
|
||||
private:
|
||||
mfem::ParBilinearForm &mass;
|
||||
mfem::ParBilinearForm &stiff;
|
||||
|
||||
double dt;
|
||||
};
|
||||
|
||||
class ParSystemOperator : public mfem::Operator
|
||||
{
|
||||
public:
|
||||
/// Nonlinear operator of the form that combines the mass, res, stiff,
|
||||
/// and load elements for implicit/explicit ODE integration
|
||||
/// \param[in] ess_bdr - array of boundaries attributes marked essential
|
||||
/// \param[in] mass - bilinear form for mass matrix (not owned)
|
||||
/// \param[in] res - nonlinear residual operator (not owned)
|
||||
/// \param[in] stiff - bilinear form for stiffness matrix (not owned)
|
||||
/// \param[in] load - load vector (not owned)
|
||||
/// \param[in] a - used to move the spatial residual to the rhs
|
||||
ParSystemOperator(mfem::ParBilinearForm &_mass,
|
||||
mfem::ParBilinearForm &_stiff);
|
||||
|
||||
/// Compute r = M@k + K@(x+dt*k)
|
||||
/// (with `@` denoting matrix-vector multiplication)
|
||||
/// \param[in] k - dx/dt
|
||||
/// \param[out] r - the residual
|
||||
/// \note the signs on each operator must be accounted for elsewhere
|
||||
void Mult(const mfem::Vector &k, mfem::Vector &r) const override;
|
||||
|
||||
/// Compute J = M + dt * K
|
||||
/// \param[in] k - dx/dt
|
||||
mfem::Operator &GetGradient(const mfem::Vector &k) const override;
|
||||
|
||||
/// Set current dt and x values - needed to compute action and Jacobian.
|
||||
void setParameters(double _dt, const mfem::Vector *_x);
|
||||
|
||||
~ParSystemOperator();
|
||||
|
||||
private:
|
||||
mfem::ParBilinearForm &mass;
|
||||
mfem::ParBilinearForm &stiff;
|
||||
mutable mfem::HypreParMatrix *jacobian, *stiff_jacobian;
|
||||
|
||||
double dt;
|
||||
const mfem::Vector *x;
|
||||
|
||||
mutable mfem::Vector work, work2;
|
||||
|
||||
std::unique_ptr<PAJacobianOperator> pa_jac;
|
||||
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The weak
|
||||
form of du/dt = -a.grad(u) + nu Delta(u) is M du/dt = K u + b, where M and
|
||||
K are the mass and advection-diffusion matrices, and b describes the flow
|
||||
on the boundary. This can be written as a general ODE,
|
||||
du/dt = M^{-1} (K u + b), and this class is used to evaluate the right-hand
|
||||
side. */
|
||||
class ParAdvectionDiffusionEvolution : public mfem::TimeDependentOperator
|
||||
{
|
||||
public:
|
||||
/// \param[in] M - parallel bilinear form for mass matrix
|
||||
/// \param[in] K - parallel bilinear form for stiffness matrix
|
||||
ParAdvectionDiffusionEvolution(mfem::ParBilinearForm &M,
|
||||
mfem::ParBilinearForm &K);
|
||||
|
||||
/// Perform the action of the operator: y = k = f(x, t), where k solves
|
||||
/// Compute k = M^-1(Kx + l)
|
||||
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
|
||||
|
||||
/// Solve the implicit equation: k = f(x + dt k, t), for the unknown k at
|
||||
/// the current time t.
|
||||
void ImplicitSolve(const double dt, const mfem::Vector &x,
|
||||
mfem::Vector &k) override;
|
||||
|
||||
virtual ~ParAdvectionDiffusionEvolution();
|
||||
|
||||
private:
|
||||
mfem::OperatorHandle M_;
|
||||
mfem::ParBilinearForm &M, &K;
|
||||
/// solver for inverting mass matrix for explicit time-marching
|
||||
std::unique_ptr<mfem::Solver> M_prec;
|
||||
mfem::CGSolver M_solver;
|
||||
/// solver for implicit time-marching
|
||||
mfem::Solver *prec;
|
||||
mfem::GMRESSolver linear_solver;
|
||||
mfem::NewtonSolver newton;
|
||||
|
||||
mfem::Vector diag;
|
||||
mutable mfem::Vector z, work, work2;
|
||||
|
||||
/// pointer-to-implementation idiom
|
||||
/// Hides implementation details of this operator
|
||||
/// Operator that combines the linear spatial discretization with
|
||||
/// the load vector into one operator used for implicit solves
|
||||
std::unique_ptr<ParSystemOperator> combined_oper;
|
||||
|
||||
/// sets the state and dt for the combined operator
|
||||
/// \param[in] dt - time increment
|
||||
/// \param[in] x - the current state
|
||||
void setOperParameters(double dt, const mfem::Vector *x);
|
||||
|
||||
};
|
||||
|
||||
// Choice for the problem setup. The fluid velocity, initial condition and
|
||||
// inflow boundary condition are chosen based on this parameter.
|
||||
int problem;
|
||||
|
||||
// Velocity coefficient
|
||||
void velocity_function(const Vector &X, Vector &v);
|
||||
|
||||
// Initial condition
|
||||
double u0_function(const Vector &X);
|
||||
|
||||
// Inflow boundary condition
|
||||
double inflow_function(const Vector &X, const double t);
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
problem = 3;
|
||||
const char *mesh_file = "../data/periodic-square.mesh";
|
||||
int ser_ref_levels = 0;
|
||||
int par_ref_levels = 0;
|
||||
int order = 3;
|
||||
const char *device_config = "cpu";
|
||||
int ode_solver_type = 22;
|
||||
double t_final = 3 * 2*M_PI;
|
||||
double dt = 0.01;
|
||||
bool glvis = false;
|
||||
bool paraview = false;
|
||||
int vis_steps = 5;
|
||||
|
||||
double nu_val = 0.001;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&glvis, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview-datafiles", "-no-paraview",
|
||||
"--no-paraview-datafiles",
|
||||
"Save data files for ParaView (paraview.org) visualization.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.AddOption(&nu_val, "-nu", "--nu-value",
|
||||
"Value for \nu, the parameter that controls diffusion.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
std::cout << "Num ranks: " << num_procs << "\n";
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 3. Read the serial mesh from the given mesh file on all processors. We can
|
||||
// handle geometrically periodic meshes in this code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter. If the mesh is of NURBS type, we convert it
|
||||
// to a (piecewise-polynomial) high-order mesh.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 7. Define the finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
H1_FECollection fec(order, dim, BasisType::GaussLobatto);
|
||||
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
|
||||
|
||||
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of unknowns: " << global_vSize << endl;
|
||||
}
|
||||
|
||||
// 8. Set up and assemble the bilinear and linear forms corresponding to the
|
||||
// CG discretization.
|
||||
/// negative to move the diffusion terms to the right side
|
||||
ConstantCoefficient nu(-nu_val);
|
||||
ConstantCoefficient one(1.0);
|
||||
VectorFunctionCoefficient velocity(dim, velocity_function);
|
||||
FunctionCoefficient u0(u0_function);
|
||||
|
||||
ParBilinearForm *m_pa = new ParBilinearForm(fes);
|
||||
ParBilinearForm *k_pa = new ParBilinearForm(fes);
|
||||
m_pa->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
k_pa->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
|
||||
/// create mass matrix
|
||||
m_pa->AddDomainIntegrator(new MassIntegrator(one));
|
||||
/// add advection terms to stiffness matrix
|
||||
k_pa->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
/// add diffusion terms to stiffness matrix
|
||||
k_pa->AddDomainIntegrator(new DiffusionIntegrator(nu));
|
||||
|
||||
m_pa->Assemble();
|
||||
int skip_zeros = 0;
|
||||
k_pa->Assemble(skip_zeros);
|
||||
m_pa->Finalize();
|
||||
k_pa->Finalize(skip_zeros);
|
||||
|
||||
ParBilinearForm *m = new ParBilinearForm(fes);
|
||||
ParBilinearForm *k = new ParBilinearForm(fes);
|
||||
/// create mass matrix
|
||||
m->AddDomainIntegrator(new MassIntegrator);
|
||||
/// add advection terms to stiffness matrix
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
/// add diffusion terms to stiffness matrix
|
||||
k->AddDomainIntegrator(new DiffusionIntegrator(nu));
|
||||
|
||||
m->Assemble();
|
||||
k->Assemble(skip_zeros);
|
||||
m->Finalize();
|
||||
k->Finalize(skip_zeros);
|
||||
|
||||
|
||||
ParGridFunction *u = new ParGridFunction(fes);
|
||||
u->UseDevice(true);
|
||||
u->ProjectCoefficient(u0);
|
||||
|
||||
|
||||
HypreParVector *U = u->GetTrueDofs();
|
||||
|
||||
ParSystemOperator pso(*m, *k);
|
||||
ParSystemOperator pso_pa(*m_pa, *k_pa);
|
||||
|
||||
pso.setParameters(dt, U);
|
||||
pso_pa.setParameters(dt, U);
|
||||
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
mfem::Vector pso_r(U->Size());
|
||||
double t1 = MPI_Wtime();
|
||||
pso.Mult(*U, pso_r);
|
||||
double t2 = MPI_Wtime();
|
||||
double fa_mult_time = t2 - t1;
|
||||
double average_fa_mult_time;
|
||||
MPI_Reduce(&fa_mult_time, &average_fa_mult_time, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
std::cout << "FA Mult time: " << average_fa_mult_time / num_procs << endl;
|
||||
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
mfem::Vector pso_pa_r(U->Size());
|
||||
double t3 = MPI_Wtime();
|
||||
pso_pa.Mult(*U, pso_pa_r);
|
||||
double t4 = MPI_Wtime();
|
||||
double pa_mult_time = t4 - t3;
|
||||
double average_pa_mult_time;
|
||||
MPI_Reduce(&pa_mult_time, &average_pa_mult_time, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
std::cout << "FA Mult time: " << average_pa_mult_time / num_procs << endl;
|
||||
|
||||
double local_mult_speedup = (t2-t1) / (t4-t3);
|
||||
double global_mult_speedup;
|
||||
MPI_Reduce(&local_mult_speedup, &global_mult_speedup, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
|
||||
if (myid == 0)
|
||||
std::cout << "PA mult speedup: " << global_mult_speedup / num_procs << endl;
|
||||
|
||||
mfem::Vector diff_r(pso_pa_r);
|
||||
diff_r -= pso_r;
|
||||
// std::cout << "r diff: " << diff_r.Norml2() << std::endl;
|
||||
|
||||
mfem::Operator &pso_jac = pso.GetGradient(*U);
|
||||
mfem::Operator &pso_pa_jac = pso_pa.GetGradient(*U);
|
||||
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
mfem::Vector pso_jac_r(U->Size());
|
||||
double t5 = MPI_Wtime();
|
||||
pso_jac.Mult(*U, pso_jac_r);
|
||||
double t6 = MPI_Wtime();
|
||||
double fa_jac_mult_time = t6-t5;
|
||||
double average_fa_jac_time;
|
||||
MPI_Reduce(&fa_jac_mult_time, &average_fa_jac_time, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
std::cout << "FA Jac Mult time: " << average_fa_jac_time / num_procs << endl;
|
||||
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
mfem::Vector pso_pa_jac_r(U->Size());
|
||||
double t7 = MPI_Wtime();
|
||||
pso_pa_jac.Mult(*U, pso_pa_jac_r);
|
||||
double t8 = MPI_Wtime();
|
||||
double pa_jac_mult_time = t8-t7;
|
||||
double average_pa_jac_time;
|
||||
MPI_Reduce(&pa_jac_mult_time, &average_pa_jac_time, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
std::cout << "PA Jac Mult time: " << average_pa_jac_time / num_procs << endl;
|
||||
|
||||
double local_jac_speedup = (t6-t5) / (t8-t7);
|
||||
double global_jac_speedup;
|
||||
MPI_Reduce(&local_jac_speedup, &global_jac_speedup, 1,
|
||||
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
std::cout << "PA Jac mult speedup: " << global_jac_speedup / num_procs << endl;
|
||||
|
||||
// 13. Free the used memory.
|
||||
delete U;
|
||||
delete u;
|
||||
delete k;
|
||||
delete m;
|
||||
delete fes;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Velocity coefficient
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
double center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 3:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const double w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const double w = M_PI/2;
|
||||
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial condition
|
||||
double u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
double center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
|
||||
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
double x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const double f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Inflow boundary condition (zero for the problems considered in this example)
|
||||
double inflow_function(const Vector &x, const double t)
|
||||
{
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3: return 0.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
class AdvectionDiffusionEvolution::SystemOperator : public mfem::Operator
|
||||
{
|
||||
public:
|
||||
/// Nonlinear operator of the form that combines the mass, res, stiff,
|
||||
/// and load elements for implicit/explicit ODE integration
|
||||
/// \param[in] mass - bilinear form for mass matrix (not owned)
|
||||
/// \param[in] res - nonlinear residual operator (not owned)
|
||||
/// \param[in] stiff - bilinear form for stiffness matrix (not owned)
|
||||
/// \param[in] load - load vector (not owned)
|
||||
/// \param[in] a - used to move the spatial residual to the rhs
|
||||
SystemOperator(BilinearForm &_mass, BilinearForm &_stiff,
|
||||
const mfem::Vector &b)
|
||||
: Operator(_mass.Height()), mass(_mass), stiff(_stiff),
|
||||
load(b), Jacobian(NULL), dt(0.0), x(NULL), work(height)
|
||||
{ }
|
||||
|
||||
/// Compute r = M@k + K@(x+dt*k) + l
|
||||
/// (with `@` denoting matrix-vector multiplication)
|
||||
/// \param[in] k - dx/dt
|
||||
/// \param[out] r - the residual
|
||||
/// \note the signs on each operator must be accounted for elsewhere
|
||||
void Mult(const mfem::Vector &k, mfem::Vector &r) const override
|
||||
{
|
||||
/// work = x+dt*k = x+dt*dx/dt = x+dx
|
||||
add(1.0, *x, dt, k, work);
|
||||
r = 0.0;
|
||||
stiff.AddMult(work, r);
|
||||
r += load;
|
||||
mass.AddMult(k, r, -1.0);
|
||||
}
|
||||
|
||||
/// Compute J = M + dt * K
|
||||
/// \param[in] k - dx/dt
|
||||
mfem::Operator &GetGradient(const mfem::Vector &k) const override
|
||||
{
|
||||
delete Jacobian;
|
||||
Jacobian = Add(-1.0, mass.SpMat(), dt, stiff.SpMat());
|
||||
return *Jacobian;
|
||||
}
|
||||
|
||||
/// Set current dt and x values - needed to compute action and Jacobian.
|
||||
void setParameters(double _dt, const mfem::Vector *_x)
|
||||
{
|
||||
dt = _dt;
|
||||
x = _x;
|
||||
};
|
||||
|
||||
~SystemOperator() {delete Jacobian;};
|
||||
|
||||
private:
|
||||
BilinearForm &mass;
|
||||
BilinearForm &stiff;
|
||||
const mfem::Vector &load;
|
||||
mutable mfem::SparseMatrix *Jacobian;
|
||||
|
||||
double dt;
|
||||
const mfem::Vector *x;
|
||||
|
||||
mutable mfem::Vector work, work2;
|
||||
|
||||
};
|
||||
|
||||
AdvectionDiffusionEvolution::AdvectionDiffusionEvolution(
|
||||
BilinearForm &_M, BilinearForm &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b),
|
||||
z(_M.Height())
|
||||
{
|
||||
bool pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
|
||||
Array<int> ess_tdof_list;
|
||||
if (pa)
|
||||
{
|
||||
M_prec.reset(new OperatorJacobiSmoother(M, ess_tdof_list));
|
||||
M_solver.SetOperator(M);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_prec.reset(new DSmoother(M.SpMat()));
|
||||
M_solver.SetOperator(M.SpMat());
|
||||
}
|
||||
|
||||
combined_oper.reset(new SystemOperator(_M, _K, _b));
|
||||
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
|
||||
linear_solver.iterative_mode = true;
|
||||
linear_solver.SetRelTol(1e-12);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(100);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
linear_solver.SetPreconditioner(prec);
|
||||
|
||||
newton.iterative_mode = false;
|
||||
newton.SetRelTol(1e-9);
|
||||
newton.SetAbsTol(0.0);
|
||||
newton.SetMaxIter(100);
|
||||
newton.SetPrintLevel(-1);
|
||||
newton.SetSolver(linear_solver);
|
||||
newton.SetOperator(*combined_oper);
|
||||
}
|
||||
|
||||
void AdvectionDiffusionEvolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (K x + b)
|
||||
K.Mult(x, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void AdvectionDiffusionEvolution::ImplicitSolve(const double dt,
|
||||
const Vector &x,
|
||||
Vector &k)
|
||||
{
|
||||
setOperParameters(dt, &x);
|
||||
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
|
||||
newton.Mult(zero, k);
|
||||
MFEM_VERIFY(newton.GetConverged(), "Newton solver did not converge!");
|
||||
}
|
||||
|
||||
void AdvectionDiffusionEvolution::setOperParameters(double dt,
|
||||
const mfem::Vector *x)
|
||||
{
|
||||
combined_oper->setParameters(dt, x);
|
||||
}
|
||||
|
||||
AdvectionDiffusionEvolution::~AdvectionDiffusionEvolution() {}
|
||||
|
||||
|
||||
PAJacobianOperator::PAJacobianOperator(ParBilinearForm &_mass, ParBilinearForm &_stiff)
|
||||
: Operator(_mass.ParFESpace()->GetTrueVSize()), mass(_mass), stiff(_stiff),
|
||||
dt(0.0) { }
|
||||
|
||||
|
||||
void PAJacobianOperator::Mult(const mfem::Vector &k, mfem::Vector &r) const
|
||||
{
|
||||
r.UseDevice(true);
|
||||
r = 0.0;
|
||||
stiff.TrueAddMult(k, r, dt);
|
||||
mass.TrueAddMult(k, r, -1.0);
|
||||
}
|
||||
|
||||
void PAJacobianOperator::setParameters(const double _dt)
|
||||
{
|
||||
dt = _dt;
|
||||
};
|
||||
|
||||
ParSystemOperator::ParSystemOperator(ParBilinearForm &_mass, ParBilinearForm &_stiff)
|
||||
: Operator(_mass.ParFESpace()->GetTrueVSize()), mass(_mass), stiff(_stiff),
|
||||
jacobian(NULL), stiff_jacobian(NULL), dt(0.0), x(NULL),
|
||||
work(height)
|
||||
{
|
||||
pa_jac.reset(new PAJacobianOperator(mass, stiff));
|
||||
}
|
||||
|
||||
/// Compute r = M@k + K@(x+dt*k)
|
||||
/// (with `@` denoting matrix-vector multiplication)
|
||||
/// \param[in] k - dx/dt
|
||||
/// \param[out] r - the residual
|
||||
/// \note the signs on each operator must be accounted for elsewhere
|
||||
void ParSystemOperator::Mult(const mfem::Vector &k, mfem::Vector &r) const
|
||||
{
|
||||
r = 0.0;
|
||||
work.UseDevice(true);
|
||||
work = 0.0;
|
||||
/// work = x+dt*k = x+dt*dx/dt = x+dx
|
||||
if (x)
|
||||
{
|
||||
add(1.0, *x, dt, k, work);
|
||||
}
|
||||
|
||||
stiff.TrueAddMult(work, r);
|
||||
mass.TrueAddMult(k, r, -1.0);
|
||||
}
|
||||
|
||||
/// Compute J = M + dt * K
|
||||
/// \param[in] k - dx/dt
|
||||
mfem::Operator &ParSystemOperator::GetGradient(const mfem::Vector &k) const
|
||||
{
|
||||
bool mass_pa = mass.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
|
||||
bool stiff_pa = stiff.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
|
||||
|
||||
if (mass_pa && stiff_pa)
|
||||
{
|
||||
return *pa_jac.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete stiff_jacobian;
|
||||
delete jacobian;
|
||||
jacobian = mass.ParallelAssemble();
|
||||
*jacobian *= -1.0; //alpha;
|
||||
stiff_jacobian = stiff.ParallelAssemble();
|
||||
jacobian->Add(dt, *stiff_jacobian);
|
||||
return *jacobian;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set current dt and x values - needed to compute action and Jacobian.
|
||||
void ParSystemOperator::setParameters(const double _dt, const mfem::Vector *_x)
|
||||
{
|
||||
dt = _dt;
|
||||
x = _x;
|
||||
pa_jac->setParameters(_dt);
|
||||
};
|
||||
|
||||
ParSystemOperator::~ParSystemOperator()
|
||||
{
|
||||
delete jacobian;
|
||||
delete stiff_jacobian;
|
||||
};
|
||||
|
||||
ParAdvectionDiffusionEvolution::ParAdvectionDiffusionEvolution(
|
||||
ParBilinearForm &_M, ParBilinearForm &_K)
|
||||
: TimeDependentOperator(_M.ParFESpace()->GetTrueVSize()), M(_M), K(_K), z(_M.Height())
|
||||
{
|
||||
bool mass_pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
|
||||
bool stiff_pa = K.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
M_solver = CGSolver(MPI_COMM_WORLD);
|
||||
if (mass_pa)
|
||||
{
|
||||
M_prec.reset(new OperatorJacobiSmoother(M, ess_tdof_list));
|
||||
M_solver.SetOperator(M);
|
||||
}
|
||||
else
|
||||
{
|
||||
M_.Reset(_M.ParallelAssemble(), true);
|
||||
|
||||
// M_prec.reset(new HypreSmoother());
|
||||
// M_solver.SetOperator(M.As<HypreParMatrix>());
|
||||
HypreParMatrix &M_mat = *M_.As<HypreParMatrix>();
|
||||
// HypreParMatrix &K_mat = *K.As<HypreParMatrix>();
|
||||
M_prec.reset(new HypreSmoother(M_mat, HypreSmoother::Jacobi));
|
||||
}
|
||||
|
||||
combined_oper.reset(new ParSystemOperator(_M, _K));
|
||||
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
|
||||
if (mass_pa && stiff_pa)
|
||||
{
|
||||
diag.UseDevice(true);
|
||||
diag.SetSize(M.ParFESpace()->GetTrueVSize());
|
||||
diag = 0.0;
|
||||
work.UseDevice(true);
|
||||
work2.UseDevice(true);
|
||||
work.SetSize(M.ParFESpace()->GetTrueVSize());
|
||||
work2.SetSize(M.ParFESpace()->GetTrueVSize());
|
||||
work = 0.0;
|
||||
work2 = 0.0;
|
||||
M.AssembleDiagonal(work);
|
||||
|
||||
ParBilinearForm k(M.ParFESpace());
|
||||
ConstantCoefficient nu(-0.01);
|
||||
k.AddDomainIntegrator(new mfem::DiffusionIntegrator(nu));
|
||||
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
k.Assemble(0);
|
||||
k.Finalize(0);
|
||||
k.AssembleDiagonal(work2);
|
||||
|
||||
double dt = 0.1;
|
||||
add(-1.0, work, dt, work2, diag);
|
||||
|
||||
prec = new OperatorChebyshevSmoother(combined_oper.get(), diag,
|
||||
ess_tdof_list, 5,
|
||||
M.ParFESpace()->GetComm());
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreSmoother();
|
||||
}
|
||||
|
||||
linear_solver = GMRESSolver(MPI_COMM_WORLD);
|
||||
linear_solver.iterative_mode = true;
|
||||
linear_solver.SetRelTol(1e-12);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(2000);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
linear_solver.SetPreconditioner(*prec);
|
||||
linear_solver.SetKDim(2000);
|
||||
|
||||
newton.iterative_mode = true;
|
||||
newton.SetRelTol(1e-9);
|
||||
newton.SetAbsTol(0.0);
|
||||
newton.SetMaxIter(10);
|
||||
newton.SetPrintLevel(-1);
|
||||
newton.SetSolver(linear_solver);
|
||||
newton.SetOperator(*combined_oper);
|
||||
}
|
||||
|
||||
void ParAdvectionDiffusionEvolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (K x + b)
|
||||
K.Mult(x, z);
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void ParAdvectionDiffusionEvolution::ImplicitSolve(const double dt,
|
||||
const Vector &x,
|
||||
Vector &k)
|
||||
{
|
||||
setOperParameters(dt, &x);
|
||||
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
|
||||
newton.Mult(zero, k);
|
||||
MFEM_VERIFY(newton.GetConverged(), "Newton solver did not converge!");
|
||||
}
|
||||
|
||||
void ParAdvectionDiffusionEvolution::setOperParameters(const double dt,
|
||||
const mfem::Vector *x)
|
||||
{
|
||||
combined_oper->setParameters(dt, x);
|
||||
}
|
||||
|
||||
ParAdvectionDiffusionEvolution::~ParAdvectionDiffusionEvolution() {delete prec;}
|
||||
@@ -34,15 +34,6 @@ if (MFEM_USE_MPI)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_SLEPC)
|
||||
list(APPEND PETSC_EXAMPLES_SRCS
|
||||
ex11p.cpp
|
||||
)
|
||||
list(APPEND PETSC_RC_FILES
|
||||
rc_ex11p_lobpcg rc_ex11p_gd
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
|
||||
@@ -87,22 +78,12 @@ set(EX9_E_ARGS -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts
|
||||
set(EX9_ES_ARGS -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts rc_ex9p_expl --no-step)
|
||||
set(EX9_IS_ARGS -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts rc_ex9p_impl --implicit -tf 0.5)
|
||||
set(EX10_ARGS -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p -tf 30 -s 3 -rs 2 -dt 3)
|
||||
if (MFEM_USE_SLEPC)
|
||||
set(EX11_ARGS_SINV -m ../../data/star.mesh --useslepc)
|
||||
set(EX11_ARGS_LOBPCG -m ../../data/star.mesh --useslepc --slepcopts rc_ex11p_lobpcg)
|
||||
set(EX11_ARGS_GD -m ../../data/star.mesh --useslepc --slepcopts rc_ex11p_gd)
|
||||
endif()
|
||||
|
||||
# Add the tests: one test per command-line-variable.
|
||||
set(TEST_OPTIONS_VARS
|
||||
EX1_ARGS_W EX1_ARGS_P EX2_ARGS EX3_ARGS EX4_ARGS EX4_HYB_ARGS
|
||||
EX5_BDDC_LB_ARGS EX5_BDDC_GB_ARGS EX5_FSPL_ARGS EX6_ARGS EX6_NONOVL_ARGS
|
||||
EX9_E_ARGS EX9_ES_ARGS EX9_IS_ARGS EX10_ARGS)
|
||||
if (MFEM_USE_SLEPC)
|
||||
list(APPEND TEST_OPTIONS_VARS EX11_ARGS_SINV EX11_ARGS_LOBPCG EX11_ARGS_GD)
|
||||
endif()
|
||||
|
||||
foreach(TEST_OPTIONS_VAR ${TEST_OPTIONS_VARS})
|
||||
foreach(TEST_OPTIONS_VAR
|
||||
EX1_ARGS_W EX1_ARGS_P EX2_ARGS EX3_ARGS EX4_ARGS EX4_HYB_ARGS
|
||||
EX5_BDDC_LB_ARGS EX5_BDDC_GB_ARGS EX5_FSPL_ARGS EX6_ARGS EX6_NONOVL_ARGS
|
||||
EX9_E_ARGS EX9_ES_ARGS EX9_IS_ARGS EX10_ARGS)
|
||||
string(REGEX REPLACE "^(.+)_ARGS" "\\1" TEST_NAME_UC ${TEST_OPTIONS_VAR})
|
||||
string(REGEX REPLACE "^([^_]+)" "\\1P" TEST_NAME_UC ${TEST_NAME_UC})
|
||||
string(TOLOWER ${TEST_NAME_UC} TEST_NAME_FULL)
|
||||
|
||||
@@ -1,440 +0,0 @@
|
||||
// MFEM Example 11 - Parallel Version
|
||||
// PETSc Modification
|
||||
//
|
||||
// Compile with: make ex11p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex11p -m ../../data/star.mesh
|
||||
// mpirun -np 4 ex11p -m ../../data/star.mesh --slepcopts rc_ex11p_lobpcg
|
||||
// mpirun -np 4 ex11p -m ../../data/star.mesh --slepcopts rc_ex11p_gd
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve the
|
||||
// eigenvalue problem -Delta u = lambda u with homogeneous
|
||||
// Dirichlet boundary conditions.
|
||||
//
|
||||
// We compute a number of the lowest eigenmodes by discretizing
|
||||
// the Laplacian and Mass operators using a FE space of the
|
||||
// specified order, or an isoparametric/isogeometric space if
|
||||
// order < 1 (quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example demonstrates the use of the SLEPc eigensolver as an
|
||||
// alternative to the LOBPCG eigenvalue solver. The shift and
|
||||
// invert spectral transformation is used to help the convergence
|
||||
// to the smaller eigenvalues. Alternative solver parameters can
|
||||
// be passed in a file with "-slepcopts".
|
||||
//
|
||||
// Reusing a single GLVis visualization window for multiple
|
||||
// eigenfunctions is also illustrated.
|
||||
//
|
||||
// We recommend viewing Example 1 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef MFEM_USE_SLEPC
|
||||
#error This examples requires that MFEM is build with MFEM_USE_SLEPC=YES
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 1;
|
||||
int order = 1;
|
||||
int nev = 5;
|
||||
int seed = 75;
|
||||
bool slu_solver = false;
|
||||
bool sp_solver = false;
|
||||
bool visualization = 1;
|
||||
bool use_slepc = true;
|
||||
const char *slepcrc_file = "";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&nev, "-n", "--num-eigs",
|
||||
"Number of desired eigenmodes.");
|
||||
args.AddOption(&seed, "-s", "--seed",
|
||||
"Random seed used to initialize LOBPCG.");
|
||||
#ifdef MFEM_USE_SUPERLU
|
||||
args.AddOption(&slu_solver, "-slu", "--superlu", "-no-slu",
|
||||
"--no-superlu", "Use the SuperLU Solver.");
|
||||
#endif
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
args.AddOption(&sp_solver, "-sp", "--strumpack", "-no-sp",
|
||||
"--no-strumpack", "Use the STRUMPACK Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&use_slepc, "-useslepc","--useslepc","-no-slepc",
|
||||
"--no-slepc","Use or not SLEPc to solve the eigenvalue problem");
|
||||
args.AddOption(&slepcrc_file, "-slepcopts", "--slepcopts",
|
||||
"SlepcOptions file to use.");
|
||||
args.Parse();
|
||||
if (slu_solver && sp_solver)
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "WARNING: Both SuperLU and STRUMPACK have been selected,"
|
||||
<< " please choose either one." << endl
|
||||
<< " Defaulting to SuperLU." << endl;
|
||||
sp_solver = false;
|
||||
}
|
||||
// The command line options are also passed to the STRUMPACK
|
||||
// solver. So do not exit if some options are not recognized.
|
||||
if (!sp_solver)
|
||||
{
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 2b. We initialize SLEPc. This internally initializes PETSc as well.
|
||||
MFEMInitializeSlepc(NULL,NULL,slepcrc_file,NULL);
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement (2 by default, or
|
||||
// specified on the command line with -rs).
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution (1 time by
|
||||
// default, or specified on the command line with -rp). Once the parallel
|
||||
// mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else if (pmesh->GetNodes())
|
||||
{
|
||||
fec = pmesh->GetNodes()->OwnFEC();
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
|
||||
// element space. The first corresponds to the Laplacian operator -Delta,
|
||||
// while the second is a simple mass matrix needed on the right hand side
|
||||
// of the generalized eigenvalue problem below. The boundary conditions
|
||||
// are implemented by elimination with special values on the diagonal to
|
||||
// shift the Dirichlet eigenvalues out of the computational range. After
|
||||
// serial and parallel assembly we extract the corresponding parallel
|
||||
// matrices A and M.
|
||||
ConstantCoefficient one(1.0);
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
}
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
if (pmesh->bdr_attributes.Size() == 0)
|
||||
{
|
||||
// Add a mass term if the mesh has no boundary, e.g. periodic mesh or
|
||||
// closed surface.
|
||||
a->AddDomainIntegrator(new MassIntegrator(one));
|
||||
}
|
||||
a->Assemble();
|
||||
a->EliminateEssentialBCDiag(ess_bdr, 1.0);
|
||||
a->Finalize();
|
||||
|
||||
ParBilinearForm *m = new ParBilinearForm(fespace);
|
||||
m->AddDomainIntegrator(new MassIntegrator(one));
|
||||
m->Assemble();
|
||||
// shift the eigenvalue corresponding to eliminated dofs to a large value
|
||||
m->EliminateEssentialBCDiag(ess_bdr, numeric_limits<double>::min());
|
||||
m->Finalize();
|
||||
|
||||
PetscParMatrix *pA = NULL, *pM = NULL;
|
||||
HypreParMatrix *A = NULL, *M = NULL;
|
||||
Operator::Type tid =
|
||||
!use_slepc ? Operator::Hypre_ParCSR : Operator::PETSC_MATAIJ;
|
||||
OperatorHandle Ah(tid), Mh(tid);
|
||||
|
||||
a->ParallelAssemble(Ah);
|
||||
if (!use_slepc) { Ah.Get(A); }
|
||||
else { Ah.Get(pA); }
|
||||
Ah.SetOperatorOwner(false);
|
||||
|
||||
m->ParallelAssemble(Mh);
|
||||
if (!use_slepc) {Mh.Get(M); }
|
||||
else {Mh.Get(pM); }
|
||||
Mh.SetOperatorOwner(false);
|
||||
|
||||
#if defined(MFEM_USE_SUPERLU) || defined(MFEM_USE_STRUMPACK)
|
||||
Operator * Arow = NULL;
|
||||
#ifdef MFEM_USE_SUPERLU
|
||||
if (slu_solver)
|
||||
{
|
||||
Arow = new SuperLURowLocMatrix(*A);
|
||||
}
|
||||
#endif
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (sp_solver)
|
||||
{
|
||||
Arow = new STRUMPACKRowLocMatrix(*A);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
delete a;
|
||||
delete m;
|
||||
|
||||
// 8. Define and configure the LOBPCG eigensolver and the BoomerAMG
|
||||
// preconditioner for A to be used within the solver. Set the matrices
|
||||
// which define the generalized eigenproblem A x = lambda M x.
|
||||
Solver * precond = NULL;
|
||||
if (!use_slepc)
|
||||
{
|
||||
if (!slu_solver && !sp_solver)
|
||||
{
|
||||
HypreBoomerAMG * amg = new HypreBoomerAMG(*A);
|
||||
amg->SetPrintLevel(0);
|
||||
precond = amg;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_SUPERLU
|
||||
if (slu_solver)
|
||||
{
|
||||
SuperLUSolver * superlu = new SuperLUSolver(MPI_COMM_WORLD);
|
||||
superlu->SetPrintStatistics(false);
|
||||
superlu->SetSymmetricPattern(true);
|
||||
superlu->SetColumnPermutation(superlu::PARMETIS);
|
||||
superlu->SetOperator(*Arow);
|
||||
precond = superlu;
|
||||
}
|
||||
#endif
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (sp_solver)
|
||||
{
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->DisableMatching();
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
precond = strumpack;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
HypreLOBPCG * lobpcg = NULL;
|
||||
SlepcEigenSolver * slepc = NULL;
|
||||
if (!use_slepc)
|
||||
{
|
||||
|
||||
lobpcg = new HypreLOBPCG(MPI_COMM_WORLD);
|
||||
lobpcg->SetNumModes(nev);
|
||||
lobpcg->SetRandomSeed(seed);
|
||||
lobpcg->SetPreconditioner(*precond);
|
||||
lobpcg->SetMaxIter(200);
|
||||
lobpcg->SetTol(1e-8);
|
||||
lobpcg->SetPrecondUsageMode(1);
|
||||
lobpcg->SetPrintLevel(1);
|
||||
lobpcg->SetMassMatrix(*M);
|
||||
lobpcg->SetOperator(*A);
|
||||
}
|
||||
else
|
||||
{
|
||||
slepc = new SlepcEigenSolver(MPI_COMM_WORLD);
|
||||
slepc->SetNumModes(nev);
|
||||
slepc->SetWhichEigenpairs(SlepcEigenSolver::TARGET_REAL);
|
||||
slepc->SetTarget(0.0);
|
||||
slepc->SetSpectralTransformation(SlepcEigenSolver::SHIFT_INVERT);
|
||||
slepc->SetOperators(*pA,*pM);
|
||||
}
|
||||
|
||||
// 9. Compute the eigenmodes and extract the array of eigenvalues. Define a
|
||||
// parallel grid function to represent each of the eigenmodes returned by
|
||||
// the solver.
|
||||
Array<double> eigenvalues;
|
||||
if (!use_slepc)
|
||||
{
|
||||
lobpcg->Solve();
|
||||
lobpcg->GetEigenvalues(eigenvalues);
|
||||
}
|
||||
else
|
||||
{
|
||||
slepc->Solve();
|
||||
eigenvalues.SetSize(nev);
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
slepc->GetEigenvalue(i,eigenvalues[i]);
|
||||
}
|
||||
}
|
||||
Vector temp(fespace->GetTrueVSize());
|
||||
ParGridFunction x(fespace);
|
||||
|
||||
// 10. Save the refined mesh and the modes in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g mode".
|
||||
{
|
||||
ostringstream mesh_name, mode_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
// convert eigenvector from HypreParVector to ParGridFunction
|
||||
if (!use_slepc)
|
||||
{
|
||||
x = lobpcg->GetEigenvector(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
slepc->GetEigenvector(i,temp);
|
||||
x.Distribute(temp);
|
||||
|
||||
}
|
||||
|
||||
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
|
||||
<< setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mode_ofs(mode_name.str().c_str());
|
||||
mode_ofs.precision(8);
|
||||
x.Save(mode_ofs);
|
||||
mode_name.str("");
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream mode_sock(vishost, visport);
|
||||
mode_sock.precision(8);
|
||||
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
if ( myid == 0 )
|
||||
{
|
||||
cout << "Eigenmode " << i+1 << '/' << nev
|
||||
<< ", Lambda = " << eigenvalues[i] << endl;
|
||||
}
|
||||
|
||||
// convert eigenvector from HypreParVector to ParGridFunction
|
||||
if (!use_slepc)
|
||||
{
|
||||
x = lobpcg->GetEigenvector(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
slepc->GetEigenvector(i,temp);
|
||||
x.Distribute(temp);
|
||||
}
|
||||
|
||||
mode_sock << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pmesh << x << flush
|
||||
<< "window_title 'Eigenmode " << i+1 << '/' << nev
|
||||
<< ", Lambda = " << eigenvalues[i] << "'" << endl;
|
||||
|
||||
char c;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "press (q)uit or (c)ontinue --> " << flush;
|
||||
cin >> c;
|
||||
}
|
||||
MPI_Bcast(&c, 1, MPI_CHAR, 0, MPI_COMM_WORLD);
|
||||
|
||||
if (c != 'c')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
mode_sock.close();
|
||||
}
|
||||
|
||||
// 12. Free the used memory.
|
||||
if (!use_slepc)
|
||||
{
|
||||
delete lobpcg;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete slepc;
|
||||
}
|
||||
delete precond;
|
||||
delete M;
|
||||
delete A;
|
||||
#if defined(MFEM_USE_SUPERLU) || defined(MFEM_USE_STRUMPACK)
|
||||
delete Arow;
|
||||
#endif
|
||||
|
||||
delete fespace;
|
||||
if (order > 0)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
delete pmesh;
|
||||
|
||||
// We finalize SLEPc
|
||||
MFEMFinalizeSlepc();
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -23,9 +23,6 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
|
||||
SEQ_EXAMPLES =
|
||||
PAR_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex9p ex10p
|
||||
ifeq ($(MFEM_USE_SLEPC),YES)
|
||||
PAR_EXAMPLES += ex11p
|
||||
endif
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
@@ -90,9 +87,6 @@ EX10_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p
|
||||
EX10_MF_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_mf -tf 6 -s 3 -rs 0 -dt 3
|
||||
EX10_MFOP_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_mfop -tf 6 -s 3 -rs 0 -dt 3
|
||||
EX10_JFNK_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_jfnk --jfnk -tf 6 -s 3 -rs 0 -dt 3
|
||||
EX11_ARGS_SINV := -m ../../data/star.mesh --useslepc
|
||||
EX11_ARGS_LOBPCG := -m ../../data/star.mesh --useslepc --slepcopts rc_ex11p_lobpcg
|
||||
EX11_ARGS_GD := -m ../../data/star.mesh --useslepc --slepcopts rc_ex11p_gd
|
||||
|
||||
ex1p-test-par: ex1p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX1_ARGS_W))
|
||||
@@ -120,12 +114,6 @@ ex10p-test-par: ex10p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_MF_ARGS))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_MFOP_ARGS))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_JFNK_ARGS))
|
||||
ifeq ($(MFEM_USE_SLEPC),YES)
|
||||
ex11p-test-par: ex11p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX11_ARGS_SINV))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX11_ARGS_LOBPCG))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX11_ARGS_GD))
|
||||
endif
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# Options for the eigenvalue solver
|
||||
-eps_view
|
||||
-eps_converged_reason
|
||||
-eps_type gd
|
||||
# Options for the spectral transform
|
||||
-st_type precond
|
||||
@@ -1,11 +0,0 @@
|
||||
# Options for the eigenvalue solver
|
||||
-eps_monitor
|
||||
-eps_converged_reason
|
||||
-eps_view_values
|
||||
-eps_type lobpcg
|
||||
-eps_gen_hermitian
|
||||
-eps_smallest_real
|
||||
-eps_lobpcg_blocksize 5
|
||||
# Options for the spectral transform
|
||||
-st_type precond
|
||||
-st_pc_type gamg
|
||||
@@ -32,13 +32,6 @@
|
||||
// is used for the Finite Element order and "-go" is used for the
|
||||
// geometry order. Note that they can be used independently, i.e.
|
||||
// "-o 8 -go 3" solves for 8th order FE on a third order geometry.
|
||||
//
|
||||
// NOTE: Model/Mesh files for this example are in the (large) data file
|
||||
// repository of MFEM here https://github.com/mfem/data under the
|
||||
// folder named "pumi", which consists of the following sub-folders:
|
||||
// a) geom --> model files
|
||||
// b) parallel --> parallel pumi mesh files
|
||||
// c) serial --> serial pumi mesh files
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -36,14 +36,6 @@
|
||||
// option "-o" is used for the Finite Element order and "-go" for
|
||||
// the geometry order. Note that they can be used independently:
|
||||
// "-o 8 -go 3" solves for 8th order FE on third order geometry.
|
||||
//
|
||||
// NOTE: Model/Mesh files for this example are in the (large) data file
|
||||
// repository of MFEM here https://github.com/mfem/data under the
|
||||
// folder named "pumi", which consists of the following sub-folders:
|
||||
// a) geom --> model files
|
||||
// b) parallel --> parallel pumi mesh files
|
||||
// c) serial --> serial pumi mesh files
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -43,14 +43,6 @@
|
||||
// also illustrated.
|
||||
//
|
||||
// We recommend viewing Example 1 before viewing this example.
|
||||
//
|
||||
// NOTE: Model/Mesh files for this example are in the (large) data file
|
||||
// repository of MFEM here https://github.com/mfem/data under the
|
||||
// folder named "pumi", which consists of the following sub-folders:
|
||||
// a) geom --> model files
|
||||
// b) parallel --> parallel pumi mesh files
|
||||
// c) serial --> serial pumi mesh files
|
||||
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// MFEM Example 6 - Parallel Version
|
||||
// PUMI Modification
|
||||
//
|
||||
// Compile with: make ex6p
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 8 ex6p
|
||||
//
|
||||
@@ -18,13 +18,6 @@
|
||||
// is added to modify the "adapt_ratio" which is the fraction of
|
||||
// allowable error that scales the output size field of the error
|
||||
// estimator.
|
||||
//
|
||||
// NOTE: Model/Mesh files for this example are in the (large) data file
|
||||
// repository of MFEM here https://github.com/mfem/data under the
|
||||
// folder named "pumi", which consists of the following sub-folders:
|
||||
// a) geom --> model files
|
||||
// b) parallel --> parallel pumi mesh files
|
||||
// c) serial --> serial pumi mesh files
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
@@ -339,6 +332,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
apf::destroyField(Tmag_field);
|
||||
apf::destroyField(ipfield);
|
||||
apf::destroyNumbering(pumi_mesh->findNumbering("LocalVertexNumbering"));
|
||||
|
||||
// 18. Perform MesAdapt.
|
||||
ma::Input* erinput = ma::configure(pumi_mesh, sizefield);
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class HypreMat
|
||||
{
|
||||
private:
|
||||
std::vector<int> I, J;
|
||||
std::vector<double> d;
|
||||
int size;
|
||||
|
||||
public:
|
||||
HypreMat() : size(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Size() const { return size; }
|
||||
|
||||
void ReadMatrixParallel(std::string filename, const int np)
|
||||
{
|
||||
int lastRow = 0;
|
||||
int count = 0;
|
||||
|
||||
std::vector<int> R;
|
||||
|
||||
for (int p=0; p<np; ++p)
|
||||
{
|
||||
std::ifstream f(filename.c_str() + std::to_string(p), std::ifstream::in);
|
||||
|
||||
int rowFirst, rowLast, colFirst, colLast, row, col;
|
||||
double v;
|
||||
|
||||
f >> rowFirst >> rowLast >> colFirst >> colLast;
|
||||
|
||||
I.resize(rowLast+2);
|
||||
|
||||
for (row=rowFirst+1; row<=rowLast+1; ++row)
|
||||
{
|
||||
I[row] = 0;
|
||||
}
|
||||
|
||||
while (f.good())
|
||||
{
|
||||
f >> row >> col >> v;
|
||||
|
||||
if (row >= 2485)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
//if (row < lastRow) // verify ascending order of rows
|
||||
// abort();
|
||||
|
||||
lastRow = row;
|
||||
|
||||
I[row+1]++;
|
||||
R.push_back(row);
|
||||
J.push_back(col);
|
||||
d.push_back(v);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Partial sum of I
|
||||
|
||||
size = I.size() - 1;
|
||||
|
||||
I[0] = 0;
|
||||
for (int j=1; j<size; ++j)
|
||||
{
|
||||
I[j+1] += I[j];
|
||||
}
|
||||
|
||||
//int *Idata = I.data();
|
||||
|
||||
if (I[size] != J.size() || I[size] != d.size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
{
|
||||
// Reorder J and d
|
||||
const int nnz = J.size();
|
||||
if (nnz != d.size() || nnz != R.size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
std::vector<int> JJ(nnz);
|
||||
std::vector<double> dd(nnz);
|
||||
std::vector<int> cnt;
|
||||
|
||||
JJ = J;
|
||||
dd = d;
|
||||
|
||||
cnt.assign(I.size(), 0); // slightly larger than necessary
|
||||
|
||||
for (int j=0; j<nnz; ++j)
|
||||
{
|
||||
J[I[R[j]] + cnt[R[j]]] = JJ[j];
|
||||
d[I[R[j]] + cnt[R[j]]] = dd[j];
|
||||
cnt[R[j]]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyReorder(HypreMat const& A, std::vector<int> const& permRow,
|
||||
std::vector<int> const& permCol)
|
||||
{
|
||||
if (permRow.size() != A.Size() || permCol.size() != A.Size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
size = A.Size();
|
||||
I.resize(size+1);
|
||||
|
||||
I[0] = 0;
|
||||
|
||||
for (int j=0; j<size; ++j)
|
||||
{
|
||||
const std::size_t nnz_j = A.I[j+1] - A.I[j];
|
||||
I[permRow[j]+1] = nnz_j;
|
||||
}
|
||||
|
||||
for (int j=1; j<size; ++j)
|
||||
{
|
||||
I[j+1] += I[j];
|
||||
}
|
||||
|
||||
const std::size_t nnz = I[size];
|
||||
J.resize(nnz);
|
||||
d.resize(nnz);
|
||||
|
||||
std::vector<std::size_t> cnt;
|
||||
cnt.assign(size, 0);
|
||||
|
||||
for (int j=0; j<size; ++j)
|
||||
{
|
||||
const int pj = permRow[j];
|
||||
const std::size_t nnz_j = A.I[j+1] - A.I[j];
|
||||
|
||||
for (int k=0; k<nnz_j; ++k, cnt[pj]++)
|
||||
{
|
||||
J[I[pj] + cnt[pj]] = permCol[A.J[A.I[j] + k]];
|
||||
d[I[pj] + cnt[pj]] = A.d[A.I[j] + k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Print(std::string filename)
|
||||
{
|
||||
std::ofstream f(filename.c_str());
|
||||
|
||||
for (int j=0; j<size; ++j)
|
||||
{
|
||||
for (int k=I[j]; k<I[j+1]; ++k)
|
||||
{
|
||||
f << j << " " << J[k] << " " << d[k] << endl;
|
||||
}
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
void Compare(HypreMat const& A)
|
||||
{
|
||||
if (size != A.Size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
for (int j=0; j<size; ++j)
|
||||
{
|
||||
for (int k=I[j]; k<I[j+1]; ++k)
|
||||
{
|
||||
int m = -1;
|
||||
|
||||
for (int l=A.I[j]; l<A.I[j+1]; ++l)
|
||||
{
|
||||
if (A.J[l] == J[k])
|
||||
{
|
||||
m = l;
|
||||
}
|
||||
}
|
||||
|
||||
if (m < 0)
|
||||
{
|
||||
cout << "row " << j << " column " << J[k] << " not found" << endl;
|
||||
}
|
||||
//else if (fabs(A.d[m] - d[k]) > 1.0e-8)
|
||||
else if (fabs(fabs(A.d[m]) - fabs(d[k])) > 0.0)
|
||||
{
|
||||
cout << "entry (" << j << ", " << J[k] << ") " << d[k] << " != " << A.d[m] <<
|
||||
endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ReadCrd(std::string filename, std::vector<double>& crd)
|
||||
{
|
||||
std::ifstream f(filename.c_str(), std::ifstream::in);
|
||||
|
||||
double c;
|
||||
|
||||
while (f.good())
|
||||
{
|
||||
f >> c;
|
||||
crd.push_back(c);
|
||||
}
|
||||
|
||||
// for some reason, the last value gets pushed twice
|
||||
crd.pop_back();
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
void ReadCrdParallel(std::string filenamebase, const int np,
|
||||
std::vector<double>& crd)
|
||||
{
|
||||
for (int p=0; p<np; ++p)
|
||||
{
|
||||
ReadCrd(filenamebase + std::to_string(p),
|
||||
crd); // results get concatenated in crd
|
||||
}
|
||||
}
|
||||
|
||||
// crd is the DOF coordinates in serial, crdp in parallel.
|
||||
void SerialToParallelDOFMap(std::vector<double> const& crd,
|
||||
std::vector<double> const& crdp,
|
||||
std::vector<int>& sp)
|
||||
{
|
||||
if (crdp.size() != crd.size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
const int dim = 3;
|
||||
const int n = crd.size() / dim; // number of DOF's
|
||||
|
||||
sp.resize(n);
|
||||
|
||||
const double tol = 1.0e-8;
|
||||
|
||||
// Simple O(n^2) search
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
sp[i] = -1;
|
||||
|
||||
for (int j=0; j<n; ++j)
|
||||
{
|
||||
bool eq = true;
|
||||
for (int l=0; l<dim; ++l)
|
||||
{
|
||||
if (fabs(crd[(dim*i)+l] - crdp[(dim*j)+l]) > tol)
|
||||
{
|
||||
eq = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (eq)
|
||||
{
|
||||
if (sp[i] != -1)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
sp[i] = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (sp[i] < 0)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int np = 2;
|
||||
|
||||
std::vector<double> crd0, crdp0, crd1, crdp1;
|
||||
|
||||
ReadCrd("dofcrd0Ser", crd0);
|
||||
ReadCrd("dofcrd1Ser", crd1);
|
||||
|
||||
const int numCrd = crd0.size() / 3;
|
||||
|
||||
if (3*numCrd != crd0.size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
//for (int i=7440; i<crd.size(); ++i)
|
||||
//cout << "crd[" << i << "] " << crd[i] << endl;
|
||||
|
||||
ReadCrdParallel("dofcrd0Par", np, crdp0);
|
||||
ReadCrdParallel("dofcrd1Par", np, crdp1);
|
||||
|
||||
if (3*numCrd != crdp0.size())
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
std::vector<int> sp0, sp1; // serial to parallel DOF map for a subdomain
|
||||
SerialToParallelDOFMap(crd0, crdp0, sp0);
|
||||
SerialToParallelDOFMap(crd1, crdp1, sp1);
|
||||
|
||||
/*
|
||||
for (int i=0; i<sp.size(); ++i)
|
||||
cout << i << " " << sp[i] << endl;
|
||||
*/
|
||||
|
||||
cout << "sp0" << endl;
|
||||
for (int i=0; i<sp0.size(); ++i)
|
||||
{
|
||||
cout << i << " " << sp0[i] << endl;
|
||||
}
|
||||
|
||||
cout << "sp1" << endl;
|
||||
for (int i=0; i<sp1.size(); ++i)
|
||||
{
|
||||
cout << i << " " << sp1[i] << endl;
|
||||
}
|
||||
|
||||
HypreMat Aser, Apar, AserToPar;
|
||||
|
||||
/*
|
||||
Aser.ReadMatrixParallel("HypreAsdComplexIm1_Serial.0000", 1);
|
||||
Apar.ReadMatrixParallel("HypreAsdComplexIm1_Par5.0000", np);
|
||||
*/
|
||||
|
||||
Aser.ReadMatrixParallel("ifopSer.0000", 1);
|
||||
Apar.ReadMatrixParallel("ifopPar.0000", np);
|
||||
|
||||
//AserToPar.CopyReorder(Aser, sp, sp);
|
||||
AserToPar.CopyReorder(Aser, sp0, sp1);
|
||||
|
||||
/*
|
||||
Aser.Print("rbSer");
|
||||
AserToPar.Print("rbSerToPar");
|
||||
Apar.Print("rbPar");
|
||||
*/
|
||||
|
||||
cout << "Comparing " << endl;
|
||||
|
||||
AserToPar.Compare(Apar);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
#ifndef TESTSTRUMPACK_HPP
|
||||
#define TESTSTRUMPACK_HPP
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
|
||||
|
||||
void TestStrumpackConstructor()
|
||||
{
|
||||
int num_procs, rank;
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
||||
|
||||
const int num_loc_rows = 100;
|
||||
const int first_loc_row = num_loc_rows * rank;
|
||||
const int glob_nrows = num_loc_rows * num_procs;
|
||||
const int glob_ncols = glob_nrows;
|
||||
|
||||
int *opI = new int[num_loc_rows+1];
|
||||
|
||||
for (int i=0; i<num_loc_rows+1; ++i)
|
||||
{
|
||||
opI[i] = 0;
|
||||
}
|
||||
|
||||
for (int i=0; i<num_loc_rows; ++i)
|
||||
{
|
||||
int nnz_i = 3;
|
||||
|
||||
if ((first_loc_row + i) == 0 ||
|
||||
(first_loc_row + i) == glob_nrows-1) // if first or last row
|
||||
{
|
||||
nnz_i = 2;
|
||||
}
|
||||
|
||||
opI[i+1] = opI[i] + nnz_i;
|
||||
}
|
||||
|
||||
const int nnz = opI[num_loc_rows];
|
||||
|
||||
int *opJ = new int[nnz];
|
||||
double *data = new double[nnz];
|
||||
|
||||
int cnt = 0;
|
||||
for (int i=0; i<num_loc_rows; ++i)
|
||||
{
|
||||
const int globalRow = first_loc_row + i;
|
||||
|
||||
// Diagonal entry
|
||||
|
||||
opJ[cnt] = first_loc_row + i;
|
||||
|
||||
if (globalRow == 0 || globalRow == glob_nrows-1)
|
||||
{
|
||||
data[cnt] = 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
data[cnt] = 3.0;
|
||||
}
|
||||
|
||||
cnt++;
|
||||
|
||||
// Upper diagonal
|
||||
if (globalRow < glob_nrows-1)
|
||||
{
|
||||
opJ[cnt] = first_loc_row + i + 1;
|
||||
data[cnt] = -1.0;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
// Lower diagonal
|
||||
if (globalRow > 0)
|
||||
{
|
||||
opJ[cnt] = first_loc_row + i - 1;
|
||||
data[cnt] = -1.0;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
Operator *op = new STRUMPACKRowLocMatrix(MPI_COMM_WORLD, num_loc_rows,
|
||||
first_loc_row, glob_nrows, glob_ncols, opI, opJ, data);
|
||||
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, MPI_COMM_WORLD);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->SetOperator(*op);
|
||||
strumpack->SetFromCommandLine();
|
||||
|
||||
Vector x(num_loc_rows);
|
||||
Vector y(num_loc_rows);
|
||||
|
||||
x = 1.0;
|
||||
strumpack->Mult(x, y);
|
||||
|
||||
delete opI;
|
||||
delete opJ;
|
||||
delete data;
|
||||
}
|
||||
|
||||
#endif // TESTSTRUMPACK_HPP
|
||||
+4
-13
@@ -13,25 +13,17 @@ set(SRCS
|
||||
bilinearform.cpp
|
||||
bilinearform_ext.cpp
|
||||
bilininteg.cpp
|
||||
bilininteg_convection_pa.cpp
|
||||
bilininteg_convection_ea.cpp
|
||||
bilininteg_dgtrace_pa.cpp
|
||||
bilininteg_dgtrace_ea.cpp
|
||||
bilininteg_diffusion_pa.cpp
|
||||
bilininteg_diffusion_ea.cpp
|
||||
bilininteg_convection.cpp
|
||||
bilininteg_dgtrace.cpp
|
||||
bilininteg_diffusion.cpp
|
||||
bilininteg_divergence.cpp
|
||||
bilininteg_hcurl.cpp
|
||||
bilininteg_hdiv.cpp
|
||||
bilininteg_vectorfe.cpp
|
||||
bilininteg_gradient.cpp
|
||||
bilininteg_mass_pa.cpp
|
||||
bilininteg_mass_ea.cpp
|
||||
bilininteg_transpose_ea.cpp
|
||||
bilininteg_mass.cpp
|
||||
bilininteg_vecdiffusion.cpp
|
||||
bilininteg_vecmass.cpp
|
||||
coefficient.cpp
|
||||
complex_fem.cpp
|
||||
convergence.cpp
|
||||
datacollection.cpp
|
||||
eltrans.cpp
|
||||
estimators.cpp
|
||||
@@ -66,7 +58,6 @@ set(HDRS
|
||||
bilininteg.hpp
|
||||
coefficient.hpp
|
||||
complex_fem.hpp
|
||||
convergence.hpp
|
||||
datacollection.hpp
|
||||
eltrans.hpp
|
||||
estimators.hpp
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
#include "adios2datacollection.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -89,4 +87,4 @@ noexcept
|
||||
|
||||
} //end namespace mfem
|
||||
|
||||
#endif // MFEM_USE_ADIOS2
|
||||
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
#define MFEM_ADIOS2DATACOLLECTION
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
|
||||
#include "../general/adios2stream.hpp"
|
||||
#include "datacollection.hpp"
|
||||
|
||||
@@ -88,6 +85,4 @@ private:
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_ADIOS2
|
||||
|
||||
#endif /* MFEM_ADIOS2DATACOLLECTION */
|
||||
|
||||
+16
-123
@@ -76,7 +76,7 @@ BilinearForm::BilinearForm(FiniteElementSpace * f)
|
||||
precompute_sparsity = 0;
|
||||
diag_policy = DIAG_KEEP;
|
||||
|
||||
assembly = AssemblyLevel::LEGACYFULL;
|
||||
assembly = AssemblyLevel::FULL;
|
||||
batch = 1;
|
||||
ext = NULL;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
|
||||
precompute_sparsity = ps;
|
||||
diag_policy = DIAG_KEEP;
|
||||
|
||||
assembly = AssemblyLevel::LEGACYFULL;
|
||||
assembly = AssemblyLevel::FULL;
|
||||
batch = 1;
|
||||
ext = NULL;
|
||||
|
||||
@@ -121,13 +121,13 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
assembly = assembly_level;
|
||||
switch (assembly)
|
||||
{
|
||||
case AssemblyLevel::LEGACYFULL:
|
||||
break;
|
||||
case AssemblyLevel::FULL:
|
||||
ext = new FABilinearFormExtension(this);
|
||||
// ext = new FABilinearFormExtension(this);
|
||||
// Use the original BilinearForm implementation for now
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
ext = new EABilinearFormExtension(this);
|
||||
mfem_error("Element assembly not supported yet... stay tuned!");
|
||||
// ext = new EABilinearFormExtension(this);
|
||||
break;
|
||||
case AssemblyLevel::PARTIAL:
|
||||
ext = new PABilinearFormExtension(this);
|
||||
@@ -144,7 +144,7 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
void BilinearForm::EnableStaticCondensation()
|
||||
{
|
||||
delete static_cond;
|
||||
if (assembly != AssemblyLevel::LEGACYFULL)
|
||||
if (assembly != AssemblyLevel::FULL)
|
||||
{
|
||||
static_cond = NULL;
|
||||
MFEM_WARNING("Static condensation not supported for this assembly level");
|
||||
@@ -169,7 +169,7 @@ void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space,
|
||||
const Array<int> &ess_tdof_list)
|
||||
{
|
||||
delete hybridization;
|
||||
if (assembly != AssemblyLevel::LEGACYFULL)
|
||||
if (assembly != AssemblyLevel::FULL)
|
||||
{
|
||||
delete constr_integ;
|
||||
hybridization = NULL;
|
||||
@@ -224,7 +224,7 @@ MatrixInverse * BilinearForm::Inverse() const
|
||||
|
||||
void BilinearForm::Finalize (int skip_zeros)
|
||||
{
|
||||
if (assembly == AssemblyLevel::LEGACYFULL)
|
||||
if (assembly == AssemblyLevel::FULL)
|
||||
{
|
||||
if (!static_cond) { mat->Finalize(skip_zeros); }
|
||||
if (mat_e) { mat_e->Finalize(skip_zeros); }
|
||||
@@ -627,33 +627,6 @@ void BilinearForm::AssembleDiagonal(Vector &diag) const
|
||||
MFEM_ASSERT(diag.Size() == fes->GetTrueVSize(),
|
||||
"Vector for holding diagonal has wrong size!");
|
||||
const Operator *P = fes->GetProlongationMatrix();
|
||||
// For an AMR mesh, a convergent diagonal is assembled with |P^T| d_e,
|
||||
// where |P^T| has the entry-wise absolute values of the conforming
|
||||
// prolongation transpose operator.
|
||||
if (P && !fes->Conforming())
|
||||
{
|
||||
Vector local_diag(P->Height());
|
||||
ext->AssembleDiagonal(local_diag);
|
||||
const SparseMatrix *SP = dynamic_cast<const SparseMatrix*>(P);
|
||||
#ifdef MFEM_USE_MPI
|
||||
const HypreParMatrix *HP = dynamic_cast<const HypreParMatrix*>(P);
|
||||
#endif
|
||||
if (SP)
|
||||
{
|
||||
SP->AbsMultTranspose(local_diag, diag);
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
else if (HP)
|
||||
{
|
||||
HP->AbsMultTranspose(1.0, local_diag, 0.0, diag);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Prolongation matrix has unexpected type.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!IsIdentityProlongation(P))
|
||||
{
|
||||
Vector local_diag(P->Height());
|
||||
@@ -667,7 +640,8 @@ void BilinearForm::AssembleDiagonal(Vector &diag) const
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->GetDiag(diag);
|
||||
MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
|
||||
"matrix and use SparseMatrix::GetDiag?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,7 +1084,7 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
|
||||
mat = NULL;
|
||||
mat_e = NULL;
|
||||
extern_bfs = 0;
|
||||
assembly = AssemblyLevel::LEGACYFULL;
|
||||
assembly = AssemblyLevel::FULL;
|
||||
ext = NULL;
|
||||
}
|
||||
|
||||
@@ -1135,7 +1109,7 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
|
||||
bbfi_marker = mbf->bbfi_marker;
|
||||
btfbfi_marker = mbf->btfbfi_marker;
|
||||
|
||||
assembly = AssemblyLevel::LEGACYFULL;
|
||||
assembly = AssemblyLevel::FULL;
|
||||
ext = NULL;
|
||||
}
|
||||
|
||||
@@ -1148,8 +1122,6 @@ void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
assembly = assembly_level;
|
||||
switch (assembly)
|
||||
{
|
||||
case AssemblyLevel::LEGACYFULL:
|
||||
break;
|
||||
case AssemblyLevel::FULL:
|
||||
// ext = new FAMixedBilinearFormExtension(this);
|
||||
// Use the original BilinearForm implementation for now
|
||||
@@ -1220,7 +1192,7 @@ void MixedBilinearForm::AddMultTranspose(const Vector & x, Vector & y,
|
||||
|
||||
MatrixInverse * MixedBilinearForm::Inverse() const
|
||||
{
|
||||
if (assembly != AssemblyLevel::LEGACYFULL)
|
||||
if (assembly != AssemblyLevel::FULL)
|
||||
{
|
||||
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this assembly level!");
|
||||
return NULL;
|
||||
@@ -1233,7 +1205,7 @@ MatrixInverse * MixedBilinearForm::Inverse() const
|
||||
|
||||
void MixedBilinearForm::Finalize (int skip_zeros)
|
||||
{
|
||||
if (assembly == AssemblyLevel::LEGACYFULL)
|
||||
if (assembly == AssemblyLevel::FULL)
|
||||
{
|
||||
mat -> Finalize (skip_zeros);
|
||||
}
|
||||
@@ -1460,57 +1432,9 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AssembleDiagonal_ADAt(const Vector &D,
|
||||
Vector &diag) const
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
MFEM_ASSERT(diag.Size() == test_fes->GetTrueVSize(),
|
||||
"Vector for holding diagonal has wrong size!");
|
||||
MFEM_ASSERT(D.Size() == trial_fes->GetTrueVSize(),
|
||||
"Vector for holding diagonal has wrong size!");
|
||||
const Operator *P_trial = trial_fes->GetProlongationMatrix();
|
||||
const Operator *P_test = test_fes->GetProlongationMatrix();
|
||||
if (!IsIdentityProlongation(P_trial))
|
||||
{
|
||||
Vector local_D(P_trial->Height());
|
||||
P_trial->Mult(D, local_D);
|
||||
|
||||
if (!IsIdentityProlongation(P_test))
|
||||
{
|
||||
Vector local_diag(P_test->Height());
|
||||
ext->AssembleDiagonal_ADAt(local_D, local_diag);
|
||||
P_test->MultTranspose(local_diag, diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
ext->AssembleDiagonal_ADAt(local_D, diag);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsIdentityProlongation(P_test))
|
||||
{
|
||||
Vector local_diag(P_test->Height());
|
||||
ext->AssembleDiagonal_ADAt(D, local_diag);
|
||||
P_test->MultTranspose(local_diag, diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
ext->AssembleDiagonal_ADAt(D, diag);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
|
||||
"matrix and use SparseMatrix functions?");
|
||||
}
|
||||
}
|
||||
|
||||
void MixedBilinearForm::ConformingAssemble()
|
||||
{
|
||||
if (assembly != AssemblyLevel::LEGACYFULL)
|
||||
if (assembly != AssemblyLevel::FULL)
|
||||
{
|
||||
MFEM_WARNING("Conforming assemble not supported for this assembly level!");
|
||||
return;
|
||||
@@ -1771,40 +1695,9 @@ MixedBilinearForm::~MixedBilinearForm()
|
||||
delete ext;
|
||||
}
|
||||
|
||||
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
MFEM_ABORT("the assembly level has already been set!");
|
||||
}
|
||||
assembly = assembly_level;
|
||||
switch (assembly)
|
||||
{
|
||||
case AssemblyLevel::FULL:
|
||||
// Use the original BilinearForm implementation for now
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
mfem_error("Element assembly not supported yet... stay tuned!");
|
||||
break;
|
||||
case AssemblyLevel::PARTIAL:
|
||||
ext = new PADiscreteLinearOperatorExtension(this);
|
||||
break;
|
||||
case AssemblyLevel::NONE:
|
||||
mfem_error("Matrix-free action not supported yet... stay tuned!");
|
||||
break;
|
||||
default:
|
||||
mfem_error("Unknown assembly level");
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->Assemble();
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> dom_vdofs, ran_vdofs;
|
||||
ElementTransformation *T;
|
||||
const FiniteElement *dom_fe, *ran_fe;
|
||||
|
||||
+39
-126
@@ -25,15 +25,12 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @brief Enumeration defining the assembly level for bilinear and nonlinear
|
||||
form classes derived from Operator. */
|
||||
/// Enumeration defining the assembly level for bilinear and nonlinear form
|
||||
/// classes derived from Operator.
|
||||
enum class AssemblyLevel
|
||||
{
|
||||
/// Legacy fully assembled form, i.e. a global sparse matrix in MFEM, Hypre
|
||||
/// or PETSC format. This assembly is ALWAYS performed on the host.
|
||||
LEGACYFULL = 0,
|
||||
/// Fully assembled form, i.e. a global sparse matrix in MFEM format. This
|
||||
/// assembly is compatible with device execution.
|
||||
/// Fully assembled form, i.e. a global sparse matrix in MFEM, Hypre or PETSC
|
||||
/// format.
|
||||
FULL,
|
||||
/// Form assembled at element level, which computes and stores dense element
|
||||
/// matrices.
|
||||
@@ -47,19 +44,15 @@ enum class AssemblyLevel
|
||||
};
|
||||
|
||||
|
||||
/** @brief A "square matrix" operator for the associated FE space and
|
||||
BLFIntegrators The sum of all the BLFIntegrators can be used form the matrix
|
||||
M. This class also supports other assembly levels specified via the
|
||||
SetAssemblyLevel() function. */
|
||||
/** Class for bilinear form - "Matrix" with associated FE space and
|
||||
BLFIntegrators. */
|
||||
class BilinearForm : public Matrix
|
||||
{
|
||||
protected:
|
||||
/// Sparse matrix \f$ M \f$ to be associated with the form. Owned.
|
||||
/// Sparse matrix to be associated with the form. Owned.
|
||||
SparseMatrix *mat;
|
||||
|
||||
/** @brief Sparse Matrix \f$ M_e \f$ used to store the eliminations
|
||||
from the b.c. Owned.
|
||||
\f$ M + M_e = M_{original} \f$ */
|
||||
/// Matrix used to eliminate b.c. Owned.
|
||||
SparseMatrix *mat_e;
|
||||
|
||||
/// FE space on which the form lives. Not owned.
|
||||
@@ -69,12 +62,12 @@ protected:
|
||||
AssemblyLevel assembly;
|
||||
/// Element batch size used in the form action (1, 8, num_elems, etc.)
|
||||
int batch;
|
||||
/** @brief Extension for supporting Full Assembly (FA), Element Assembly (EA),
|
||||
/** Extension for supporting Full Assembly (FA), Element Assembly (EA),
|
||||
Partial Assembly (PA), or Matrix Free assembly (MF). */
|
||||
BilinearFormExtension *ext;
|
||||
|
||||
/** @brief Indicates the Mesh::sequence corresponding to the current state of
|
||||
the BilinearForm. */
|
||||
/// Indicates the Mesh::sequence corresponding to the current state of the
|
||||
/// BilinearForm.
|
||||
long sequence;
|
||||
|
||||
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
|
||||
@@ -122,7 +115,7 @@ protected:
|
||||
static_cond = NULL; hybridization = NULL;
|
||||
precompute_sparsity = 0;
|
||||
diag_policy = DIAG_KEEP;
|
||||
assembly = AssemblyLevel::LEGACYFULL;
|
||||
assembly = AssemblyLevel::FULL;
|
||||
batch = 1;
|
||||
ext = NULL;
|
||||
}
|
||||
@@ -154,43 +147,35 @@ public:
|
||||
/// Get the size of the BilinearForm as a square matrix.
|
||||
int Size() const { return height; }
|
||||
|
||||
/// Set the desired assembly level.
|
||||
/** Valid choices are:
|
||||
|
||||
- AssemblyLevel::FULL (default)
|
||||
- AssemblyLevel::PARTIAL
|
||||
- AssemblyLevel::ELEMENT
|
||||
- AssemblyLevel::NONE
|
||||
|
||||
This method must be called before assembly. */
|
||||
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
|
||||
/** This method must be called before assembly. */
|
||||
void SetAssemblyLevel(AssemblyLevel assembly_level);
|
||||
|
||||
/// Returns the assembly level
|
||||
AssemblyLevel GetAssemblyLevel() const { return assembly; }
|
||||
|
||||
/** @brief Enable the use of static condensation. For details see the
|
||||
description for class StaticCondensation in fem/staticcond.hpp This method
|
||||
should be called before assembly. If the number of unknowns after static
|
||||
/** Enable the use of static condensation. For details see the description
|
||||
for class StaticCondensation in fem/staticcond.hpp This method should be
|
||||
called before assembly. If the number of unknowns after static
|
||||
condensation is not reduced, it is not enabled. */
|
||||
void EnableStaticCondensation();
|
||||
|
||||
/** @brief Check if static condensation was actually enabled by a previous
|
||||
call to EnableStaticCondensation(). */
|
||||
/** Check if static condensation was actually enabled by a previous call to
|
||||
EnableStaticCondensation(). */
|
||||
bool StaticCondensationIsEnabled() const { return static_cond; }
|
||||
|
||||
/// Return the trace FE space associated with static condensation.
|
||||
FiniteElementSpace *SCFESpace() const
|
||||
{ return static_cond ? static_cond->GetTraceFESpace() : NULL; }
|
||||
|
||||
/// Enable hybridization.
|
||||
/** For details see the description for class
|
||||
/** Enable hybridization; for details see the description for class
|
||||
Hybridization in fem/hybridization.hpp. This method should be called
|
||||
before assembly. */
|
||||
void EnableHybridization(FiniteElementSpace *constr_space,
|
||||
BilinearFormIntegrator *constr_integ,
|
||||
const Array<int> &ess_tdof_list);
|
||||
|
||||
/** @brief For scalar FE spaces, precompute the sparsity pattern of the matrix
|
||||
/** For scalar FE spaces, precompute the sparsity pattern of the matrix
|
||||
(assuming dense element matrices) based on the types of integrators
|
||||
present in the bilinear form. */
|
||||
void UsePrecomputedSparsity(int ps = 1) { precompute_sparsity = ps; }
|
||||
@@ -209,16 +194,15 @@ public:
|
||||
/// Use the sparsity of @a A to allocate the internal SparseMatrix.
|
||||
void UseSparsity(SparseMatrix &A);
|
||||
|
||||
/// Pre-allocate the internal SparseMatrix before assembly.
|
||||
/** If the flag 'precompute sparsity'
|
||||
is set, the matrix is allocated in CSR format (i.e.
|
||||
/** Pre-allocate the internal SparseMatrix before assembly. If the flag
|
||||
'precompute sparsity' is set, the matrix is allocated in CSR format (i.e.
|
||||
finalized) and the entries are initialized with zeros. */
|
||||
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
|
||||
|
||||
/// Access all the integrators added with AddDomainIntegrator().
|
||||
/// Access all integrators added with AddDomainIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetDBFI() { return &dbfi; }
|
||||
|
||||
/// Access all the integrators added with AddBoundaryIntegrator().
|
||||
/// Access all integrators added with AddBoundaryIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBBFI() { return &bbfi; }
|
||||
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
@@ -235,85 +219,64 @@ public:
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBFBFI_Marker() { return &bfbfi_marker; }
|
||||
|
||||
/// Returns a reference to: \f$ M_{ij} \f$
|
||||
const double &operator()(int i, int j) { return (*mat)(i,j); }
|
||||
|
||||
/// Returns a reference to: \f$ M_{ij} \f$
|
||||
/// Returns reference to a_{ij}.
|
||||
virtual double &Elem(int i, int j);
|
||||
|
||||
/// Returns constant reference to: \f$ M_{ij} \f$
|
||||
/// Returns constant reference to a_{ij}.
|
||||
virtual const double &Elem(int i, int j) const;
|
||||
|
||||
/// Matrix vector multiplication: \f$ y = M x \f$
|
||||
/// Matrix vector multiplication.
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
/** @brief Matrix vector multiplication with the original uneliminated
|
||||
matrix. The original matrix is \f$ M + M_e \f$ so we have:
|
||||
\f$ y = M x + M_e x \f$ */
|
||||
void FullMult(const Vector &x, Vector &y) const
|
||||
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
|
||||
|
||||
/// Add the matrix vector multiple to a vector: \f$ y += a M x \f$
|
||||
virtual void AddMult(const Vector &x, Vector &y, const double a = 1.0) const
|
||||
{ mat -> AddMult (x, y, a); }
|
||||
|
||||
/** @brief Add the original uneliminated matrix vector multiple to a vector.
|
||||
The original matrix is \f$ M + Me \f$ so we have:
|
||||
\f$ y += M x + M_e x \f$ */
|
||||
void FullAddMult(const Vector &x, Vector &y) const
|
||||
{ mat->AddMult(x, y); mat_e->AddMult(x, y); }
|
||||
|
||||
/// Add the matrix transpose vector multiplication: \f$ y += a M^T x \f$
|
||||
virtual void AddMultTranspose(const Vector & x, Vector & y,
|
||||
const double a = 1.0) const
|
||||
{ mat->AddMultTranspose(x, y, a); }
|
||||
|
||||
/** @brief Add the original uneliminated matrix transpose vector
|
||||
multiple to a vector. The original matrix is \f$ M + M_e \f$
|
||||
so we have: \f$ y += M^T x + {M_e}^T x \f$ */
|
||||
void FullAddMultTranspose(const Vector & x, Vector & y) const
|
||||
{ mat->AddMultTranspose(x, y); mat_e->AddMultTranspose(x, y); }
|
||||
|
||||
/// Matrix transpose vector multiplication: \f$ y = M^T x \f$
|
||||
virtual void MultTranspose(const Vector & x, Vector & y) const
|
||||
{ y = 0.0; AddMultTranspose (x, y); }
|
||||
|
||||
/// Compute \f$ y^T M x \f$
|
||||
double InnerProduct(const Vector &x, const Vector &y) const
|
||||
{ return mat->InnerProduct (x, y); }
|
||||
|
||||
/// Returns a pointer to (approximation) of the matrix inverse: \f$ M^{-1} \f$
|
||||
/// Returns a pointer to (approximation) of the matrix inverse.
|
||||
virtual MatrixInverse *Inverse() const;
|
||||
|
||||
/// Finalizes the matrix initialization.
|
||||
virtual void Finalize(int skip_zeros = 1);
|
||||
|
||||
/// Returns a const reference to the sparse matrix.
|
||||
/// Returns a reference to the sparse matrix
|
||||
const SparseMatrix &SpMat() const
|
||||
{
|
||||
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
|
||||
return *mat;
|
||||
}
|
||||
|
||||
/// Returns a reference to the sparse matrix: \f$ M \f$
|
||||
SparseMatrix &SpMat()
|
||||
{
|
||||
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
|
||||
return *mat;
|
||||
}
|
||||
|
||||
/** @brief Nullifies the internal matrix \f$ M \f$ and returns a pointer
|
||||
to it. Used for transfering ownership. */
|
||||
SparseMatrix *LoseMat() { SparseMatrix *tmp = mat; mat = NULL; return tmp; }
|
||||
|
||||
/// Returns a const reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
|
||||
/// Returns a reference to the sparse matrix of eliminated b.c.
|
||||
const SparseMatrix &SpMatElim() const
|
||||
{
|
||||
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
|
||||
return *mat_e;
|
||||
}
|
||||
|
||||
/// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
|
||||
SparseMatrix &SpMatElim()
|
||||
{
|
||||
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
|
||||
@@ -348,7 +311,6 @@ public:
|
||||
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
/// Sets all sparse values of \f$ M \f$ and \f$ M_e \f$ to 'a'.
|
||||
void operator=(const double a)
|
||||
{
|
||||
if (mat != NULL) { *mat = a; }
|
||||
@@ -366,18 +328,15 @@ public:
|
||||
for an AMR mesh. */
|
||||
void AssembleDiagonal(Vector &diag) const;
|
||||
|
||||
/// Get the finite element space prolongation operator.
|
||||
/// Get the finite element space prolongation matrix
|
||||
virtual const Operator *GetProlongation() const
|
||||
{ return fes->GetConformingProlongation(); }
|
||||
/// Get the finite element space restriction operator
|
||||
/// Get the finite element space restriction matrix
|
||||
virtual const Operator *GetRestriction() const
|
||||
{ return fes->GetConformingRestriction(); }
|
||||
/// Get the output finite element space prolongation matrix
|
||||
virtual const Operator *GetOutputProlongation() const
|
||||
{ return GetProlongation(); }
|
||||
/// Get the output finite element space prolongation matrix (local diagonal)
|
||||
virtual const Operator *GetLocalOutputProlongation() const
|
||||
{ return GetOutputProlongation(); }
|
||||
/// Get the output finite element space restriction matrix
|
||||
virtual const Operator *GetOutputRestriction() const
|
||||
{ return GetRestriction(); }
|
||||
@@ -532,12 +491,10 @@ public:
|
||||
double value);
|
||||
|
||||
/// Eliminate the given @a vdofs. NOTE: here, @a vdofs is a list of DOFs.
|
||||
/** In this case the eliminations are applied to the internal \f$ M \f$
|
||||
and @a rhs without storing the elimination matrix \f$ M_e \f$. */
|
||||
void EliminateVDofs(const Array<int> &vdofs, const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
|
||||
/// Eliminate the given @a vdofs, storing the eliminated part internally in \f$ M_e \f$.
|
||||
/// Eliminate the given @a vdofs, storing the eliminated part internally.
|
||||
/** This method works in conjunction with EliminateVDofsInRHS() and allows
|
||||
elimination of boundary conditions in multiple right-hand sides. In this
|
||||
method, @a vdofs is a list of DOFs. */
|
||||
@@ -566,11 +523,9 @@ public:
|
||||
void EliminateVDofsInRHS(const Array<int> &vdofs, const Vector &x,
|
||||
Vector &b);
|
||||
|
||||
/// Compute inner product for full uneliminated matrix \f$ y^T M x + y^T M_e x \f$
|
||||
double FullInnerProduct(const Vector &x, const Vector &y) const
|
||||
{ return mat->InnerProduct(x, y) + mat_e->InnerProduct(x, y); }
|
||||
|
||||
/// Update the @a FiniteElementSpace and delete all data associated with the old one.
|
||||
virtual void Update(FiniteElementSpace *nfes = NULL);
|
||||
|
||||
/// (DEPRECATED) Return the FE space associated with the BilinearForm.
|
||||
@@ -582,30 +537,12 @@ public:
|
||||
/// Read-only access to the associated FiniteElementSpace.
|
||||
const FiniteElementSpace *FESpace() const { return fes; }
|
||||
|
||||
/// Sets diagonal policy used upon construction of the linear system.
|
||||
/** Policies include:
|
||||
|
||||
- DIAG_ZERO (Set the diagonal values to zero)
|
||||
- DIAG_ONE (Set the diagonal values to one)
|
||||
- DIAG_KEEP (Keep the diagonal values)
|
||||
*/
|
||||
/// Sets diagonal policy used upon construction of the linear system
|
||||
void SetDiagonalPolicy(DiagonalPolicy policy);
|
||||
|
||||
/// Indicate that integrators are not owned by the BilinearForm
|
||||
void UseExternalIntegrators() { extern_bfs = 1; };
|
||||
|
||||
void GetTimings(double& t1, double& t2)
|
||||
{
|
||||
t1 = 0.0;
|
||||
t2 = 0.0;
|
||||
PABilinearFormExtension *paext = dynamic_cast<PABilinearFormExtension*>(ext);
|
||||
if (paext != NULL)
|
||||
{
|
||||
t1 = paext->timingDomain;
|
||||
t2 = paext->timingBoundary;
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys bilinear form.
|
||||
virtual ~BilinearForm();
|
||||
};
|
||||
@@ -613,16 +550,16 @@ public:
|
||||
|
||||
/**
|
||||
Class for assembling of bilinear forms `a(u,v)` defined on different
|
||||
trial and test spaces. The assembled matrix `M` is such that
|
||||
trial and test spaces. The assembled matrix `A` is such that
|
||||
|
||||
a(u,v) = V^t M U
|
||||
a(u,v) = V^t A U
|
||||
|
||||
where `U` and `V` are the vectors representing the functions `u` and `v`,
|
||||
respectively. The first argument, `u`, of `a(,)` is in the trial space
|
||||
and the second argument, `v`, is in the test space. Thus,
|
||||
|
||||
# of rows of M = dimension of the test space and
|
||||
# of cols of M = dimension of the trial space.
|
||||
# of rows of A = dimension of the test space and
|
||||
# of cols of A = dimension of the trial space.
|
||||
|
||||
Both trial and test spaces should be defined on the same mesh.
|
||||
*/
|
||||
@@ -691,15 +628,11 @@ public:
|
||||
FiniteElementSpace *te_fes,
|
||||
MixedBilinearForm *mbf);
|
||||
|
||||
/// Returns a reference to: \f$ M_{ij} \f$
|
||||
virtual double &Elem(int i, int j);
|
||||
|
||||
/// Returns a reference to: \f$ M_{ij} \f$
|
||||
virtual const double &Elem(int i, int j) const;
|
||||
|
||||
/// Matrix multiplication: \f$ y = M x \f$
|
||||
virtual void Mult(const Vector & x, Vector & y) const;
|
||||
|
||||
virtual void AddMult(const Vector & x, Vector & y,
|
||||
const double a = 1.0) const;
|
||||
|
||||
@@ -709,7 +642,6 @@ public:
|
||||
|
||||
virtual MatrixInverse *Inverse() const;
|
||||
|
||||
/// Finalizes the matrix initialization.
|
||||
virtual void Finalize(int skip_zeros = 1);
|
||||
|
||||
/** Extract the associated matrix as SparseMatrix blocks. The number of
|
||||
@@ -717,14 +649,8 @@ public:
|
||||
test and trial spaces, respectively. */
|
||||
void GetBlocks(Array2D<SparseMatrix *> &blocks) const;
|
||||
|
||||
/// Returns a const reference to the sparse matrix: \f$ M \f$
|
||||
const SparseMatrix &SpMat() const { return *mat; }
|
||||
|
||||
/// Returns a reference to the sparse matrix: \f$ M \f$
|
||||
SparseMatrix &SpMat() { return *mat; }
|
||||
|
||||
/** @brief Nullifies the internal matrix \f$ M \f$ and returns a pointer
|
||||
to it. Used for transfering ownership. */
|
||||
SparseMatrix *LoseMat() { SparseMatrix *tmp = mat; mat = NULL; return tmp; }
|
||||
|
||||
/// Adds a domain integrator. Assumes ownership of @a bfi.
|
||||
@@ -771,7 +697,6 @@ public:
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBTFBFI_Marker() { return &btfbfi_marker; }
|
||||
|
||||
/// Sets all sparse values of \f$ M \f$ to @a a.
|
||||
void operator=(const double a) { *mat = a; }
|
||||
|
||||
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
|
||||
@@ -780,10 +705,6 @@ public:
|
||||
|
||||
void Assemble(int skip_zeros = 1);
|
||||
|
||||
/** @brief Assemble the diagonal of ADA^T into diag, where A is this mixed
|
||||
bilinear form and D is a diagonal. */
|
||||
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
|
||||
|
||||
/// Get the input finite element space prolongation matrix
|
||||
virtual const Operator *GetProlongation() const
|
||||
{ return trial_fes->GetProlongationMatrix(); }
|
||||
@@ -999,17 +920,9 @@ public:
|
||||
/// Access all interpolators added with AddDomainInterpolator().
|
||||
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
|
||||
|
||||
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
|
||||
/** This method must be called before assembly. */
|
||||
void SetAssemblyLevel(AssemblyLevel assembly_level);
|
||||
|
||||
/** @brief Construct the internal matrix representation of the discrete
|
||||
linear operator. */
|
||||
virtual void Assemble(int skip_zeros = 1);
|
||||
|
||||
/// Get the output finite element space prolongation matrix (local diagonal)
|
||||
virtual const Operator *GetLocalOutputProlongation() const
|
||||
{ return test_fes->GetLocalProlongationMatrix(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+6
-866
@@ -15,9 +15,6 @@
|
||||
#include "../general/forall.hpp"
|
||||
#include "bilinearform.hpp"
|
||||
#include "libceed/ceed.hpp"
|
||||
#include "pgridfunc.hpp"
|
||||
|
||||
#include "../general/tic_toc.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -48,12 +45,9 @@ PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
|
||||
elem_restrict = NULL;
|
||||
int_face_restrict_lex = NULL;
|
||||
bdr_face_restrict_lex = NULL;
|
||||
|
||||
timingDomain = 0.0;
|
||||
timingBoundary = 0.0;
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
void PABilinearFormExtension::SetupRestrictionOperators()
|
||||
{
|
||||
ElementDofOrdering ordering = UsesTensorBasis(*a->FESpace())?
|
||||
ElementDofOrdering::LEXICOGRAPHIC:
|
||||
@@ -71,8 +65,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
if (int_face_restrict_lex == NULL && a->GetFBFI()->Size() > 0)
|
||||
{
|
||||
int_face_restrict_lex = trialFes->GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Interior);
|
||||
ElementDofOrdering::LEXICOGRAPHIC, FaceType::Interior);
|
||||
faceIntX.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
faceIntY.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
faceIntY.UseDevice(true); // ensure 'faceIntY = 0.0' is done on device
|
||||
@@ -81,9 +74,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
if (bdr_face_restrict_lex == NULL && a->GetBFBFI()->Size() > 0)
|
||||
{
|
||||
bdr_face_restrict_lex = trialFes->GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Boundary,
|
||||
m);
|
||||
ElementDofOrdering::LEXICOGRAPHIC, FaceType::Boundary);
|
||||
faceBdrX.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
faceBdrY.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
faceBdrY.UseDevice(true); // ensure 'faceBoundY = 0.0' is done on device
|
||||
@@ -92,7 +83,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
|
||||
void PABilinearFormExtension::Assemble()
|
||||
{
|
||||
SetupRestrictionOperators(L2FaceValues::DoubleValued);
|
||||
SetupRestrictionOperators();
|
||||
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int integratorCount = integrators.Size();
|
||||
@@ -101,13 +92,6 @@ void PABilinearFormExtension::Assemble()
|
||||
integrators[i]->AssemblePA(*a->FESpace());
|
||||
}
|
||||
|
||||
Array<BilinearFormIntegrator*> &bdryIntegrators = *a->GetBBFI();
|
||||
const int bdryIntegratorCount = bdryIntegrators.Size();
|
||||
for (int i = 0; i < bdryIntegratorCount; ++i)
|
||||
{
|
||||
bdryIntegrators[i]->AssemblePA(*a->FESpace());
|
||||
}
|
||||
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int intFaceIntegratorCount = intFaceIntegrators.Size();
|
||||
for (int i = 0; i < intFaceIntegratorCount; ++i)
|
||||
@@ -121,96 +105,6 @@ void PABilinearFormExtension::Assemble()
|
||||
{
|
||||
bdrFaceIntegrators[i]->AssemblePABoundaryFaces(*a->FESpace());
|
||||
}
|
||||
|
||||
timingDomain = 0.0;
|
||||
timingBoundary = 0.0;
|
||||
|
||||
if (bdryIntegratorCount > 0)
|
||||
{
|
||||
FiniteElementSpace *fes = a->GetFES();
|
||||
const int nbe = fes->GetNBE();
|
||||
const int bedofs = nbe > 0 ? nbe * fes->GetVDim() * fes->GetBE(0)->GetDof() : 0;
|
||||
gatherMap.SetSize(bedofs);
|
||||
indices.SetSize(bedofs);
|
||||
|
||||
ndofs = fes->GetNDofs();
|
||||
offsets.SetSize(ndofs+1);
|
||||
for (int i = 0; i <= ndofs; ++i)
|
||||
{
|
||||
offsets[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nbe; i++)
|
||||
{
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
Array<int> vdofs;
|
||||
fes -> GetBdrElementVDofs (i, vdofs);
|
||||
|
||||
const TensorBasisElement* el =
|
||||
dynamic_cast<const TensorBasisElement*>(&be);
|
||||
|
||||
MFEM_VERIFY(el != NULL, "");
|
||||
|
||||
const Array<int> &fe_dof_map = el->GetDofMap();
|
||||
|
||||
MFEM_VERIFY(fe_dof_map.Size() == fes->GetBE(i)->GetDof(), "");
|
||||
MFEM_VERIFY(vdofs.Size() == fes->GetBE(i)->GetDof(), "");
|
||||
|
||||
for (int j=0; j<vdofs.Size(); ++j)
|
||||
{
|
||||
const int sidj = fe_dof_map[j];
|
||||
const int idj = sidj >= 0 ? sidj : -1 - sidj;
|
||||
const int dof_j = vdofs[idj];
|
||||
const int d = dof_j >= 0 ? dof_j : -1-dof_j;
|
||||
offsets[d+1]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i <= ndofs; ++i)
|
||||
{
|
||||
// Partial sum
|
||||
offsets[i] += offsets[i - 1];
|
||||
}
|
||||
|
||||
int os = 0;
|
||||
Array<int> cnt(ndofs);
|
||||
cnt = 0;
|
||||
indices = 0;
|
||||
|
||||
for (int i = 0; i < nbe; i++)
|
||||
{
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
Array<int> vdofs;
|
||||
fes -> GetBdrElementVDofs (i, vdofs);
|
||||
|
||||
const TensorBasisElement* el =
|
||||
dynamic_cast<const TensorBasisElement*>(&be);
|
||||
|
||||
MFEM_VERIFY(el != NULL, "");
|
||||
|
||||
const Array<int> &fe_dof_map = el->GetDofMap();
|
||||
|
||||
MFEM_VERIFY(fe_dof_map.Size() == fes->GetBE(i)->GetDof(), "");
|
||||
MFEM_VERIFY(vdofs.Size() == fes->GetBE(i)->GetDof(), "");
|
||||
|
||||
for (int j=0; j<vdofs.Size(); ++j)
|
||||
{
|
||||
const int sidj = fe_dof_map[j];
|
||||
const int idj = sidj >= 0 ? sidj : -1 - sidj;
|
||||
const int dof_j = vdofs[idj];
|
||||
const bool plus = (sidj >= 0 && dof_j >= 0) || (sidj < 0 && dof_j < 0);
|
||||
const int d = dof_j >= 0 ? dof_j : -1-dof_j;
|
||||
const int lid = os + j;
|
||||
gatherMap[lid] = plus ? d : -1-d;
|
||||
indices[offsets[d] + cnt[d]] = plus ? lid : -1-lid;
|
||||
cnt[d]++;
|
||||
}
|
||||
|
||||
os += vdofs.Size();
|
||||
}
|
||||
|
||||
MFEM_VERIFY(os == bedofs, "");
|
||||
}
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
@@ -218,7 +112,7 @@ void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
|
||||
const int iSz = integrators.Size();
|
||||
if (elem_restrict && !DeviceCanUseCeed())
|
||||
if (elem_restrict)
|
||||
{
|
||||
localY = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
@@ -278,18 +172,10 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
A.Reset(oper); // A will own oper
|
||||
}
|
||||
|
||||
//#define SWTIMING
|
||||
|
||||
void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
|
||||
#ifdef SWTIMING
|
||||
StopWatch swd;
|
||||
swd.Clear();
|
||||
swd.Start();
|
||||
#endif
|
||||
|
||||
const int iSz = integrators.Size();
|
||||
if (DeviceCanUseCeed() || !elem_restrict)
|
||||
{
|
||||
@@ -311,93 +197,6 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
|
||||
#ifdef SWTIMING
|
||||
swd.Stop();
|
||||
timingDomain += swd.RealTime();
|
||||
|
||||
StopWatch swb;
|
||||
swb.Clear();
|
||||
swb.Start();
|
||||
#endif
|
||||
|
||||
Array<BilinearFormIntegrator*> &bdryIntegrators = *a->GetBBFI();
|
||||
|
||||
const int biSz = bdryIntegrators.Size();
|
||||
if (DeviceCanUseCeed() || !elem_restrict)
|
||||
{
|
||||
MFEM_ABORT("Not implemented");
|
||||
}
|
||||
else if (biSz > 0)
|
||||
{
|
||||
FiniteElementSpace *fes = a->GetFES();
|
||||
|
||||
const int nbe = fes->GetNBE();
|
||||
const int bedofs = nbe > 0 ? nbe * fes->GetVDim() * fes->GetBE(0)->GetDof() : 0;
|
||||
bdryX.SetSize(bedofs);
|
||||
bdryY.SetSize(bedofs);
|
||||
bdryX.UseDevice(true);
|
||||
bdryY.UseDevice(true);
|
||||
bdryX = 0.0;
|
||||
bdryY = 0.0;
|
||||
|
||||
{
|
||||
MFEM_VERIFY(gatherMap.Size() == bedofs, "");
|
||||
auto d_gatherMap = gatherMap.Read();
|
||||
auto d_x = x.Read();
|
||||
auto d_bdryX = bdryX.Write();
|
||||
|
||||
MFEM_FORALL(i, bedofs,
|
||||
{
|
||||
const int gid = d_gatherMap[i];
|
||||
const bool plus = gid >= 0;
|
||||
const int j = plus ? gid : -1-gid;
|
||||
|
||||
d_bdryX[i] = plus ? d_x[j] : -d_x[j];
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < biSz; ++i)
|
||||
{
|
||||
bdryIntegrators[i]->AddMultPA(bdryX, bdryY);
|
||||
}
|
||||
//elem_restrict->MultTranspose(bdryY, y);
|
||||
|
||||
// bdryY contains quantities on all boundary elements. Now add them to y.
|
||||
|
||||
{
|
||||
const int xsize = x.Size();
|
||||
MFEM_VERIFY(y.Size() == xsize, "");
|
||||
MFEM_VERIFY(gatherMap.Size() == bedofs, "");
|
||||
auto d_offsets = offsets.Read();
|
||||
auto d_indices = indices.Read();
|
||||
auto d_y = y.ReadWrite();
|
||||
auto d_bdryY = bdryY.Read();
|
||||
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
const int nextOffset = d_offsets[i + 1];
|
||||
|
||||
double val = 0.0;
|
||||
for (int j = offset; j < nextOffset; ++j)
|
||||
{
|
||||
const int id = d_indices[j];
|
||||
const bool plus = id >= 0;
|
||||
const int idj = plus ? id : -1-id;
|
||||
const double yval = d_bdryY[idj];
|
||||
val += plus ? yval : -yval;
|
||||
}
|
||||
|
||||
d_y[i] += val;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SWTIMING
|
||||
swb.Stop();
|
||||
timingBoundary += swb.RealTime();
|
||||
#endif
|
||||
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
@@ -455,10 +254,6 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
Array<BilinearFormIntegrator*> &bdryIntegrators = *a->GetBBFI();
|
||||
const int biSz = bdryIntegrators.Size();
|
||||
MFEM_VERIFY(biSz == 0, "TODO");
|
||||
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
@@ -492,463 +287,6 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
// Data and methods for element-assembled bilinear forms
|
||||
EABilinearFormExtension::EABilinearFormExtension(BilinearForm *form)
|
||||
: PABilinearFormExtension(form),
|
||||
factorize_face_terms(form->FESpace()->IsDGSpace())
|
||||
{
|
||||
}
|
||||
|
||||
void EABilinearFormExtension::Assemble()
|
||||
{
|
||||
SetupRestrictionOperators(L2FaceValues::SingleValued);
|
||||
|
||||
ne = trialFes->GetMesh()->GetNE();
|
||||
elemDofs = trialFes->GetFE(0)->GetDof();
|
||||
|
||||
ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType());
|
||||
ea_data.UseDevice(true);
|
||||
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int integratorCount = integrators.Size();
|
||||
for (int i = 0; i < integratorCount; ++i)
|
||||
{
|
||||
integrators[i]->AssembleEA(*a->FESpace(), ea_data, i);
|
||||
}
|
||||
|
||||
faceDofs = trialFes ->
|
||||
GetTraceElement(0, trialFes->GetMesh()->GetFaceBaseGeometry(0)) ->
|
||||
GetDof();
|
||||
|
||||
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
|
||||
"Element assembly does not support AddBoundaryIntegrator yet.");
|
||||
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int intFaceIntegratorCount = intFaceIntegrators.Size();
|
||||
if (intFaceIntegratorCount>0)
|
||||
{
|
||||
nf_int = trialFes->GetNFbyType(FaceType::Interior);
|
||||
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
|
||||
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
|
||||
}
|
||||
for (int i = 0; i < intFaceIntegratorCount; ++i)
|
||||
{
|
||||
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
|
||||
ea_data_int,
|
||||
ea_data_ext,
|
||||
i);
|
||||
}
|
||||
|
||||
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
|
||||
const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
|
||||
if (boundFaceIntegratorCount>0)
|
||||
{
|
||||
nf_bdr = trialFes->GetNFbyType(FaceType::Boundary);
|
||||
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs, Device::GetMemoryType());
|
||||
ea_data_bdr = 0.0;
|
||||
}
|
||||
for (int i = 0; i < boundFaceIntegratorCount; ++i)
|
||||
{
|
||||
bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr,i);
|
||||
}
|
||||
|
||||
if (factorize_face_terms && int_face_restrict_lex)
|
||||
{
|
||||
auto restFint = dynamic_cast<const L2FaceRestriction&>(*int_face_restrict_lex);
|
||||
restFint.AddFaceMatricesToElementMatrices(ea_data_int, ea_data);
|
||||
}
|
||||
if (factorize_face_terms && bdr_face_restrict_lex)
|
||||
{
|
||||
auto restFbdr = dynamic_cast<const L2FaceRestriction&>(*bdr_face_restrict_lex);
|
||||
restFbdr.AddFaceMatricesToElementMatrices(ea_data_bdr, ea_data);
|
||||
}
|
||||
}
|
||||
|
||||
void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Apply the Element Restriction
|
||||
const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
|
||||
if (!useRestrict)
|
||||
{
|
||||
y.UseDevice(true); // typically this is a large vector, so store on device
|
||||
y = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
}
|
||||
// Apply the Element Matrices
|
||||
const int NDOFS = elemDofs;
|
||||
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(glob_j, ne*NDOFS,
|
||||
{
|
||||
const int e = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A(i, j, e)*X(i, e);
|
||||
}
|
||||
Y(j, e) += res;
|
||||
});
|
||||
// Apply the Element Restriction transposed
|
||||
if (useRestrict)
|
||||
{
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
|
||||
// Treatment of interior faces
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
{
|
||||
// Apply the Interior Face Restriction
|
||||
int_face_restrict_lex->Mult(x, faceIntX);
|
||||
if (faceIntX.Size()>0)
|
||||
{
|
||||
faceIntY = 0.0;
|
||||
// Apply the interior face matrices
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int);
|
||||
auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int);
|
||||
if (!factorize_face_terms)
|
||||
{
|
||||
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
MFEM_FORALL(glob_j, nf_int*NDOFS,
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(i, j, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(i, j, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
});
|
||||
}
|
||||
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
MFEM_FORALL(glob_j, nf_int*NDOFS,
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_ext(i, j, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_ext(i, j, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
// Apply the Interior Face Restriction transposed
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Treatment of boundary faces
|
||||
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
|
||||
const int bFISz = bdrFaceIntegrators.Size();
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
|
||||
{
|
||||
// Apply the Boundary Face Restriction
|
||||
bdr_face_restrict_lex->Mult(x, faceBdrX);
|
||||
if (faceBdrX.Size()>0)
|
||||
{
|
||||
faceBdrY = 0.0;
|
||||
// Apply the boundary face matrices
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr);
|
||||
auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr);
|
||||
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
|
||||
MFEM_FORALL(glob_j, nf_bdr*NDOFS,
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A(i, j, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Apply the Element Restriction
|
||||
const bool useRestrict = DeviceCanUseCeed() || !elem_restrict;
|
||||
if (!useRestrict)
|
||||
{
|
||||
y.UseDevice(true); // typically this is a large vector, so store on device
|
||||
y = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
}
|
||||
// Apply the Element Matrices transposed
|
||||
const int NDOFS = elemDofs;
|
||||
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(glob_j, ne*NDOFS,
|
||||
{
|
||||
const int e = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A(j, i, e)*X(i, e);
|
||||
}
|
||||
Y(j, e) += res;
|
||||
});
|
||||
// Apply the Element Restriction transposed
|
||||
if (useRestrict)
|
||||
{
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
|
||||
// Treatment of interior faces
|
||||
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
{
|
||||
// Apply the Interior Face Restriction
|
||||
int_face_restrict_lex->Mult(x, faceIntX);
|
||||
if (faceIntX.Size()>0)
|
||||
{
|
||||
faceIntY = 0.0;
|
||||
// Apply the interior face matrices transposed
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int);
|
||||
auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int);
|
||||
if (!factorize_face_terms)
|
||||
{
|
||||
auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
MFEM_FORALL(glob_j, nf_int*NDOFS,
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(j, i, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_int(j, i, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
});
|
||||
}
|
||||
auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
|
||||
MFEM_FORALL(glob_j, nf_int*NDOFS,
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_ext(j, i, 0, f)*X(i, 0, f);
|
||||
}
|
||||
Y(j, 1, f) += res;
|
||||
res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A_ext(j, i, 1, f)*X(i, 1, f);
|
||||
}
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
// Apply the Interior Face Restriction transposed
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Treatment of boundary faces
|
||||
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
|
||||
const int bFISz = bdrFaceIntegrators.Size();
|
||||
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
|
||||
{
|
||||
// Apply the Boundary Face Restriction
|
||||
bdr_face_restrict_lex->Mult(x, faceBdrX);
|
||||
if (faceBdrX.Size()>0)
|
||||
{
|
||||
faceBdrY = 0.0;
|
||||
// Apply the boundary face matrices transposed
|
||||
const int NDOFS = faceDofs;
|
||||
auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr);
|
||||
auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr);
|
||||
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
|
||||
MFEM_FORALL(glob_j, nf_bdr*NDOFS,
|
||||
{
|
||||
const int f = glob_j/NDOFS;
|
||||
const int j = glob_j%NDOFS;
|
||||
double res = 0.0;
|
||||
for (int i = 0; i < NDOFS; i++)
|
||||
{
|
||||
res += A(j, i, f)*X(i, f);
|
||||
}
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Data and methods for fully-assembled bilinear forms
|
||||
FABilinearFormExtension::FABilinearFormExtension(BilinearForm *form)
|
||||
: EABilinearFormExtension(form),
|
||||
mat(form->FESpace()->GetVSize(),form->FESpace()->GetVSize(),0),
|
||||
face_mat(form->FESpace()->GetVSize(),0,0),
|
||||
use_face_mat(false)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if ( ParFiniteElementSpace* pfes =
|
||||
dynamic_cast<ParFiniteElementSpace*>(form->FESpace()) )
|
||||
{
|
||||
if (pfes->IsDGSpace())
|
||||
{
|
||||
use_face_mat = true;
|
||||
pfes->ExchangeFaceNbrData();
|
||||
face_mat.SetWidth(pfes->GetFaceNbrVSize());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::Assemble()
|
||||
{
|
||||
EABilinearFormExtension::Assemble();
|
||||
FiniteElementSpace &fes = *a->FESpace();
|
||||
if (fes.IsDGSpace())
|
||||
{
|
||||
const L2ElementRestriction *restE =
|
||||
static_cast<const L2ElementRestriction*>(elem_restrict);
|
||||
const L2FaceRestriction *restF =
|
||||
static_cast<const L2FaceRestriction*>(int_face_restrict_lex);
|
||||
// 1. Fill I
|
||||
// 1.1 Increment with restE
|
||||
restE->FillI(mat);
|
||||
// 1.2 Increment with restF
|
||||
if (restF) { restF->FillI(mat, face_mat); }
|
||||
// 1.3 Sum the non-zeros in I
|
||||
auto h_I = mat.HostReadWriteI();
|
||||
int cpt = 0;
|
||||
const int vd = fes.GetVDim();
|
||||
const int ndofs = ne*elemDofs*vd;
|
||||
for (int i = 0; i < ndofs; i++)
|
||||
{
|
||||
const int nnz = h_I[i];
|
||||
h_I[i] = cpt;
|
||||
cpt += nnz;
|
||||
}
|
||||
const int nnz = cpt;
|
||||
h_I[ndofs] = nnz;
|
||||
mat.GetMemoryJ().New(nnz, mat.GetMemoryJ().GetMemoryType());
|
||||
mat.GetMemoryData().New(nnz, mat.GetMemoryData().GetMemoryType());
|
||||
if (use_face_mat && restF)
|
||||
{
|
||||
auto h_I_face = face_mat.HostReadWriteI();
|
||||
int cpt = 0;
|
||||
for (int i = 0; i < ndofs; i++)
|
||||
{
|
||||
const int nnz = h_I_face[i];
|
||||
h_I_face[i] = cpt;
|
||||
cpt += nnz;
|
||||
}
|
||||
const int nnz_face = cpt;
|
||||
h_I_face[ndofs] = nnz_face;
|
||||
face_mat.GetMemoryJ().New(nnz_face,
|
||||
face_mat.GetMemoryJ().GetMemoryType());
|
||||
face_mat.GetMemoryData().New(nnz_face,
|
||||
face_mat.GetMemoryData().GetMemoryType());
|
||||
}
|
||||
// 2. Fill J and Data
|
||||
// 2.1 Fill J and Data with Elem ea_data
|
||||
restE->FillJAndData(ea_data, mat);
|
||||
// 2.2 Fill J and Data with Face ea_data_ext
|
||||
if (restF) { restF->FillJAndData(ea_data_ext, mat, face_mat); }
|
||||
// 2.3 Shift indirections in I back to original
|
||||
auto I = mat.HostReadWriteI();
|
||||
for (int i = ndofs; i > 0; i--)
|
||||
{
|
||||
I[i] = I[i-1];
|
||||
}
|
||||
I[0] = 0;
|
||||
if (use_face_mat && restF)
|
||||
{
|
||||
auto I_face = face_mat.HostReadWriteI();
|
||||
for (int i = ndofs; i > 0; i--)
|
||||
{
|
||||
I_face[i] = I_face[i-1];
|
||||
}
|
||||
I_face[0] = 0;
|
||||
}
|
||||
}
|
||||
else // continuous Galerkin case
|
||||
{
|
||||
const ElementRestriction &rest =
|
||||
static_cast<const ElementRestriction&>(*elem_restrict);
|
||||
rest.FillSparseMatrix(ea_data, mat);
|
||||
}
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
mat.Mult(x, y);
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (const ParFiniteElementSpace *pfes =
|
||||
dynamic_cast<const ParFiniteElementSpace*>(testFes))
|
||||
{
|
||||
ParGridFunction x_gf;
|
||||
x_gf.MakeRef(const_cast<ParFiniteElementSpace*>(pfes),
|
||||
const_cast<Vector&>(x),0);
|
||||
x_gf.ExchangeFaceNbrData();
|
||||
Vector &shared_x = x_gf.FaceNbrData();
|
||||
if (shared_x.Size()) { face_mat.AddMult(shared_x, y); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
mat.MultTranspose(x, y);
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (const ParFiniteElementSpace *pfes =
|
||||
dynamic_cast<const ParFiniteElementSpace*>(testFes))
|
||||
{
|
||||
ParGridFunction x_gf;
|
||||
x_gf.MakeRef(const_cast<ParFiniteElementSpace*>(pfes),
|
||||
const_cast<Vector&>(x),0);
|
||||
x_gf.ExchangeFaceNbrData();
|
||||
Vector &shared_x = x_gf.FaceNbrData();
|
||||
if (shared_x.Size()) { face_mat.AddMultTranspose(shared_x, y); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
MixedBilinearFormExtension::MixedBilinearFormExtension(MixedBilinearForm *form)
|
||||
: Operator(form->Height(), form->Width()), a(form)
|
||||
{
|
||||
@@ -996,12 +334,6 @@ void PAMixedBilinearFormExtension::Assemble()
|
||||
{
|
||||
integrators[i]->AssemblePA(*trialFes, *testFes);
|
||||
}
|
||||
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
|
||||
"Partial assembly does not support AddBoundaryIntegrator yet.");
|
||||
MFEM_VERIFY(a->GetTFBFI()->Size() == 0,
|
||||
"Partial assembly does not support AddTraceFaceIntegrator yet.");
|
||||
MFEM_VERIFY(a->GetBTFBFI()->Size() == 0,
|
||||
"Partial assembly does not support AddBdrTraceFaceIntegrator yet.");
|
||||
}
|
||||
|
||||
void PAMixedBilinearFormExtension::Update()
|
||||
@@ -1019,6 +351,7 @@ void PAMixedBilinearFormExtension::Update()
|
||||
localTrial.UseDevice(true);
|
||||
localTrial.SetSize(elem_restrict_trial->Height(),
|
||||
Device::GetMemoryType());
|
||||
|
||||
}
|
||||
if (elem_restrict_test)
|
||||
{
|
||||
@@ -1154,197 +487,4 @@ void PAMixedBilinearFormExtension::AddMultTranspose(const Vector &x, Vector &y,
|
||||
}
|
||||
}
|
||||
|
||||
void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
|
||||
Vector &diag) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
|
||||
const int iSz = integrators.Size();
|
||||
|
||||
if (elem_restrict_trial)
|
||||
{
|
||||
const ElementRestriction* H1elem_restrict_trial =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_trial);
|
||||
if (H1elem_restrict_trial)
|
||||
{
|
||||
H1elem_restrict_trial->MultUnsigned(D, localTrial);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict_trial->Mult(D, localTrial);
|
||||
}
|
||||
}
|
||||
|
||||
if (elem_restrict_test)
|
||||
{
|
||||
localTest = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
if (elem_restrict_trial)
|
||||
{
|
||||
integrators[i]->AssembleDiagonalPA_ADAt(localTrial, localTest);
|
||||
}
|
||||
else
|
||||
{
|
||||
integrators[i]->AssembleDiagonalPA_ADAt(D, localTest);
|
||||
}
|
||||
}
|
||||
const ElementRestriction* H1elem_restrict_test =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (H1elem_restrict_test)
|
||||
{
|
||||
H1elem_restrict_test->MultTransposeUnsigned(localTest, diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_restrict_test->MultTranspose(localTest, diag);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
diag.UseDevice(true); // typically this is a large vector, so store on device
|
||||
diag = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
if (elem_restrict_trial)
|
||||
{
|
||||
integrators[i]->AssembleDiagonalPA_ADAt(localTrial, diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
integrators[i]->AssembleDiagonalPA_ADAt(D, diag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PADiscreteLinearOperatorExtension::PADiscreteLinearOperatorExtension(
|
||||
DiscreteLinearOperator *linop) :
|
||||
PAMixedBilinearFormExtension(linop)
|
||||
{
|
||||
}
|
||||
|
||||
const Operator *PADiscreteLinearOperatorExtension::GetLocalOutputProlongation()
|
||||
const
|
||||
{
|
||||
return a->GetLocalOutputProlongation();
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::Assemble()
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int integratorCount = integrators.Size();
|
||||
for (int i = 0; i < integratorCount; ++i)
|
||||
{
|
||||
integrators[i]->AssemblePA(*trialFes, *testFes);
|
||||
}
|
||||
|
||||
test_multiplicity.UseDevice(true);
|
||||
test_multiplicity.SetSize(elem_restrict_test->Width()); // l-vector
|
||||
Vector ones(elem_restrict_test->Height()); // e-vector
|
||||
ones = 1.0;
|
||||
|
||||
const ElementRestriction* elem_restrict =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (elem_restrict)
|
||||
{
|
||||
elem_restrict->MultTransposeUnsigned(ones, test_multiplicity);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("A real ElementRestriction is required in this setting!");
|
||||
}
|
||||
|
||||
auto tm = test_multiplicity.ReadWrite();
|
||||
MFEM_FORALL(i, test_multiplicity.Size(),
|
||||
{
|
||||
tm[i] = 1.0 / tm[i];
|
||||
});
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::AddMult(
|
||||
const Vector &x, Vector &y, const double c) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int iSz = integrators.Size();
|
||||
|
||||
// * G operation
|
||||
SetupMultInputs(elem_restrict_trial, x, localTrial,
|
||||
elem_restrict_test, y, localTest, c);
|
||||
|
||||
// * B^TDB operation
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultPA(localTrial, localTest);
|
||||
}
|
||||
|
||||
// do a kind of "set" rather than "add" in the below
|
||||
// operation as compared to the BilinearForm case
|
||||
// * G^T operation (kind of...)
|
||||
const ElementRestriction* elem_restrict =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (elem_restrict)
|
||||
{
|
||||
tempY.SetSize(y.Size());
|
||||
elem_restrict->MultLeftInverse(localTest, tempY);
|
||||
y += tempY;
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("In this setting you need a real ElementRestriction!");
|
||||
}
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::AddMultTranspose(
|
||||
const Vector &x, Vector &y, const double c) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int iSz = integrators.Size();
|
||||
|
||||
// do a kind of "set" rather than "add" in the below
|
||||
// operation as compared to the BilinearForm case
|
||||
// * G operation (kinda)
|
||||
Vector xscaled(x);
|
||||
MFEM_VERIFY(x.Size() == test_multiplicity.Size(), "Input vector of wrong size");
|
||||
auto xs = xscaled.ReadWrite();
|
||||
auto tm = test_multiplicity.Read();
|
||||
MFEM_FORALL(i, x.Size(),
|
||||
{
|
||||
xs[i] *= tm[i];
|
||||
});
|
||||
SetupMultInputs(elem_restrict_test, xscaled, localTest,
|
||||
elem_restrict_trial, y, localTrial, c);
|
||||
|
||||
// * B^TD^TB operation
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultTransposePA(localTest, localTrial);
|
||||
}
|
||||
|
||||
// * G^T operation
|
||||
if (elem_restrict_trial)
|
||||
{
|
||||
tempY.SetSize(y.Size());
|
||||
elem_restrict_trial->MultTranspose(localTrial, tempY);
|
||||
y += tempY;
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("Trial ElementRestriction not defined");
|
||||
}
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::FormRectangularSystemOperator(
|
||||
const Array<int>& ess1, const Array<int>& ess2, OperatorHandle &A)
|
||||
{
|
||||
const Operator *Pi = this->GetProlongation();
|
||||
const Operator *Po = this->GetLocalOutputProlongation();
|
||||
Operator *rap = SetupRAP(Pi, Po);
|
||||
|
||||
RectangularConstrainedOperator *Arco
|
||||
= new RectangularConstrainedOperator(rap, ess1, ess2, rap != this);
|
||||
|
||||
A.Reset(Arco);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+49
-95
@@ -21,14 +21,10 @@ namespace mfem
|
||||
|
||||
class BilinearForm;
|
||||
class MixedBilinearForm;
|
||||
class DiscreteLinearOperator;
|
||||
|
||||
/// Class extending the BilinearForm class to support different AssemblyLevels.
|
||||
/** FA - Full Assembly
|
||||
PA - Partial Assembly
|
||||
EA - Element Assembly
|
||||
MF - Matrix Free
|
||||
*/
|
||||
|
||||
/** @brief Class extending the BilinearForm class to support the different
|
||||
AssemblyLevel%s. */
|
||||
class BilinearFormExtension : public Operator
|
||||
{
|
||||
protected:
|
||||
@@ -46,7 +42,6 @@ public:
|
||||
/// Get the finite element space restriction matrix
|
||||
virtual const Operator *GetRestriction() const;
|
||||
|
||||
/// Assemble at the level given for the BilinearFormExtension subclass
|
||||
virtual void Assemble() = 0;
|
||||
|
||||
virtual void AssembleDiagonal(Vector &diag) const
|
||||
@@ -63,26 +58,62 @@ public:
|
||||
virtual void Update() = 0;
|
||||
};
|
||||
|
||||
/// Data and methods for fully-assembled bilinear forms
|
||||
class FABilinearFormExtension : public BilinearFormExtension
|
||||
{
|
||||
public:
|
||||
FABilinearFormExtension(BilinearForm *form)
|
||||
: BilinearFormExtension(form) { }
|
||||
|
||||
/// TODO
|
||||
void Assemble() {}
|
||||
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
|
||||
void FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B,
|
||||
int copy_interior = 0) {}
|
||||
void Mult(const Vector &x, Vector &y) const {}
|
||||
void MultTranspose(const Vector &x, Vector &y) const {}
|
||||
void Update() {}
|
||||
~FABilinearFormExtension() {}
|
||||
};
|
||||
|
||||
/// Data and methods for element-assembled bilinear forms
|
||||
class EABilinearFormExtension : public BilinearFormExtension
|
||||
{
|
||||
public:
|
||||
EABilinearFormExtension(BilinearForm *form)
|
||||
: BilinearFormExtension(form) { }
|
||||
|
||||
/// TODO
|
||||
void Assemble() {}
|
||||
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
|
||||
void FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B,
|
||||
int copy_interior = 0) {}
|
||||
void Mult(const Vector &x, Vector &y) const {}
|
||||
void MultTranspose(const Vector &x, Vector &y) const {}
|
||||
void Update() {}
|
||||
~EABilinearFormExtension() {}
|
||||
};
|
||||
|
||||
/// Data and methods for partially-assembled bilinear forms
|
||||
class PABilinearFormExtension : public BilinearFormExtension
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace *trialFes, *testFes; // Not owned
|
||||
mutable Vector localX, localY;
|
||||
mutable Vector bdryX, bdryY;
|
||||
mutable Vector faceIntX, faceIntY;
|
||||
mutable Vector faceBdrX, faceBdrY;
|
||||
const Operator *elem_restrict; // Not owned
|
||||
const Operator *int_face_restrict_lex; // Not owned
|
||||
const Operator *bdr_face_restrict_lex; // Not owned
|
||||
Array<int> gatherMap;
|
||||
Array<int> indices;
|
||||
Array<int> offsets;
|
||||
int ndofs;
|
||||
|
||||
public:
|
||||
PABilinearFormExtension(BilinearForm*);
|
||||
|
||||
void SetupRestrictionOperators();
|
||||
void Assemble();
|
||||
void AssembleDiagonal(Vector &diag) const;
|
||||
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
|
||||
@@ -90,55 +121,14 @@ public:
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B,
|
||||
int copy_interior = 0);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
void Update();
|
||||
|
||||
mutable double timingDomain, timingBoundary;
|
||||
|
||||
protected:
|
||||
void SetupRestrictionOperators(const L2FaceValues m);
|
||||
};
|
||||
|
||||
/// Data and methods for element-assembled bilinear forms
|
||||
class EABilinearFormExtension : public PABilinearFormExtension
|
||||
{
|
||||
protected:
|
||||
int ne;
|
||||
int elemDofs;
|
||||
// The element matrices are stored row major
|
||||
Vector ea_data;
|
||||
int nf_int, nf_bdr;
|
||||
int faceDofs;
|
||||
Vector ea_data_int, ea_data_ext, ea_data_bdr;
|
||||
bool factorize_face_terms;
|
||||
|
||||
public:
|
||||
EABilinearFormExtension(BilinearForm *form);
|
||||
|
||||
void Assemble();
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
};
|
||||
|
||||
/// Data and methods for fully-assembled bilinear forms
|
||||
class FABilinearFormExtension : public EABilinearFormExtension
|
||||
{
|
||||
private:
|
||||
SparseMatrix mat;
|
||||
/// face_mat handles parallelism for DG face terms.
|
||||
SparseMatrix face_mat;
|
||||
bool use_face_mat;
|
||||
|
||||
public:
|
||||
FABilinearFormExtension(BilinearForm *form);
|
||||
|
||||
void Assemble();
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
};
|
||||
|
||||
/// Data and methods for matrix-free bilinear forms NOT YET IMPLEMENTED.
|
||||
/// Data and methods for matrix-free bilinear forms
|
||||
class MFBilinearFormExtension : public BilinearFormExtension
|
||||
{
|
||||
public:
|
||||
@@ -158,12 +148,8 @@ public:
|
||||
~MFBilinearFormExtension() {}
|
||||
};
|
||||
|
||||
/// Class extending the MixedBilinearForm class to support different AssemblyLevels.
|
||||
/** FA - Full Assembly
|
||||
PA - Partial Assembly
|
||||
EA - Element Assembly
|
||||
MF - Matrix Free
|
||||
*/
|
||||
/** @brief Class extending the MixedBilinearForm class to support the different
|
||||
AssemblyLevel%s. */
|
||||
class MixedBilinearFormExtension : public Operator
|
||||
{
|
||||
protected:
|
||||
@@ -200,8 +186,6 @@ public:
|
||||
virtual void AddMultTranspose(const Vector &x, Vector &y,
|
||||
const double c=1.0) const = 0;
|
||||
|
||||
virtual void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const = 0;
|
||||
|
||||
virtual void Update() = 0;
|
||||
};
|
||||
|
||||
@@ -213,7 +197,7 @@ protected:
|
||||
mutable Vector localTrial, localTest, tempY;
|
||||
const Operator *elem_restrict_trial; // Not owned
|
||||
const Operator *elem_restrict_test; // Not owned
|
||||
|
||||
private:
|
||||
/// Helper function to set up inputs/outputs for Mult or MultTranspose
|
||||
void SetupMultInputs(const Operator *elem_restrict_x,
|
||||
const Vector &x, Vector &localX,
|
||||
@@ -252,40 +236,10 @@ public:
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
/// y += c*A^T*x
|
||||
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
|
||||
/// Assemble the diagonal of ADA^T for a diagonal vector D.
|
||||
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
|
||||
|
||||
/// Update internals for when a new MixedBilinearForm is given to this class
|
||||
void Update();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
This acts very much like PAMixedBilinearFormExtension, but its
|
||||
FormRectangularSystemOperator implementation emulates 'Set' rather than
|
||||
'Add' in the assembly case.
|
||||
*/
|
||||
class PADiscreteLinearOperatorExtension : public PAMixedBilinearFormExtension
|
||||
{
|
||||
public:
|
||||
PADiscreteLinearOperatorExtension(DiscreteLinearOperator *linop);
|
||||
|
||||
/// Partial assembly of all internal integrators
|
||||
void Assemble();
|
||||
|
||||
void AddMult(const Vector &x, Vector &y, const double c) const;
|
||||
|
||||
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
|
||||
|
||||
void FormRectangularSystemOperator(const Array<int>&, const Array<int>&,
|
||||
OperatorHandle& A);
|
||||
|
||||
const Operator * GetLocalOutputProlongation() const;
|
||||
|
||||
private:
|
||||
Vector test_multiplicity;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+49
-137
@@ -22,14 +22,14 @@ namespace mfem
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(fes)\n"
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
|
||||
const FiniteElementSpace&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(fes, fes)\n"
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
@@ -47,40 +47,7 @@ void BilinearFormIntegrator::AssemblePABoundaryFaces(const FiniteElementSpace&)
|
||||
|
||||
void BilinearFormIntegrator::AssembleDiagonalPA(Vector &)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
|
||||
Vector &emat,
|
||||
const bool add)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssembleEA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
|
||||
&fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
|
||||
&fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssembleEABoundaryFaces(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
|
||||
{
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA_ADAt(...)\n"
|
||||
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
@@ -92,7 +59,7 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
|
||||
|
||||
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AddMultTransposePA(...)\n"
|
||||
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
@@ -900,44 +867,6 @@ const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
/// alpha (n x u, v)
|
||||
void VectorFEBoundaryTangentIntegrator::AssembleElementMatrix
|
||||
(const FiniteElement &el, ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, Trans);
|
||||
|
||||
const int nd1 = el.GetDof();
|
||||
|
||||
DenseMatrix vshape(nd1, 3);
|
||||
DenseMatrix vshapeRotated(nd1, 3);
|
||||
|
||||
elmat.SetSize(nd1);
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Trans.SetIntPoint (&ip);
|
||||
|
||||
Vector n(3);
|
||||
CalcOrtho(Trans.Jacobian(), n);
|
||||
|
||||
el.CalcVShape(Trans, vshape);
|
||||
|
||||
for (int j=0; j<nd1; ++j)
|
||||
{
|
||||
// Set vshapeRotated(j) = n x vshape
|
||||
vshapeRotated(j, 0) = (n[1] * vshape(j, 2)) - (n[2] * vshape(j, 1));
|
||||
vshapeRotated(j, 1) = (n[2] * vshape(j, 0)) - (n[0] * vshape(j, 2));
|
||||
vshapeRotated(j, 2) = (n[0] * vshape(j, 1)) - (n[1] * vshape(j, 0));
|
||||
}
|
||||
|
||||
const double w = alpha * ip.weight; // Trans.Weight() is included in n
|
||||
|
||||
AddMult_a_ABt(w, vshape, vshapeRotated, elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void BoundaryMassIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
@@ -960,25 +889,22 @@ void BoundaryMassIntegrator::AssembleFaceMatrix(
|
||||
{
|
||||
int order = 2 * el1.GetOrder();
|
||||
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
// Set the integration point in the face and the neighboring element
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring element's integration point
|
||||
const IntegrationPoint &eip = Trans.GetElement1IntPoint();
|
||||
IntegrationPoint eip;
|
||||
Trans.Loc1.Transform(ip, eip);
|
||||
el1.CalcShape(eip, shape);
|
||||
|
||||
w = Trans.Weight() * ip.weight;
|
||||
Trans.Face->SetIntPoint(&ip);
|
||||
w = Trans.Face->Weight() * ip.weight;
|
||||
if (Q)
|
||||
{
|
||||
w *= Q -> Eval(Trans, ip);
|
||||
w *= Q -> Eval(*Trans.Face, ip);
|
||||
}
|
||||
|
||||
AddMult_a_VVt(w, shape, elmat);
|
||||
@@ -1563,7 +1489,6 @@ void CurlCurlIntegrator::AssembleElementMatrix
|
||||
double w;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector D;
|
||||
DenseMatrix curlshape(nd,dimc), curlshape_dFt(nd,dimc), M;
|
||||
#else
|
||||
curlshape.SetSize(nd,dimc);
|
||||
@@ -1571,7 +1496,6 @@ void CurlCurlIntegrator::AssembleElementMatrix
|
||||
#endif
|
||||
elmat.SetSize(nd);
|
||||
if (MQ) { M.SetSize(dimc); }
|
||||
if (DQ) { D.SetSize(dimc); }
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
@@ -1615,12 +1539,6 @@ void CurlCurlIntegrator::AssembleElementMatrix
|
||||
Mult(curlshape_dFt, M, curlshape);
|
||||
AddMultABt(curlshape, curlshape_dFt, elmat);
|
||||
}
|
||||
else if (DQ)
|
||||
{
|
||||
DQ->Eval(D, Trans, ip);
|
||||
D *= w;
|
||||
AddMultADAt(curlshape_dFt, D, elmat);
|
||||
}
|
||||
else if (Q)
|
||||
{
|
||||
w *= Q->Eval(Trans, ip);
|
||||
@@ -2056,7 +1974,7 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
|
||||
D.SetSize(VQ ? VQ->GetVDim() : 0);
|
||||
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#endif
|
||||
DenseMatrix tmp(test_vshape.Height(), K.Width());
|
||||
DenseMatrix tmp(trial_vshape.Height(), K.Width());
|
||||
|
||||
elmat.SetSize (test_dof, trial_dof);
|
||||
|
||||
@@ -2617,23 +2535,23 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
|
||||
{
|
||||
order++;
|
||||
}
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Access the neighboring elements' integration points
|
||||
// Note: eip2 will only contain valid data if Elem2 exists
|
||||
const IntegrationPoint &eip1 = Trans.GetElement1IntPoint();
|
||||
const IntegrationPoint &eip2 = Trans.GetElement2IntPoint();
|
||||
|
||||
IntegrationPoint eip1, eip2;
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
if (ndof2)
|
||||
{
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
}
|
||||
el1.CalcShape(eip1, shape1);
|
||||
|
||||
Trans.Face->SetIntPoint(&ip);
|
||||
Trans.Elem1->SetIntPoint(&eip1);
|
||||
|
||||
u->Eval(vu, *Trans.Elem1, eip1);
|
||||
|
||||
if (dim == 1)
|
||||
@@ -2642,7 +2560,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcOrtho(Trans.Jacobian(), nor);
|
||||
CalcOrtho(Trans.Face->Jacobian(), nor);
|
||||
}
|
||||
|
||||
un = vu * nor;
|
||||
@@ -2657,6 +2575,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
|
||||
double rho_p;
|
||||
if (un >= 0.0 && ndof2)
|
||||
{
|
||||
Trans.Elem2->SetIntPoint(&eip2);
|
||||
rho_p = rho->Eval(*Trans.Elem2, eip2);
|
||||
}
|
||||
else
|
||||
@@ -2772,7 +2691,7 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
|
||||
{
|
||||
order = 2*el1.GetOrder();
|
||||
}
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
// assemble: < {(Q \nabla u).n},[v] > --> elmat
|
||||
@@ -2780,26 +2699,22 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
IntegrationPoint eip1, eip2;
|
||||
|
||||
// Set the integration point in the face and the neighboring elements
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring elements' integration points
|
||||
// Note: eip2 will only contain valid data if Elem2 exists
|
||||
const IntegrationPoint &eip1 = Trans.GetElement1IntPoint();
|
||||
const IntegrationPoint &eip2 = Trans.GetElement2IntPoint();
|
||||
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
Trans.Face->SetIntPoint(&ip);
|
||||
if (dim == 1)
|
||||
{
|
||||
nor(0) = 2*eip1.x - 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcOrtho(Trans.Jacobian(), nor);
|
||||
CalcOrtho(Trans.Face->Jacobian(), nor);
|
||||
}
|
||||
|
||||
el1.CalcShape(eip1, shape1);
|
||||
el1.CalcDShape(eip1, dshape1);
|
||||
Trans.Elem1->SetIntPoint(&eip1);
|
||||
w = ip.weight/Trans.Elem1->Weight();
|
||||
if (ndof2)
|
||||
{
|
||||
@@ -2845,8 +2760,10 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
|
||||
|
||||
if (ndof2)
|
||||
{
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
el2.CalcShape(eip2, shape2);
|
||||
el2.CalcDShape(eip2, dshape2);
|
||||
Trans.Elem2->SetIntPoint(&eip2);
|
||||
w = ip.weight/2/Trans.Elem2->Weight();
|
||||
if (!MQ)
|
||||
{
|
||||
@@ -3056,20 +2973,16 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
|
||||
{
|
||||
// a simple choice for the integration order; is this OK?
|
||||
const int order = 2 * max(el1.GetOrder(), ndofs2 ? el2.GetOrder() : 0);
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
for (int pind = 0; pind < ir->GetNPoints(); ++pind)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(pind);
|
||||
|
||||
// Set the integration point in the face and the neighboring elements
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring elements' integration points
|
||||
// Note: eip2 will only contain valid data if Elem2 exists
|
||||
const IntegrationPoint &eip1 = Trans.GetElement1IntPoint();
|
||||
const IntegrationPoint &eip2 = Trans.GetElement2IntPoint();
|
||||
IntegrationPoint eip1, eip2; // integration point in the reference space
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
Trans.Face->SetIntPoint(&ip);
|
||||
Trans.Elem1->SetIntPoint(&eip1);
|
||||
|
||||
el1.CalcShape(eip1, shape1);
|
||||
el1.CalcDShape(eip1, dshape1);
|
||||
@@ -3083,12 +2996,14 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcOrtho(Trans.Jacobian(), nor);
|
||||
CalcOrtho(Trans.Face->Jacobian(), nor);
|
||||
}
|
||||
|
||||
double w, wLM;
|
||||
if (ndofs2)
|
||||
{
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
Trans.Elem2->SetIntPoint(&eip2);
|
||||
el2.CalcShape(eip2, shape2);
|
||||
el2.CalcDShape(eip2, dshape2);
|
||||
CalcAdjugate(Trans.Elem2->Jacobian(), adjJ);
|
||||
@@ -3218,36 +3133,33 @@ void TraceJumpIntegrator::AssembleFaceMatrix(
|
||||
order += trial_face_fe.GetOrder();
|
||||
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
order += Trans.OrderW();
|
||||
order += Trans.Face->OrderW();
|
||||
}
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Access the neighboring elements' integration points
|
||||
// Note: eip2 will only contain valid data if Elem2 exists
|
||||
const IntegrationPoint &eip1 = Trans.GetElement1IntPoint();
|
||||
const IntegrationPoint &eip2 = Trans.GetElement2IntPoint();
|
||||
|
||||
IntegrationPoint eip1, eip2;
|
||||
// Trace finite element shape function
|
||||
Trans.Face->SetIntPoint(&ip);
|
||||
trial_face_fe.CalcShape(ip, face_shape);
|
||||
// Side 1 finite element shape function
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
test_fe1.CalcShape(eip1, shape1);
|
||||
Trans.Elem1->SetIntPoint(&eip1);
|
||||
if (ndof2)
|
||||
{
|
||||
// Side 2 finite element shape function
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
test_fe2.CalcShape(eip2, shape2);
|
||||
Trans.Elem2->SetIntPoint(&eip2);
|
||||
}
|
||||
w = ip.weight;
|
||||
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
w *= Trans.Weight();
|
||||
w *= Trans.Face->Weight();
|
||||
}
|
||||
face_shape *= w;
|
||||
for (i = 0; i < ndof1; i++)
|
||||
@@ -3312,7 +3224,7 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
|
||||
order = test_fe1.GetOrder() - 1;
|
||||
}
|
||||
order += trial_face_fe.GetOrder();
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
|
||||
+9
-202
@@ -17,18 +17,9 @@
|
||||
#include "fespace.hpp"
|
||||
#include "libceed/ceed.hpp"
|
||||
|
||||
//#define SETUPONHOST
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// Local maximum size of dofs and quads in 1D
|
||||
constexpr int HCURL_MAX_D1D = 5;
|
||||
constexpr int HCURL_MAX_Q1D = 6;
|
||||
|
||||
constexpr int HDIV_MAX_D1D = 5;
|
||||
constexpr int HDIV_MAX_Q1D = 6;
|
||||
|
||||
/// Abstract base class BilinearFormIntegrator
|
||||
class BilinearFormIntegrator : public NonlinearFormIntegrator
|
||||
{
|
||||
@@ -66,9 +57,6 @@ public:
|
||||
/// Assemble diagonal and add it to Vector @a diag.
|
||||
virtual void AssembleDiagonalPA(Vector &diag);
|
||||
|
||||
/// Assemble diagonal of ADA^T (A is this integrator) and add it to @a diag.
|
||||
virtual void AssembleDiagonalPA_ADAt(const Vector &D, Vector &diag);
|
||||
|
||||
/// Method for partially assembled action.
|
||||
/** Perform the action of integrator on the input @a x and add the result to
|
||||
the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
|
||||
@@ -87,25 +75,6 @@ public:
|
||||
called. */
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Method defining element assembly.
|
||||
/** The result of the element assembly is added to the @a emat Vector if
|
||||
@a add is true. Otherwise, if @a add is false, we set @a emat. */
|
||||
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add = true);
|
||||
/** Used with BilinearFormIntegrators that have different spaces. */
|
||||
// virtual void AssembleEA(const FiniteElementSpace &trial_fes,
|
||||
// const FiniteElementSpace &test_fes,
|
||||
// Vector &emat);
|
||||
|
||||
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add = true);
|
||||
|
||||
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add = true);
|
||||
|
||||
/// Given a particular Finite Element computes the element matrix elmat.
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
@@ -209,13 +178,8 @@ public:
|
||||
{ return 0.0; }
|
||||
|
||||
virtual ~BilinearFormIntegrator() { }
|
||||
|
||||
bool isBdryInteg = false;
|
||||
Array<int> *el_marker = NULL;
|
||||
};
|
||||
|
||||
/** Wraps a given @a BilinearFormIntegrator and transposes the resulting element
|
||||
matrices. See for example ex9, ex9p. */
|
||||
class TransposeIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
@@ -270,18 +234,6 @@ public:
|
||||
bfi->AddMultTransposePA(x, y);
|
||||
}
|
||||
|
||||
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add);
|
||||
|
||||
virtual ~TransposeIntegrator() { if (own_bfi) { delete bfi; } }
|
||||
};
|
||||
|
||||
@@ -1583,7 +1535,7 @@ public:
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := (-V u, Grad v) in 2D or 3D
|
||||
and where V is a vector coefficient, u is in H1 or L2 and v is in H1. */
|
||||
and where V is a vector coefficient, u is in H1 and v is in H1. */
|
||||
class MixedScalarWeakDivergenceIntegrator : public MixedScalarVectorIntegrator
|
||||
{
|
||||
public:
|
||||
@@ -1703,22 +1655,6 @@ protected:
|
||||
{
|
||||
trial_fe.CalcPhysCurlShape(Trans, shape);
|
||||
}
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
private:
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
|
||||
const DofToQuad *mapsOtest; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsCtest; ///< Not owned. DOF-to-quad map, closed.
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, dofs1D, dofs1Dtest,quad1D, testType, trialType, coeffDim;
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := (Q u, curl v) in 3D and
|
||||
@@ -1758,20 +1694,6 @@ protected:
|
||||
{
|
||||
test_fe.CalcPhysCurlShape(Trans, shape);
|
||||
}
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
private:
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, dofs1D, quad1D, testType, trialType, coeffDim;
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := - (Q u, grad v) in either
|
||||
@@ -1814,10 +1736,8 @@ protected:
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := (Q grad u, v) where Q is a
|
||||
scalar coefficient, and v is a vector with components v_i in the same (H1) space
|
||||
as u.
|
||||
|
||||
See also MixedVectorGradientIntegrator when v is in H(curl). */
|
||||
scalar coefficient, and v is a vector with components v_i in the same space
|
||||
as u. */
|
||||
class GradientIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
@@ -1965,9 +1885,6 @@ public:
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleDiagonalPA(Vector &diag);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
@@ -1975,7 +1892,7 @@ public:
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe);
|
||||
|
||||
void SetupPA(const FiniteElementSpace &fes);
|
||||
void SetupPA(const FiniteElementSpace &fes, const bool force = false);
|
||||
};
|
||||
|
||||
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
|
||||
@@ -2041,9 +1958,6 @@ public:
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleDiagonalPA(Vector &diag);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
@@ -2052,10 +1966,9 @@ public:
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans);
|
||||
|
||||
void SetupPA(const FiniteElementSpace &fes);
|
||||
void SetupPA(const FiniteElementSpace &fes, const bool force = false);
|
||||
};
|
||||
|
||||
/** Mass integrator (u, v) restricted to the boundary of a domain */
|
||||
class BoundaryMassIntegrator : public MassIntegrator
|
||||
{
|
||||
public:
|
||||
@@ -2069,20 +1982,6 @@ public:
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/// alpha (n x u, v)
|
||||
class VectorFEBoundaryTangentIntegrator : public MassIntegrator
|
||||
{
|
||||
private:
|
||||
double alpha;
|
||||
|
||||
public:
|
||||
VectorFEBoundaryTangentIntegrator(double a = 1.0) : alpha(a)
|
||||
{ }
|
||||
|
||||
void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans, DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/// alpha (q . grad u, v)
|
||||
class ConvectionIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
@@ -2112,9 +2011,6 @@ public:
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace&);
|
||||
|
||||
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &el,
|
||||
@@ -2214,25 +2110,11 @@ class VectorFEDivergenceIntegrator : public BilinearFormIntegrator
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
virtual void AddMultTransposePA(const Vector&, Vector&) const;
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector divshape, shape;
|
||||
#endif
|
||||
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *L2mapsO; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
|
||||
int dim, ne, dofs1D, L2dofs1D, quad1D;
|
||||
|
||||
public:
|
||||
VectorFEDivergenceIntegrator() { Q = NULL; }
|
||||
VectorFEDivergenceIntegrator(Coefficient &q) { Q = &q; }
|
||||
@@ -2243,8 +2125,6 @@ public:
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void AssembleDiagonalPA_ADAt(const Vector &D, Vector &diag);
|
||||
};
|
||||
|
||||
|
||||
@@ -2330,14 +2210,12 @@ class CurlCurlIntegrator: public BilinearFormIntegrator
|
||||
private:
|
||||
Vector vec, pointflux;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector D;
|
||||
DenseMatrix curlshape, curlshape_dFt, M;
|
||||
DenseMatrix vshape, projcurl;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
// PA extension
|
||||
@@ -2346,17 +2224,12 @@ protected:
|
||||
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
|
||||
|
||||
public:
|
||||
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; }
|
||||
CurlCurlIntegrator() { Q = NULL; MQ = NULL; }
|
||||
/// Construct a bilinear form integrator for Nedelec elements
|
||||
CurlCurlIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), Q(&q) { DQ = NULL; MQ = NULL; }
|
||||
CurlCurlIntegrator(VectorCoefficient &dq, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), DQ(&dq) { Q = NULL; MQ = NULL; }
|
||||
CurlCurlIntegrator(MatrixCoefficient &mq, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), MQ(&mq) { Q = NULL; DQ = NULL; }
|
||||
CurlCurlIntegrator(Coefficient &q) : Q(&q) { MQ = NULL; }
|
||||
CurlCurlIntegrator(MatrixCoefficient &m) : MQ(&m) { Q = NULL; }
|
||||
|
||||
/* Given a particular Finite Element, compute the
|
||||
element curl-curl matrix elmat */
|
||||
@@ -2434,11 +2307,8 @@ protected:
|
||||
Vector pa_data;
|
||||
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
|
||||
const DofToQuad *mapsOtest; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsCtest; ///< Not owned. DOF-to-quad map, closed.
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq, dofs1D, dofs1Dtest, quad1D, trial_fetype, test_fetype;
|
||||
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
|
||||
public:
|
||||
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
|
||||
@@ -2459,8 +2329,6 @@ public:
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace &fes);
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void AssembleDiagonalPA(Vector& diag);
|
||||
};
|
||||
@@ -2519,23 +2387,11 @@ class DivDivIntegrator: public BilinearFormIntegrator
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace &fes);
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void AssembleDiagonalPA(Vector& diag);
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector divshape;
|
||||
#endif
|
||||
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
|
||||
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, dofs1D, quad1D;
|
||||
|
||||
public:
|
||||
DivDivIntegrator() { Q = NULL; }
|
||||
DivDivIntegrator(Coefficient &q) : Q(&q) { }
|
||||
@@ -2688,15 +2544,6 @@ public:
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
virtual void AssembleEAInteriorFaces(const FiniteElementSpace& fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleEABoundaryFaces(const FiniteElementSpace& fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add);
|
||||
|
||||
static const IntegrationRule &GetRule(Geometry::Type geom, int order,
|
||||
FaceElementTransformations &T);
|
||||
|
||||
@@ -2899,33 +2746,11 @@ class DiscreteInterpolator : public BilinearFormIntegrator { };
|
||||
class GradientInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
GradientInterpolator() : fake_fe(NULL) { }
|
||||
virtual ~GradientInterpolator() { delete fake_fe; }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &h1_fe,
|
||||
const FiniteElement &nd_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{ nd_fe.ProjectGrad(h1_fe, Trans, elmat); }
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
/**
|
||||
trial_fes should be H1 Lagrange
|
||||
test_fes should be Nedelec
|
||||
*/
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
private:
|
||||
/// 1D finit element that generates and owns the 1D DofToQuad maps below
|
||||
FiniteElement * fake_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
|
||||
int dim, ne, o_dofs1D, c_dofs1D;
|
||||
};
|
||||
|
||||
|
||||
@@ -2940,24 +2765,6 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{ ran_fe.Project(dom_fe, Trans, elmat); }
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
private:
|
||||
/// 1D finit element that generates and owns the 1D DofToQuad maps below
|
||||
FiniteElement * fake_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
|
||||
int dim, ne, o_dofs1D, c_dofs1D;
|
||||
|
||||
Vector pa_data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -788,34 +788,20 @@ void ConvectionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
{
|
||||
vel = cQ->GetVec();
|
||||
}
|
||||
else if (VectorQuadratureFunctionCoefficient* cQ =
|
||||
dynamic_cast<VectorQuadratureFunctionCoefficient*>(Q))
|
||||
{
|
||||
const QuadratureFunction &qFun = cQ->GetQuadFunction();
|
||||
MFEM_VERIFY(qFun.Size() == dim * nq * ne,
|
||||
"Incompatible QuadratureFunction dimension \n");
|
||||
|
||||
MFEM_VERIFY(ir == &qFun.GetSpace()->GetElementIntRule(0),
|
||||
"IntegrationRule used within integrator and in"
|
||||
" QuadratureFunction appear to be different");
|
||||
|
||||
qFun.Read();
|
||||
vel.MakeRef(const_cast<QuadratureFunction &>(qFun),0);
|
||||
}
|
||||
else
|
||||
{
|
||||
vel.SetSize(dim * nq * ne);
|
||||
auto C = Reshape(vel.HostWrite(), dim, nq, ne);
|
||||
DenseMatrix Q_ir;
|
||||
Vector Vq(dim);
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
ElementTransformation& T = *fes.GetElementTransformation(e);
|
||||
Q->Eval(Q_ir, T, *ir);
|
||||
for (int q = 0; q < nq; ++q)
|
||||
{
|
||||
Q->Eval(Vq, T, ir->IntPoint(q));
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
C(i,q,e) = Q_ir(i,q);
|
||||
C(i,q,e) = Vq(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
// Copyright (c) 2010-2020, 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"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void EAConvectionAssemble1D(const int NE,
|
||||
const Array<double> &b,
|
||||
const Array<double> &g,
|
||||
const Vector &padata,
|
||||
Vector &eadata,
|
||||
const bool add,
|
||||
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);
|
||||
auto D = Reshape(padata.Read(), Q1D, NE);
|
||||
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, NE);
|
||||
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
|
||||
{
|
||||
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;
|
||||
double r_Gi[MQ1];
|
||||
double r_Bj[MQ1];
|
||||
for (int q = 0; q < Q1D; q++)
|
||||
{
|
||||
r_Gi[q] = G(q,MFEM_THREAD_ID(x));
|
||||
r_Bj[q] = B(q,MFEM_THREAD_ID(y));
|
||||
}
|
||||
MFEM_FOREACH_THREAD(i1,x,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j1,y,D1D)
|
||||
{
|
||||
double val = 0.0;
|
||||
for (int k1 = 0; k1 < Q1D; ++k1)
|
||||
{
|
||||
val += r_Bj[k1] * D(k1, e) * r_Gi[k1];
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
A(i1, j1, e) += val;
|
||||
}
|
||||
else
|
||||
{
|
||||
A(i1, j1, e) = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void EAConvectionAssemble2D(const int NE,
|
||||
const Array<double> &b,
|
||||
const Array<double> &g,
|
||||
const Vector &padata,
|
||||
Vector &eadata,
|
||||
const bool add,
|
||||
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);
|
||||
auto D = Reshape(padata.Read(), Q1D, Q1D, 2, NE);
|
||||
auto A = Reshape(eadata.ReadWrite(), D1D, D1D, D1D, D1D, NE);
|
||||
MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
|
||||
{
|
||||
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 r_B[MQ1][MD1];
|
||||
double r_G[MQ1][MD1];
|
||||
for (int d = 0; d < D1D; d++)
|
||||
{
|
||||
for (int q = 0; q < Q1D; q++)
|
||||
{
|
||||
r_B[q][d] = B(q,d);
|
||||
r_G[q][d] = G(q,d);
|
||||
}
|
||||
}
|
||||
MFEM_SHARED double s_D[MQ1][MQ1][2];
|
||||
MFEM_FOREACH_THREAD(k1,x,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(k2,y,Q1D)
|
||||
{
|
||||
s_D[k1][k2][0] = D(k1,k2,0,e);
|
||||
s_D[k1][k2][1] = D(k1,k2,1,e);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(i1,x,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i2,y,D1D)
|
||||
{
|
||||
for (int j1 = 0; j1 < D1D; ++j1)
|
||||
{
|
||||
for (int j2 = 0; j2 < D1D; ++j2)
|
||||
{
|
||||
double val = 0.0;
|
||||
for (int k1 = 0; k1 < Q1D; ++k1)
|
||||
{
|
||||
for (int k2 = 0; k2 < Q1D; ++k2)
|
||||
{
|
||||
val += (r_G[k1][i1] * r_B[k2][i2] * s_D[k1][k2][0]
|
||||
+ r_B[k1][i1] * r_G[k2][i2] * s_D[k1][k2][1])
|
||||
* r_B[k1][j1]* r_B[k2][j2];
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
A(i1, i2, j1, j2, e) += val;
|
||||
}
|
||||
else
|
||||
{
|
||||
A(i1, i2, j1, j2, e) = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
static void EAConvectionAssemble3D(const int NE,
|
||||
const Array<double> &b,
|
||||
const Array<double> &g,
|
||||
const Vector &padata,
|
||||
Vector &eadata,
|
||||
const bool add,
|
||||
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);
|
||||
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(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;
|
||||
double r_B[MQ1][MD1];
|
||||
double r_G[MQ1][MD1];
|
||||
for (int d = 0; d < D1D; d++)
|
||||
{
|
||||
for (int q = 0; q < Q1D; q++)
|
||||
{
|
||||
r_B[q][d] = B(q,d);
|
||||
r_G[q][d] = G(q,d);
|
||||
}
|
||||
}
|
||||
MFEM_FOREACH_THREAD(i1,x,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i2,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i3,z,D1D)
|
||||
{
|
||||
for (int j1 = 0; j1 < D1D; ++j1)
|
||||
{
|
||||
for (int j2 = 0; j2 < D1D; ++j2)
|
||||
{
|
||||
for (int j3 = 0; j3 < D1D; ++j3)
|
||||
{
|
||||
double val = 0.0;
|
||||
for (int k1 = 0; k1 < Q1D; ++k1)
|
||||
{
|
||||
for (int k2 = 0; k2 < Q1D; ++k2)
|
||||
{
|
||||
for (int k3 = 0; k3 < Q1D; ++k3)
|
||||
{
|
||||
double D0 = D(k1,k2,k3,0,e);
|
||||
double D1 = D(k1,k2,k3,1,e);
|
||||
double D2 = D(k1,k2,k3,2,e);
|
||||
val += (r_G[k1][i1] * r_B[k2][i2] * r_B[k3][i3] * D0
|
||||
+ r_B[k1][i1] * r_G[k2][i2] * r_B[k3][i3] * D1
|
||||
+ r_B[k1][i1] * r_B[k2][i2] * r_G[k3][i3] * D2)
|
||||
* r_B[k1][j1] * r_B[k2][j2] * r_B[k3][j3];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
{
|
||||
A(i1, i2, i3, j1, j2, j3, e) += val;
|
||||
}
|
||||
else
|
||||
{
|
||||
A(i1, i2, i3, j1, j2, j3, e) = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ConvectionIntegrator::AssembleEA(const FiniteElementSpace &fes,
|
||||
Vector &ea_data,
|
||||
const bool add)
|
||||
{
|
||||
AssemblePA(fes);
|
||||
const int ne = fes.GetMesh()->GetNE();
|
||||
const Array<double> &B = maps->B;
|
||||
const Array<double> &G = maps->G;
|
||||
if (dim == 1)
|
||||
{
|
||||
switch ((dofs1D << 4 ) | quad1D)
|
||||
{
|
||||
case 0x22: return EAConvectionAssemble1D<2,2>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x33: return EAConvectionAssemble1D<3,3>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x44: return EAConvectionAssemble1D<4,4>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x55: return EAConvectionAssemble1D<5,5>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x66: return EAConvectionAssemble1D<6,6>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x77: return EAConvectionAssemble1D<7,7>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x88: return EAConvectionAssemble1D<8,8>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x99: return EAConvectionAssemble1D<9,9>(ne,B,G,pa_data,ea_data,add);
|
||||
default: return EAConvectionAssemble1D(ne,B,G,pa_data,ea_data,add,
|
||||
dofs1D,quad1D);
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
switch ((dofs1D << 4 ) | quad1D)
|
||||
{
|
||||
case 0x22: return EAConvectionAssemble2D<2,2>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x33: return EAConvectionAssemble2D<3,3>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x44: return EAConvectionAssemble2D<4,4>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x55: return EAConvectionAssemble2D<5,5>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x66: return EAConvectionAssemble2D<6,6>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x77: return EAConvectionAssemble2D<7,7>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x88: return EAConvectionAssemble2D<8,8>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x99: return EAConvectionAssemble2D<9,9>(ne,B,G,pa_data,ea_data,add);
|
||||
default: return EAConvectionAssemble2D(ne,B,G,pa_data,ea_data,add,
|
||||
dofs1D,quad1D);
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch ((dofs1D << 4 ) | quad1D)
|
||||
{
|
||||
case 0x23: return EAConvectionAssemble3D<2,3>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x34: return EAConvectionAssemble3D<3,4>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x45: return EAConvectionAssemble3D<4,5>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x56: return EAConvectionAssemble3D<5,6>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x67: return EAConvectionAssemble3D<6,7>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x78: return EAConvectionAssemble3D<7,8>(ne,B,G,pa_data,ea_data,add);
|
||||
case 0x89: return EAConvectionAssemble3D<8,9>(ne,B,G,pa_data,ea_data,add);
|
||||
default: return EAConvectionAssemble3D(ne,B,G,pa_data,ea_data,add,
|
||||
dofs1D,quad1D);
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user