Compare commits
172
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
174b930544 | ||
|
|
afc10818a0 | ||
|
|
e9088d9b84 | ||
|
|
d62c094e97 | ||
|
|
9541aa6026 | ||
|
|
bf83b55578 | ||
|
|
8a4d53098b | ||
|
|
026d4834d2 | ||
|
|
d0caa1c432 | ||
|
|
603e545060 | ||
|
|
8db0c68dd2 | ||
|
|
6e6a3d9751 | ||
|
|
5f1b8614c6 | ||
|
|
a05a93c187 | ||
|
|
7864972fc1 | ||
|
|
ab73eea018 | ||
|
|
81421858f3 | ||
|
|
9f7baced30 | ||
|
|
a45d86ff2a | ||
|
|
a17c6530ff | ||
|
|
00b1ae1850 | ||
|
|
3c25eeab3d | ||
|
|
ac91aef8f9 | ||
|
|
c86860362a | ||
|
|
f5991995c3 | ||
|
|
b8264a5fb2 | ||
|
|
cf90fad7fb | ||
|
|
f8dae2249f | ||
|
|
fd7b54c41a | ||
|
|
4d8cd51cca | ||
|
|
d25c9ffff0 | ||
|
|
72204b7f01 | ||
|
|
4d037ca87a | ||
|
|
49a47922f2 | ||
|
|
f3b094d8d8 | ||
|
|
46ed6ee7b6 | ||
|
|
22849928ac | ||
|
|
ea0dfd78db | ||
|
|
27d6b2be89 | ||
|
|
7be7168769 | ||
|
|
6f637f57b0 | ||
|
|
bf542247e8 | ||
|
|
0cd6d21e8f | ||
|
|
4557ab17b4 | ||
|
|
844644957d | ||
|
|
a0dfc29c71 | ||
|
|
0446a8fa32 | ||
|
|
4256a61cd7 | ||
|
|
8f647fa4ef | ||
|
|
5da228a103 | ||
|
|
a20ec1f29f | ||
|
|
9066476c67 | ||
|
|
0c639efcd5 | ||
|
|
16cca1a724 | ||
|
|
157e3dc151 | ||
|
|
598a5994bc | ||
|
|
6a088021e7 | ||
|
|
703083a43c | ||
|
|
f45fbc53da | ||
|
|
345abd1a5a | ||
|
|
39e7705da1 | ||
|
|
154e100214 | ||
|
|
04bdfc9409 | ||
|
|
34cf1432e9 | ||
|
|
b723fd39cc | ||
|
|
eb863cff5f | ||
|
|
e9b8ecdff7 | ||
|
|
27c55c2280 | ||
|
|
21f37f99d2 | ||
|
|
63cfc40b41 | ||
|
|
73286c6f4d | ||
|
|
b3c2f05887 | ||
|
|
fd846ee3e1 | ||
|
|
eee909351d | ||
|
|
f79415ed65 | ||
|
|
462b21fe54 | ||
|
|
d3941dfb60 | ||
|
|
2dbc9ca892 | ||
|
|
a631e6909f | ||
|
|
4b1d423210 | ||
|
|
37af117694 | ||
|
|
c1485a49ed | ||
|
|
dcbf4f1850 | ||
|
|
98e9c4cc37 | ||
|
|
cdb92cefd0 | ||
|
|
6c1f52d805 | ||
|
|
eb1493dd80 | ||
|
|
0696c3d662 | ||
|
|
a16f6a2450 | ||
|
|
f01988c0cc | ||
|
|
604cfaa65a | ||
|
|
0c1db68601 | ||
|
|
e1733d5b29 | ||
|
|
7dfaaa6806 | ||
|
|
6d3d44bca6 | ||
|
|
4099390b15 | ||
|
|
69ac786a0c | ||
|
|
bf7a36735e | ||
|
|
72764aef89 | ||
|
|
13ed072d48 | ||
|
|
a472f4a1a3 | ||
|
|
c9a0b7195b | ||
|
|
5162cfb89f | ||
|
|
64494e2b24 | ||
|
|
989866a7aa | ||
|
|
c4a744154e | ||
|
|
21d7b39e0f | ||
|
|
9a98a2359e | ||
|
|
1f6158a726 | ||
|
|
0969273d99 | ||
|
|
37586c597c | ||
|
|
1486c07dad | ||
|
|
d090a456cf | ||
|
|
1017da8f51 | ||
|
|
7789aa9387 | ||
|
|
dde5c2a230 | ||
|
|
ec615d590b | ||
|
|
5d1b2d9261 | ||
|
|
038ee646ee | ||
|
|
5338aa8bb7 | ||
|
|
b6eac61043 | ||
|
|
c869398f60 | ||
|
|
ab6f3a546f | ||
|
|
beeabc53c6 | ||
|
|
f0124d2fc5 | ||
|
|
54dc1405dc | ||
|
|
677aa95e48 | ||
|
|
d0eaf9760a | ||
|
|
f2c9f8e400 | ||
|
|
a8a00821d1 | ||
|
|
67bd515ed7 | ||
|
|
ac8784e122 | ||
|
|
0d98a14863 | ||
|
|
a27ab37d62 | ||
|
|
b553390c8b | ||
|
|
f8a7405fb8 | ||
|
|
76d384d6be | ||
|
|
76382289d0 | ||
|
|
e0d52ab9f7 | ||
|
|
2214130993 | ||
|
|
2b760e8a0a | ||
|
|
ecfd6ff848 | ||
|
|
f96929cc9e | ||
|
|
dd4eb1a7b9 | ||
|
|
b2dce094f7 | ||
|
|
e96797c7db | ||
|
|
157f3e1a43 | ||
|
|
c9c181d25b | ||
|
|
9b5b9ddcbb | ||
|
|
b328746f4a | ||
|
|
da6f519346 | ||
|
|
806595ccc2 | ||
|
|
48183748ba | ||
|
|
06ccc3cc29 | ||
|
|
07be8038a2 | ||
|
|
11801d19a8 | ||
|
|
a7f4a57370 | ||
|
|
323000faee | ||
|
|
bb78d8514f | ||
|
|
fb596116ab | ||
|
|
1cf11888b2 | ||
|
|
7a2bae81d9 | ||
|
|
63889ef70f | ||
|
|
2ba5aeaa93 | ||
|
|
f581eec582 | ||
|
|
defc0b741a | ||
|
|
330764a2e7 | ||
|
|
3e4ae315e7 | ||
|
|
769e9eeba5 | ||
|
|
b0d7d3e909 | ||
|
|
3e6bcc91f8 | ||
|
|
3450a067d3 |
@@ -47,26 +47,36 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-10.15]
|
||||
target: [debug, optim]
|
||||
mpi: [sequential, parallel]
|
||||
target: [dbg, opt]
|
||||
mpi: [seq, par]
|
||||
build-system: [make]
|
||||
# 'include' allows us to
|
||||
# - add a variable without creating a new matrix dimension.
|
||||
# - add a new combination ('build-system: cmake' case here)
|
||||
hypre-target: [int32]
|
||||
# 'include' allows us to:
|
||||
# - Add a variable to all jobs without creating a new matrix dimension.
|
||||
# Codecov is defined that way.
|
||||
# - Add a new combination.
|
||||
# 'build-system: cmake' and 'hypre-target: int64'
|
||||
#
|
||||
# note: we will gather coverage info for any non-debug run except the
|
||||
# CMake build.
|
||||
include:
|
||||
- target: debug
|
||||
- target: dbg
|
||||
codecov: NO
|
||||
- target: optim
|
||||
- target: opt
|
||||
codecov: YES
|
||||
- os: ubuntu-18.04
|
||||
target: optim
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: parallel
|
||||
mpi: par
|
||||
build-system: cmake
|
||||
name: ${{ matrix.os }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.build-system }}
|
||||
hypre-target: int32
|
||||
- os: ubuntu-18.04
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-target: int64
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -92,7 +102,7 @@ jobs:
|
||||
# TODO: It would be nice to have only one step, e.g. with a dedicated
|
||||
# action, but I (@adrienbernede) don't see how at the moment.
|
||||
- name: get MPI (Linux)
|
||||
if: matrix.mpi == 'parallel' && matrix.os == 'ubuntu-18.04'
|
||||
if: matrix.mpi == 'par' && matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
sudo apt-get install mpich libmpich-dev
|
||||
export MAKE_CXX_FLAG="MPICXX=mpic++"
|
||||
@@ -103,11 +113,11 @@ jobs:
|
||||
sudo apt-get install lcov
|
||||
|
||||
- name: Set up Homebrew
|
||||
if: ( matrix.mpi == 'parallel' || matrix.codecov == 'YES' ) && matrix.os == 'macos-10.15'
|
||||
if: ( matrix.mpi == 'par' || matrix.codecov == 'YES' ) && matrix.os == 'macos-10.15'
|
||||
uses: Homebrew/actions/setup-homebrew@c4aafe8c4620bf08883dd4679c374f11e73329d3
|
||||
|
||||
- name: get MPI (MacOS)
|
||||
if: matrix.mpi == 'parallel' && matrix.os == 'macos-10.15'
|
||||
if: matrix.mpi == 'par' && matrix.os == 'macos-10.15'
|
||||
run: |
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew install openmpi
|
||||
@@ -123,39 +133,40 @@ jobs:
|
||||
# Install will only run on cache miss.
|
||||
- name: cache hypre
|
||||
id: hypre-cache
|
||||
if: matrix.mpi == 'parallel'
|
||||
if: matrix.mpi == 'par'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-v2.0
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'parallel' && steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v1.0
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.0
|
||||
with:
|
||||
hypre-archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: ${{ matrix.hypre-target }}
|
||||
|
||||
# Get Metis through cache, or build it.
|
||||
# Install will only run on cache miss.
|
||||
- name: cache metis
|
||||
id: metis-cache
|
||||
if: matrix.mpi == 'parallel'
|
||||
if: matrix.mpi == 'par'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.0
|
||||
|
||||
- name: install metis
|
||||
if: matrix.mpi == 'parallel' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v1.0
|
||||
if: matrix.mpi == 'par' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.0
|
||||
with:
|
||||
metis-archive: ${{ env.METIS_ARCHIVE }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v1.0
|
||||
uses: mfem/github-actions/build-mfem@v2.0
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
target: ${{ matrix.target }}
|
||||
@@ -168,17 +179,17 @@ jobs:
|
||||
|
||||
# Run checks (and only checks) on debug targets
|
||||
- name: checks
|
||||
if: matrix.build-system == 'make' && matrix.target == 'debug'
|
||||
if: matrix.build-system == 'make' && matrix.target == 'dbg'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make check
|
||||
|
||||
- name: unit tests
|
||||
if: matrix.build-system == 'make' && matrix.target == 'optim'
|
||||
if: matrix.build-system == 'make' && matrix.target == 'opt'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make unittest
|
||||
|
||||
- name: tests
|
||||
if: matrix.build-system == 'make' && matrix.target == 'optim'
|
||||
if: matrix.build-system == 'make' && matrix.target == 'opt'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make test
|
||||
|
||||
@@ -190,8 +201,8 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v1.0
|
||||
uses: mfem/github-actions/upload-coverage@v2.0
|
||||
with:
|
||||
name: ${{ matrix.os }}-${{ matrix.mpi }}
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
directories: "fem general linalg mesh"
|
||||
|
||||
@@ -53,32 +53,33 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.0
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@master
|
||||
uses: mfem/github-actions/build-hypre@v2.0
|
||||
with:
|
||||
hypre-archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: int32
|
||||
|
||||
- name: Cache Metis Install
|
||||
id: metis-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.0
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@master
|
||||
uses: mfem/github-actions/build-metis@v2.0
|
||||
with:
|
||||
metis-archive: ${{ env.METIS_ARCHIVE }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build-mfem
|
||||
uses: mfem/github-actions/build-mfem@master
|
||||
uses: mfem/github-actions/build-mfem@v2.0
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: optim
|
||||
|
||||
@@ -28,49 +28,24 @@ jobs:
|
||||
access_token: ${{ github.token }}
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
- name: copyright check
|
||||
id: copyright
|
||||
run: |
|
||||
cd mfem
|
||||
if git grep -l "^\(#\|//\).*\(\-2020\|\ 2010,\)" > matches.txt
|
||||
then
|
||||
echo "Please update the following files to Copyright (c) 2010-2021:"
|
||||
cat matches.txt
|
||||
exit 1
|
||||
else
|
||||
echo "No outdated copyright found."
|
||||
fi
|
||||
./config/githooks/pre-push --copyright
|
||||
|
||||
continue-on-error: true
|
||||
|
||||
- name: license check
|
||||
id: license
|
||||
run: |
|
||||
cd mfem
|
||||
if git grep -li "^\(#\|//\).*GNU\ Lesser\ General\ Public\ License" > matches.txt
|
||||
then
|
||||
echo "Please update the following files to the BSD-3 license:"
|
||||
cat matches.txt
|
||||
exit 1
|
||||
else
|
||||
echo "No GNU GPL license found."
|
||||
fi
|
||||
./config/githooks/pre-push --license
|
||||
continue-on-error: true
|
||||
|
||||
- name: release check
|
||||
id: release
|
||||
run: |
|
||||
cd mfem
|
||||
if git grep -l "^\(#\|//\).*LLNL\-CODE\-443211" > matches.txt
|
||||
then
|
||||
echo "Please update the following files to LLNL-CODE-806117:"
|
||||
cat matches.txt
|
||||
exit 1
|
||||
else
|
||||
echo "No outdated release number found."
|
||||
fi
|
||||
./config/githooks/pre-push --release
|
||||
continue-on-error: true
|
||||
|
||||
- name: wrap-up
|
||||
@@ -100,8 +75,7 @@ jobs:
|
||||
|
||||
- name: style check
|
||||
run: |
|
||||
cd tests/scripts
|
||||
./runtest code-style
|
||||
./config/githooks/pre-push --style
|
||||
|
||||
documentation:
|
||||
runs-on: ubuntu-18.04
|
||||
@@ -133,5 +107,4 @@ jobs:
|
||||
run: |
|
||||
git fetch origin master:master
|
||||
git checkout -b gh-actions-branch-history
|
||||
cd tests/scripts
|
||||
./runtest branch-history
|
||||
./config/githooks/pre-push --history
|
||||
|
||||
@@ -26,6 +26,7 @@ CMakeFiles/
|
||||
config/_config.hpp
|
||||
config/config.mk
|
||||
config/sample-runs-build.log
|
||||
config/user.mk
|
||||
doc/CodeDocumentation.conf
|
||||
doc/CodeDocumentation.html
|
||||
doc/CodeDocumentation
|
||||
|
||||
+27
-8
@@ -48,39 +48,58 @@ variables:
|
||||
TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git
|
||||
TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git
|
||||
AUTOTEST_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/autotest.git
|
||||
MFEM_DATA_REPO: https://github.com/mfem/data.git
|
||||
ARTIFACTS_DIR: artifacts
|
||||
|
||||
# The pipeline is divided into stages. Usually, these are also synchronization
|
||||
# points, however, we use "needs" keyword to express the DAG of jobs for more
|
||||
# efficiency.
|
||||
# - We use setup phase to download content outside of mfem directory.
|
||||
# - We use setup and setup_baseline phases to download content outside of mfem
|
||||
# directory.
|
||||
# - Allocate/Release is where quartz resources are allocated/released once for all.
|
||||
# - Build and Test is where we build and MFEM for multiple toolchains.
|
||||
# - Baseline_checks gathers baseline-type test suites execution
|
||||
# - Baseline_publish, only available on master, allows to update baseline
|
||||
# results
|
||||
stages:
|
||||
- setup
|
||||
- q_allocate_resources
|
||||
- q_build_and_test
|
||||
- q_release_resources
|
||||
- l_build_and_test
|
||||
- c_build_and_test
|
||||
- setup
|
||||
- setup_baseline
|
||||
- baseline_check
|
||||
- baseline_to_autotest
|
||||
- baseline_publish
|
||||
|
||||
# The setup job in setup stage don't rely on MFEM git repo. It prepares a
|
||||
# pipeline-wide working directory downloading/updating external repos.
|
||||
# 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 clones the mfem/data repo in ${BUILD_ROOT}. The build_and_test script
|
||||
# then symlinks the repo to the parent directory of the MFEM source directory.
|
||||
# Unit tests that depend on the mfem/data repo will then detect that this
|
||||
# directory is present and be enabled.
|
||||
setup:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
stage: setup
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi
|
||||
needs: []
|
||||
|
||||
# The setup_baseline job in setup stage_baseline doesn't rely on MFEM git repo.
|
||||
# It prepares a pipeline-wide working directory downloading/updating external
|
||||
# repos. 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_baseline:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
stage: setup_baseline
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
.build_and_test_on_lassen:
|
||||
extends: [.build_blueos_3_ppc64le_ib_script, .on_lassen]
|
||||
stage: l_build_and_test
|
||||
needs: []
|
||||
needs: [setup]
|
||||
|
||||
opt_mpi_cuda_xl_16_1_1_8:
|
||||
variables:
|
||||
|
||||
+2
-1
@@ -94,6 +94,7 @@ q_report_failure:
|
||||
.build_and_test_on_quartz:
|
||||
extends: [.build_toss_3_x86_64_ib_script, .on_quartz]
|
||||
stage: q_build_and_test
|
||||
needs: [setup]
|
||||
|
||||
# Build MFEM
|
||||
debug_ser_gcc_4_9_3:
|
||||
@@ -139,7 +140,7 @@ opt_par_gcc_6_1_0_pumi:
|
||||
# Baseline
|
||||
baselinecheck_mfem_intel_quartz:
|
||||
extends: [.baselinecheck_mfem, .on_quartz]
|
||||
needs: [setup]
|
||||
needs: [setup_baseline]
|
||||
|
||||
update_autotest:
|
||||
extends: [.on_quartz]
|
||||
|
||||
-469
@@ -1,469 +0,0 @@
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
language: cpp
|
||||
|
||||
os: linux
|
||||
dist: bionic
|
||||
|
||||
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:
|
||||
|
||||
# ========================
|
||||
# Checks
|
||||
# ========================
|
||||
# - code-style
|
||||
# - documentation
|
||||
# - gitignore
|
||||
|
||||
- stage: checks
|
||||
os: linux
|
||||
dist: xenial
|
||||
name: "code-style"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- astyle=2.05.1-0ubuntu1
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- cd tests/scripts
|
||||
- ./runtest code-style
|
||||
|
||||
- stage: checks
|
||||
os: linux
|
||||
name: "documentation"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- doxygen
|
||||
- graphviz
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- cd tests/scripts
|
||||
- ./runtest documentation
|
||||
|
||||
- stage: checks
|
||||
os: linux
|
||||
name: "gitignore"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- mpich
|
||||
- libmpich-dev
|
||||
env: MPI=YES
|
||||
before_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
|
||||
# ========================
|
||||
# - branch-history
|
||||
|
||||
- stage: optional
|
||||
name: "branch-history"
|
||||
if: branch != next
|
||||
# need full git history for the binary/big files check
|
||||
git:
|
||||
depth: false
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
# update master
|
||||
- git fetch origin master:master
|
||||
# checkout a branch (otherwise Travis works in detached head)
|
||||
- git checkout -b travis_tests
|
||||
- cd tests/scripts
|
||||
- ./runtest branch-history
|
||||
|
||||
# ========================
|
||||
# Linux tests
|
||||
# ========================
|
||||
# - serial + debug
|
||||
# - serial
|
||||
# - parallel + debug
|
||||
# - parallel
|
||||
|
||||
- stage: tests
|
||||
os: linux
|
||||
compiler: gcc
|
||||
name: "Linux: Serial + Debug"
|
||||
env: DEBUG=YES
|
||||
MPI=NO
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=check
|
||||
cache:
|
||||
ccache: true
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
name: "Linux: Serial"
|
||||
env: DEBUG=NO
|
||||
MPI=NO
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=test
|
||||
cache:
|
||||
ccache: true
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
name: "Linux: Parallel + Debug"
|
||||
addons:
|
||||
apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
packages:
|
||||
# GCC 4.9
|
||||
# - g++-4.9
|
||||
# MPICH
|
||||
- mpich
|
||||
- libmpich-dev
|
||||
# OpenMPI
|
||||
# - openmpi-bin
|
||||
# - libopenmpi-dev
|
||||
env: DEBUG=YES
|
||||
MPI=YES
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=check
|
||||
NPROCS=2
|
||||
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}
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
name: "Linux: Parallel"
|
||||
addons:
|
||||
apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
packages:
|
||||
# GCC 4.9
|
||||
# - g++-4.9
|
||||
# MPICH
|
||||
- mpich
|
||||
- libmpich-dev
|
||||
# OpenMPI
|
||||
# - openmpi-bin
|
||||
# - libopenmpi-dev
|
||||
env: DEBUG=NO
|
||||
MPI=YES
|
||||
CODECOV=YES
|
||||
MFEM_TEST_TARGET=test
|
||||
NPROCS=2
|
||||
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}
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
name: "Linux: Parallel (cmake)"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- mpich
|
||||
- libmpich-dev
|
||||
env: MPI=YES
|
||||
NPROCS=2
|
||||
script:
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
- mkdir ${TRAVIS_BUILD_DIR}/build
|
||||
- cd ${TRAVIS_BUILD_DIR}/build
|
||||
- cmake ..
|
||||
-DMFEM_USE_MPI=ON
|
||||
-DHYPRE_DIR=${TRAVIS_BUILD_DIR}/../$HYPRE_TOP_DIR/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/../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}
|
||||
|
||||
# ========================
|
||||
# Mac OS X tests
|
||||
# ========================
|
||||
# - serial + debug
|
||||
# - serial
|
||||
# - parallel + debug
|
||||
# - parallel
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.2
|
||||
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
|
||||
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
|
||||
compiler: clang
|
||||
name: "Mac: Parallel + Debug"
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
env: DEBUG=YES
|
||||
MPI=YES
|
||||
CODECOV=NO
|
||||
MFEM_TEST_TARGET=check
|
||||
NPROCS=4
|
||||
TMPDIR=/tmp
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
- $HOME/local-cached
|
||||
before_cache:
|
||||
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
|
||||
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
|
||||
rm -f Lib/*.{c,o}
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode11.2
|
||||
compiler: clang
|
||||
name: "Mac: Parallel"
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
env: DEBUG=NO
|
||||
MPI=YES
|
||||
CODECOV=YES
|
||||
MFEM_TEST_TARGET=test
|
||||
NPROCS=4
|
||||
TMPDIR=/tmp
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
|
||||
- $TRAVIS_BUILD_DIR/../metis-4.0
|
||||
- $HOME/local-cached
|
||||
before_cache:
|
||||
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
|
||||
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
|
||||
rm -f Lib/*.{c,o}
|
||||
|
||||
before_install:
|
||||
# No addon for brew yet, have to install OSX packages this way.
|
||||
# - if [ $TRAVIS_OS_NAME == "osx" ] && [ $MPI == "YES" ]; then
|
||||
# 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:
|
||||
- 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 &&
|
||||
mkdir openmpi-build && cd openmpi-build &&
|
||||
../openmpi-2.1.6/configure --prefix=$HOME/local-cached &&
|
||||
make -j3 all && make install;
|
||||
fi;
|
||||
PATH=$HOME/local-cached/bin:$PATH;
|
||||
cd $TRAVIS_BUILD_DIR;
|
||||
fi
|
||||
|
||||
# Update environment to find g++ 4.9 installation first.
|
||||
# - if [ $TRAVIS_OS_NAME == "linux" ]; then
|
||||
# mkdir -p latest-gcc-symlinks;
|
||||
# ln -s /usr/bin/g++-4.9 latest-gcc-symlinks/g++;
|
||||
# ln -s /usr/bin/gcc-4.9 latest-gcc-symlinks/gcc;
|
||||
# ln -s /usr/bin/gcov-4.9 latest-gcc-symlinks/gcov;
|
||||
# export PATH=$PWD/latest-gcc-symlinks:$PATH;
|
||||
# fi
|
||||
|
||||
# Install tool to upload code coverage reports to coveralls.io
|
||||
- if [ "$CODECOV" == "YES" ]; then
|
||||
export PYTHONUSERBASE=$HOME/local;
|
||||
pip install --user cpp-coveralls;
|
||||
pip install --user pyyaml;
|
||||
PATH=$HOME/local/bin:$PATH;
|
||||
fi
|
||||
|
||||
install:
|
||||
# Set MPI compilers, print compiler version
|
||||
- if [ $MPI == "YES" ]; then
|
||||
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
export MPICH_CC="$CC";
|
||||
export MPICH_CXX="$CXX";
|
||||
else
|
||||
export OMPI_CC="$CC";
|
||||
export OMPI_CXX="$CXX";
|
||||
mpic++ --showme:version;
|
||||
fi;
|
||||
mpic++ -v;
|
||||
else
|
||||
$CXX -v;
|
||||
fi
|
||||
|
||||
# Back out of the mfem directory to install the libraries
|
||||
- cd ..
|
||||
|
||||
# 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++;
|
||||
make -j3;
|
||||
cd ../..;
|
||||
else
|
||||
echo "Reusing cached $HYPRE_TOP_DIR/";
|
||||
fi;
|
||||
ln -s $HYPRE_TOP_DIR 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
|
||||
- if [ $MPI == "YES" ]; then
|
||||
if [ ! -e metis-4.0/libmetis.a ]; then
|
||||
wget https://mfem.github.io/tpls/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;
|
||||
mv metis-4.0.3 metis-4.0;
|
||||
else
|
||||
echo "Reusing cached metis-4.0/";
|
||||
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
|
||||
- $MYCXX -v
|
||||
|
||||
# Set some variables
|
||||
- cd $TRAVIS_BUILD_DIR;
|
||||
CPPFLAGS="";
|
||||
SKIP_TEST_DIRS="";
|
||||
if [ "$CODECOV" == "YES" ]; then
|
||||
CPPFLAGS="--coverage -g";
|
||||
fi;
|
||||
if [ "$TRAVIS_OS_NAME" != "linux" ] || [ "$DEBUG" == "YES" ]; then
|
||||
CPPFLAGS+=" -pedantic -Wall -Werror";
|
||||
fi
|
||||
|
||||
# Configure the library
|
||||
- make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG $MAKE_CXX_FLAG
|
||||
MFEM_MPI_NP=$NPROCS CPPFLAGS="$CPPFLAGS"
|
||||
# Show the configuration
|
||||
- make info
|
||||
# Build the library
|
||||
- make -j3
|
||||
# Build the examples and the miniapps
|
||||
- make -j3 all
|
||||
# Run tests
|
||||
- make $MFEM_TEST_TARGET SKIP_TEST_DIRS="$SKIP_TEST_DIRS"
|
||||
|
||||
after_success:
|
||||
- if [ "$CODECOV" == "YES" ]; then
|
||||
coveralls --include fem --include general --include linalg --include
|
||||
mesh --exclude /usr --gcov-options '\-lp' --root $TRAVIS_BUILD_DIR;
|
||||
fi
|
||||
@@ -173,7 +173,11 @@ Version 4.2.1 (development)
|
||||
mixed meshes. The LOR Transfer miniapp (miniapps/tools/lor-transfer.cpp) now
|
||||
supports meshes with any element geometry.
|
||||
|
||||
- Gitlab CI: use Spack (and Uberenv) to automate the build of TPLs.
|
||||
- Testing improvements:
|
||||
* Transitioned from Travis to GitHub Action for testing/CI on GitHub.
|
||||
* Effectively remove Travis from CI.
|
||||
* Use Spack (and Uberenv) to automate TPL building in LLNL GitLab tests.
|
||||
* Added a set of suggested git hooks for developers in config/githooks.
|
||||
|
||||
- Added new miniapps demonstrating: 1) the use of GSLIB for overlapping grids,
|
||||
see gslib/schwarz_ex1, and 2) coupling different physics in different domains,
|
||||
@@ -231,6 +235,15 @@ Version 4.2.1 (development)
|
||||
|
||||
- Added makefile rule to generate TAGS table for vi or Emacs users.
|
||||
|
||||
API changes
|
||||
-----------
|
||||
- Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction`
|
||||
and `L2FaceRestriction`.
|
||||
In order to conform with the semantic of `MultTranspose` in `mfem::Operator`,
|
||||
`mfem::FaceRestriction::MultTranspose` now sets instead of adding values, and
|
||||
`mfem::FaceRestriction::AddMultTranspose` should replace previous calls to
|
||||
`mfem::FaceRestriction::MultTranspose`.
|
||||
|
||||
libCEED integration improvements
|
||||
--------------------------------
|
||||
- Refactor the libCEED integration
|
||||
|
||||
+34
-17
@@ -4,7 +4,9 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
|
||||
<a href="https://travis-ci.org/mfem/mfem"><img alt="Build Status" src="https://travis-ci.org/mfem/mfem.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
|
||||
<a href="https://mfem.github.io/doxygen/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
</p>
|
||||
@@ -63,6 +65,8 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
development branches off `mfem:master`.
|
||||
- Please follow the [developer guidelines](#developer-guidelines), in particular
|
||||
with regards to documentation and code styling.
|
||||
- Please do not commit large/binary files to the central repository (use a fork
|
||||
instead).
|
||||
- Pull requests should be issued toward `mfem:master`. Make sure
|
||||
to check the items off the [Pull Request Checklist](#pull-request-checklist).
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
@@ -71,6 +75,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
reviewers to evaluate the changes.
|
||||
- The reviewers have 3 weeks to evaluate the PR and work with the author to
|
||||
fix issues and implement improvements.
|
||||
- During review there should be no force pushes/rewriting history in the branch.
|
||||
- After approval, MFEM developers merge the PR manually in the [mfem:next branch](#masternext-workflow).
|
||||
- After a week of testing in `mfem:next`, the original PR is merged in `mfem:master`.
|
||||
- We use [milestones](https://github.com/mfem/mfem/milestones) to coordinate the
|
||||
@@ -91,8 +96,9 @@ The MFEM source code has the following structure:
|
||||
```
|
||||
.
|
||||
├── config
|
||||
│ └── cmake
|
||||
│ └── ...
|
||||
│ ├── cmake
|
||||
│ │ └── ...
|
||||
│ └── githooks
|
||||
├── data
|
||||
├── doc
|
||||
├── examples
|
||||
@@ -363,6 +369,10 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
|
||||
the PR and work with the author to implement improvements and fix issues.
|
||||
|
||||
- Once the `ready-for-review` label has been applied and reviewers have been
|
||||
assigned, the PR is considered under review. To help with the review process
|
||||
there should be no force pushes/rewriting history in the branch.
|
||||
|
||||
- After approval, the PR is [tested](#masternext-workflow) for a week with
|
||||
other approved PRs in the `mfem:next` branch.
|
||||
|
||||
@@ -370,16 +380,20 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
`mfem:next`, see the [README](tests/scripts/README) file in that directory
|
||||
for more details.
|
||||
|
||||
- Track the Travis CI, Github Actions and Appveyor [continuous integration](#automated-testing)
|
||||
- Track the GitHub Actions and Appveyor [continuous integration](#automated-testing)
|
||||
builds at the end of the PR. These should generally run clean, so address any
|
||||
errors as soon as possible. Please ask if you are unsure how to do that.
|
||||
|
||||
- Note that some tests, such as the `branch-history` check in Travis and Github
|
||||
Actions are safeguards that are allowed to fail in certain cases.
|
||||
- Note that some tests, such as the `branch-history` check in GitHub Actions
|
||||
are safeguards that are allowed to fail in certain cases.
|
||||
|
||||
- Other tests, such as the `code-style`, `documentation` and `gitignore`
|
||||
checks in Travis and Github Actions enforce MFEM-specific rules which are
|
||||
explained in the error messages and the `tests/scripts` directory.
|
||||
checks in GitHub Actions enforce MFEM-specific rules which are explained in
|
||||
the error messages and the `tests/scripts` directory.
|
||||
|
||||
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
|
||||
Actions can be triggered automatically before each push using git hooks. See
|
||||
the [git hooks README](config/githooks/README.md) for a detailed explanation.
|
||||
|
||||
- If triggered, track the status of the LLNL GitLab tests. If failing, ask
|
||||
one of the _LLNL developers_ for details.
|
||||
@@ -399,7 +413,7 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
|
||||
- [ ] `.travis.yml`
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update `.gitignore`:
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
@@ -516,7 +530,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- [ ] `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Check that version requirements for each of MFEM's dependencies are documented in `INSTALL` and up-to-date
|
||||
- [ ] Check that continuous integration server configurations reflect the dependency version requirements of the new release
|
||||
- [ ] `.travis.yml`
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update the `CHANGELOG` to organize all release contributions
|
||||
- [ ] Review the whole source code once over
|
||||
@@ -578,14 +592,17 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
MFEM has several levels of automated testing running on GitHub, as well as on
|
||||
local Mac and Linux workstations, and Livermore Computing clusters at LLNL.
|
||||
|
||||
### Linux and Mac smoke tests
|
||||
We use Travis CI and Github Actions to drive the default tests on the `master`
|
||||
and `next` branches. See the `.travis` file and the logs at
|
||||
[https://travis-ci.org/mfem/mfem](https://travis-ci.org/mfem/mfem).
|
||||
In addition, developers can set local git hooks to run some quick checks on
|
||||
commit or push, see the [README](config/githooks/README.md) in the `config/githooks`
|
||||
directory.
|
||||
|
||||
Testing using Travis CI and Github Actions should be kept lightweight, as there
|
||||
is a time constraint on jobs. Two virtual machines are configured - Mac (OS X)
|
||||
and Linux.
|
||||
### Linux and Mac smoke tests
|
||||
We use GitHub Actions to drive the default tests on the `master` and `next`
|
||||
branches. See the `.github/workflows` files and the logs at
|
||||
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
|
||||
|
||||
Testing using GitHub Actions should be kept lightweight, as there is a time
|
||||
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
|
||||
|
||||
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
|
||||
- Tests on the `next` branch are currently scheduled to run each night.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
Finite Element Discretization Library
|
||||
__
|
||||
_ __ ___ / _| ___ _ __ ___
|
||||
| '_ ` _ \ | |_ / _ \| '_ ` _ \
|
||||
| | | | | || _|| __/| | | | | |
|
||||
|_| |_| |_||_| \___||_| |_| |_|
|
||||
|
||||
https://mfem.org
|
||||
|
||||
|
||||
This directory contains recommended git hooks, which are scripts that can be
|
||||
used to improve your development experience with MFEM:
|
||||
|
||||
### The hooks
|
||||
|
||||
* `pre-commit` is a hook that will be applied before each commit and run
|
||||
`astyle` on the code. This will ensure that your changes comply with the MFEM
|
||||
code styling guidelines.
|
||||
|
||||
* `pre-push` is a hook that will be applied before each push to run a quick set
|
||||
of tests that verify that your files headers are in compliance, and that you did
|
||||
not add any large files to the repo.
|
||||
|
||||
### Setup
|
||||
|
||||
To setup the git hooks, run `make hooks`, which creates symlinks to the hooks in
|
||||
the `.git/hooks` directory. Individual hooks can be enabled by manually creating
|
||||
symlinks.
|
||||
|
||||
(You may also copy the scripts directly and customize them further, but this way
|
||||
you may miss additional updates in the future.)
|
||||
|
||||
### Failures
|
||||
|
||||
The `branch-history` check can fail in some cases when the history is OK. For
|
||||
example, when a large number of files were modified for a legitimate reason, or
|
||||
when a picture was added for documentation.
|
||||
|
||||
If that is the case, make sure the failure is indeed justified, and rerun the
|
||||
push command with the `--no-verify` option. This will skip the hooks, allowing
|
||||
you to push those changes.
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Apply automated code formatting
|
||||
make -C $(git rev-parse --show-toplevel) style
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
option=${1:-""}
|
||||
|
||||
if [[ "${option}" == "--help" ]]; then
|
||||
echo "This script runs checks on the repository."
|
||||
echo "It has 2 modes: with and without an option."
|
||||
echo ""
|
||||
echo "Options are used in GitHub Actions and can be:"
|
||||
echo " --copyright"
|
||||
echo " --license"
|
||||
echo " --release"
|
||||
echo " --style"
|
||||
echo " --history"
|
||||
echo ""
|
||||
echo "As a githook, the script is used without options."
|
||||
echo "In that case, it will run all the checks except style."
|
||||
echo ""
|
||||
echo "Use --help to print this help message."
|
||||
fi
|
||||
|
||||
cd $(git rev-parse --show-toplevel)
|
||||
|
||||
# copyright check
|
||||
copyright=true
|
||||
if [[ "${option}" == "--copyright" || "${option}" == "" ]]; then
|
||||
if git grep -l "^\(#\|//\).*\(\-2020\|\ 2010,\)" > matches.txt; then
|
||||
echo "Please update the following files to Copyright (c) 2010-2021:"
|
||||
cat matches.txt
|
||||
copyright=false
|
||||
fi
|
||||
fi
|
||||
|
||||
# license check
|
||||
license=true
|
||||
if [[ "${option}" == "--license" || "${option}" == "" ]]; then
|
||||
if git grep -li "^\(#\|//\).*GNU\ Lesser\ General\ Public\ License" > matches.txt; then
|
||||
echo "Please update the following files to the BSD-3 license:"
|
||||
cat matches.txt
|
||||
license=false
|
||||
fi
|
||||
fi
|
||||
|
||||
# release check
|
||||
release=true
|
||||
if [[ "${option}" == "--release" || "${option}" == "" ]]; then
|
||||
if git grep -l "^\(#\|//\).*LLNL\-CODE\-443211" > matches.txt
|
||||
then
|
||||
echo "Please update the following files to LLNL-CODE-806117:"
|
||||
cat matches.txt
|
||||
release=false
|
||||
fi
|
||||
fi
|
||||
|
||||
# wrap-up
|
||||
code=0
|
||||
if ! $copyright ; then
|
||||
echo "copyright check failed, unroll log for details"
|
||||
code=1
|
||||
fi
|
||||
if ! $license ; then
|
||||
echo "license check failed, unroll log for details"
|
||||
code=1
|
||||
fi
|
||||
if ! $release ; then
|
||||
echo "release check failed, unroll log for details"
|
||||
code=1
|
||||
fi
|
||||
|
||||
# `code-style` is not just a check, it will actually reformat the code if
|
||||
# necessary. This means that if one pushes while the repo is in dirty state
|
||||
# (changes not staged), those changes may be mixed with format changes.
|
||||
# To activate this, you will need to hard-copy this hook script in the hook
|
||||
# directory and uncomment only then. (See README.md)
|
||||
#
|
||||
## style check
|
||||
#if [[ "${option}" == "--style" || "${option}" == "" ]]; then
|
||||
if [[ "${option}" == "--style" ]]; then
|
||||
if which astyle && [[ "$(astyle --version)" == "Artistic Style Version 2.05.1" ]]; then
|
||||
cd tests/scripts
|
||||
if ! ./runtest code-style; then code=1; fi
|
||||
cd -
|
||||
else
|
||||
echo "Warning: astyle not found or version is not 2.05.1"
|
||||
fi
|
||||
fi
|
||||
|
||||
# branch-history
|
||||
if [[ "${option}" == "--history" || "${option}" == "" ]]; then
|
||||
git fetch origin master:master
|
||||
cd tests/scripts
|
||||
if ! ./runtest branch-history; then code=1; fi
|
||||
cd -
|
||||
fi
|
||||
|
||||
exit $code
|
||||
+1
-1
@@ -105,7 +105,7 @@ private:
|
||||
Vector diag(fespace.GetTrueVSize());
|
||||
bfs.Last()->AssembleDiagonal(diag);
|
||||
|
||||
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
|
||||
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag,
|
||||
*essentialTrueDofs.Last(), 2);
|
||||
AddLevel(opr.Ptr(), smoother, true, true);
|
||||
}
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ private:
|
||||
Vector diag(fespace.GetTrueVSize());
|
||||
bfs.Last()->AssembleDiagonal(diag);
|
||||
|
||||
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
|
||||
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag,
|
||||
*essentialTrueDofs.Last(), 2, fespace.GetParMesh()->GetComm());
|
||||
|
||||
AddLevel(opr.Ptr(), smoother, true, true);
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
#include "element-smoother.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
ElementSmoother::ElementSmoother(ParFiniteElementSpace * fes_,
|
||||
Array<int> ess_bdr, Coefficient * cf_ )
|
||||
: Solver(fes_->GetTrueVSize()), fes(fes_), cf(cf_)
|
||||
{
|
||||
comm = fes->GetComm();
|
||||
MPI_Comm_size(comm, &num_procs);
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
Pr = fes->GetProlongationMatrix();
|
||||
ParMesh * pmesh = fes->GetParMesh();
|
||||
dim = pmesh->Dimension();
|
||||
eidx.SetSize(dim);
|
||||
eidx[0] = 0;
|
||||
eidx[1] = 1;
|
||||
if (dim == 3) eidx[2] = 8;
|
||||
nrelems = pmesh->GetNE();
|
||||
tpcf.SetSize(nrelems);
|
||||
int vsize = fes->GetVSize();
|
||||
ovlp_count.SetSize(vsize);
|
||||
ovlp_count = 0.0;
|
||||
// Construct overlap count for each dof &
|
||||
// count x y z edges sharing a vertex
|
||||
for (int i=0; i<nrelems; i++)
|
||||
{
|
||||
Array<int> elem_dofs;
|
||||
fes->GetElementDofs(i,elem_dofs);
|
||||
for (int j = 0; j<elem_dofs.Size(); j++)
|
||||
{
|
||||
ovlp_count(elem_dofs[j]) +=1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vector tovlp_count(fes->TrueVSize());
|
||||
if (Pr)
|
||||
{
|
||||
Pr->MultTranspose(ovlp_count,tovlp_count);
|
||||
Pr->Mult(tovlp_count, ovlp_count);
|
||||
}
|
||||
|
||||
DenseMatrix edge_counts;
|
||||
// GetVertexToEdgeCount(pmesh,edge_counts);
|
||||
double * data = edge_counts.GetData();
|
||||
|
||||
// helper H1 fespace for communication of vertex info
|
||||
H1_FECollection fec(1, dim);
|
||||
ParFiniteElementSpace aux_fes(const_cast<ParMesh *>(pmesh), &fec);
|
||||
Vector tedge_counts(aux_fes.TrueVSize());
|
||||
Vector temp(aux_fes.GetVSize());
|
||||
|
||||
|
||||
// const Operator * Ph = aux_fes.GetProlongationMatrix();
|
||||
|
||||
// if (Ph)
|
||||
// {
|
||||
// for (int d=0; d<dim; d++)
|
||||
// {
|
||||
// temp.SetData(&data[d*aux_fes.GetVSize()]);
|
||||
// Ph->MultTranspose(temp,tedge_counts);
|
||||
// Ph->Mult(tedge_counts, temp);
|
||||
// }
|
||||
// }
|
||||
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
ovlp_count(i) = 1.0/sqrt(ovlp_count(i));
|
||||
}
|
||||
|
||||
fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
TPElementTransformation TPTrans(*fes);
|
||||
|
||||
int nredges = pmesh->GetNEdges();
|
||||
edge_orient.SetSize(nredges); // orientation of edges wrt the element
|
||||
Array<DenseMatrix *> EdgeGrad(nredges);
|
||||
Array<DenseMatrix *> EdgeMass(nredges);
|
||||
Array<DenseMatrix *> AssembledEdgeGrad(nredges);
|
||||
Array<DenseMatrix *> AssembledEdgeMass(nredges);
|
||||
// Initialize
|
||||
for (int i = 0; i<nredges; i++)
|
||||
{
|
||||
AssembledEdgeGrad[i] = nullptr;
|
||||
AssembledEdgeMass[i] = nullptr;
|
||||
EdgeGrad[i] = nullptr;
|
||||
EdgeMass[i] = nullptr;
|
||||
}
|
||||
|
||||
// loop through element to calculate Edge matrices
|
||||
Array<int> emarker(nredges); emarker = 0;
|
||||
for (int iel = 0; iel<nrelems; iel++)
|
||||
{
|
||||
tpcf[iel] = new ElementTPFunctionCoefficient(*fes,iel,*cf);
|
||||
Array<int> edges, cor;
|
||||
pmesh->GetElementEdges(iel,edges,cor);
|
||||
for (int ii = 0; ii<dim; ii++)
|
||||
{
|
||||
int i = eidx[ii];
|
||||
int edge = edges[i];
|
||||
if (emarker[edge]) continue;
|
||||
edge_orient[edge] = cor[i];
|
||||
const FiniteElement * fe = fes->GetEdgeElement(edge);
|
||||
|
||||
tpcf[iel]->SetCoord(ii);
|
||||
tpcf[iel]->SetOrient(edge_orient[edge]);
|
||||
tpcf[iel]->ResetCounter(ii);
|
||||
IntegrationRule *irs = TensorIntegrationRule(1,fe->GetOrder());
|
||||
EdgeGrad[edge] = new DenseMatrix(fe->GetDof());
|
||||
EdgeMass[edge] = new DenseMatrix(fe->GetDof());
|
||||
|
||||
int j;
|
||||
Vector * Q;
|
||||
switch (ii)
|
||||
{
|
||||
case 0:
|
||||
j = 1;
|
||||
Q = tpcf[iel]->GetVecX();
|
||||
break;
|
||||
case 1:
|
||||
j = 0;
|
||||
Q = tpcf[iel]->GetVecY();
|
||||
break;
|
||||
default:
|
||||
j=2;
|
||||
Q = tpcf[iel]->GetVecZ();
|
||||
break;
|
||||
}
|
||||
|
||||
Vector * vecG = TPTrans.GetTPTransformation(iel,ii,ii);
|
||||
Vector * vecM = nullptr;
|
||||
Vector *vecM1 = nullptr;
|
||||
Vector *vecM2 = nullptr;
|
||||
if (dim == 2)
|
||||
{
|
||||
vecM = TPTrans.GetTPTransformation(iel,ii,j);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ii)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
vecM1 = TPTrans.GetTPTransformation(iel,ii,1);
|
||||
vecM2 = TPTrans.GetTPTransformation(iel,ii,2);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
vecM1 = TPTrans.GetTPTransformation(iel,ii,0);
|
||||
vecM2 = TPTrans.GetTPTransformation(iel,ii,2);
|
||||
}
|
||||
default:
|
||||
{
|
||||
vecM1 = TPTrans.GetTPTransformation(iel,ii,0);
|
||||
vecM2 = TPTrans.GetTPTransformation(iel,ii,1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
vecM = new Vector(vecM1->Size());
|
||||
for (int i=0; i<vecM->Size(); i++)
|
||||
{
|
||||
// (*vecM)(i) = ((*vecM1)(i)+(*vecM2)(i))/2.0;
|
||||
// (*vecM)(i) = (*vecM1)(i);
|
||||
(*vecM)(i) = 1.0/((1./(*vecM1)(i)+1./(*vecM2)(i))/2.0);
|
||||
}
|
||||
}
|
||||
// GetDiffusionEdgeMatrix(edge,fes,*vecG,*Q,irs,*EdgeGrad[edge],edge_orient[edge]);
|
||||
// GetMassEdgeMatrix(edge,fes,*vecM,*Q,irs,*EdgeMass[edge],edge_orient[edge]);
|
||||
Get1DMatrices(fes,edge, edge_orient[edge],
|
||||
*vecG, *vecM,*Q,irs,*EdgeGrad[edge],*EdgeMass[edge]);
|
||||
emarker[edge] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int iel=0; iel<nrelems; iel++)
|
||||
{
|
||||
Array<int> edges, cor;
|
||||
pmesh->GetElementEdges(iel,edges,cor);
|
||||
|
||||
for (int ii = 0; ii<dim; ii++)
|
||||
{
|
||||
int i = eidx[ii];
|
||||
int k = edges[i];
|
||||
const FiniteElement *fe = fes->GetEdgeElement(k);
|
||||
int ndof = fe->GetDof();
|
||||
DenseMatrix Grad(ndof); Grad = *EdgeGrad[k];
|
||||
DenseMatrix Mass(ndof); Mass = *EdgeMass[k];
|
||||
|
||||
Array<int> vert;
|
||||
pmesh->GetEdgeVertices(k,vert);
|
||||
for (int i = 0; i<2; i++)
|
||||
{
|
||||
int vertex = vert[i];
|
||||
Array<int> vertdofs;
|
||||
fes->GetVertexDofs(vertex,vertdofs);
|
||||
// double count = edge_counts(vertdofs[0],ii);
|
||||
|
||||
// Grad(i,i) *= count;
|
||||
// Mass(i,i) *= count;
|
||||
Grad(i,i) *= 2.;
|
||||
Mass(i,i) *= 2.;
|
||||
}
|
||||
const Array<int> &dmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*fe).GetDofMap();
|
||||
SparseMatrix * P = new SparseMatrix(dmap.Size());
|
||||
for (int j = 0; j<dmap.Size(); j++)
|
||||
{
|
||||
P->Set(dmap[j],j, 1.0);
|
||||
}
|
||||
P->Finalize();
|
||||
// Map from MFEM ordering to TensorProduct Ordering
|
||||
AssembledEdgeGrad[k] = RAP(Grad,*P);
|
||||
AssembledEdgeMass[k] = RAP(Mass,*P);
|
||||
delete P;
|
||||
}
|
||||
}
|
||||
|
||||
Array<Array<int> * > tmap;
|
||||
TensorProductEssentialDofsMaps(ess_tdof_list, fes, tmap, dofmap);
|
||||
|
||||
|
||||
Array<DenseMatrix * > G(nredges);
|
||||
Array<DenseMatrix * > M(nredges);
|
||||
|
||||
for (int ie = 0; ie<nredges; ie++)
|
||||
{
|
||||
if (!emarker[ie]) continue;
|
||||
if (!AssembledEdgeMass[ie])
|
||||
{
|
||||
cout << "ie = " << ie << endl;
|
||||
MFEM_ABORT("Memory allocation incosistency 2");
|
||||
}
|
||||
const FiniteElement * fe = fes->GetEdgeElement(ie);
|
||||
int n = fe->GetDof() - tmap[ie]->Size();
|
||||
G[ie] = new DenseMatrix(n);
|
||||
M[ie] = new DenseMatrix(n);
|
||||
const Array<int> &dmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*fe).GetDofMap();
|
||||
// modify tmap to use tensor product index;
|
||||
Array<int> dmapt(dmap.Size());
|
||||
for (int i = 0; i<dmap.Size(); i++)
|
||||
{
|
||||
dmapt[dmap[i]] = i;
|
||||
}
|
||||
|
||||
// Eliminate indices corresponding to tmap from matrices Grad1D_A and Mass1D_A
|
||||
// construct Map;
|
||||
Array<int> tmap_marker(fe->GetDof());
|
||||
tmap_marker = 0;
|
||||
for (int i = 0; i<tmap[ie]->Size(); i++)
|
||||
{
|
||||
int j = (*tmap[ie])[i];
|
||||
tmap_marker[j] = 1;
|
||||
}
|
||||
Array<int> dof_list;
|
||||
for (int i =0; i<fe->GetDof(); i++)
|
||||
{
|
||||
if (tmap_marker[i]) continue;
|
||||
dof_list.Append(dmapt[i]);
|
||||
}
|
||||
dof_list.Sort();
|
||||
for (int i=0; i<dof_list.Size(); i++)
|
||||
{
|
||||
int iii = (edge_orient[ie] == 1) ? i : n - i - 1;
|
||||
int ii = dof_list[i];
|
||||
for (int j=0; j<dof_list.Size(); j++)
|
||||
{
|
||||
int jjj = (edge_orient[ie] == 1) ? j : n - j - 1;
|
||||
int jj = dof_list[j];
|
||||
(*G[ie])(iii,jjj) = (*AssembledEdgeGrad[ie])(ii,jj);
|
||||
(*M[ie])(iii,jjj) = (*AssembledEdgeMass[ie])(ii,jj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elem_inv.SetSize(nrelems);
|
||||
for (int iel = 0; iel<nrelems; iel++)
|
||||
{
|
||||
elem_inv[iel] = nullptr;
|
||||
Array<int> edges, cor;
|
||||
pmesh->GetElementEdges(iel,edges,cor);
|
||||
Array<DenseMatrix *> Gv(dim);
|
||||
Array<DenseMatrix *> Mv(dim);
|
||||
bool msize = true;
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
Gv[dim - d - 1] = G[edges[eidx[d]]];
|
||||
Mv[dim - d - 1] = M[edges[eidx[d]]];
|
||||
if (Mv[dim-d-1]->Size() == 0) msize = false;
|
||||
}
|
||||
if (msize) elem_inv[iel] = new FDSolver(Gv,Mv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ElementSmoother::Mult(const Vector &r, Vector &z) const
|
||||
{
|
||||
z = r;
|
||||
z.SetSubVectorComplement(ess_tdof_list,0.0);
|
||||
Vector rnew(fes->GetVSize());
|
||||
Vector znew(fes->GetVSize());
|
||||
Vector ztemp(fes->GetTrueVSize());
|
||||
ztemp = 0.0;
|
||||
znew = 0.0;
|
||||
|
||||
// const SparseMatrix * R = fes->GetRestrictionMatrix();
|
||||
if (Pr)
|
||||
{
|
||||
Pr->Mult(r,rnew);
|
||||
}
|
||||
else
|
||||
{
|
||||
rnew = r;
|
||||
}
|
||||
for (int iel=0; iel<nrelems; iel++)
|
||||
{
|
||||
if (!elem_inv[iel]) continue;
|
||||
int n = dofmap[iel]->Size();
|
||||
|
||||
Vector rloc(n);
|
||||
rnew.GetSubVector(*dofmap[iel],rloc);
|
||||
// pre-scale
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
int j = (*dofmap[iel])[i];
|
||||
rloc[i] *= ovlp_count[j];
|
||||
}
|
||||
Vector zloc(n);
|
||||
elem_inv[iel]->Mult(rloc,zloc);
|
||||
// post-scale
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
int j = (*dofmap[iel])[i];
|
||||
zloc[i] *= ovlp_count[j];
|
||||
}
|
||||
|
||||
znew.AddElementVector(*dofmap[iel],zloc);
|
||||
}
|
||||
if (Pr)
|
||||
{
|
||||
Pr->MultTranspose(znew,ztemp);
|
||||
}
|
||||
else
|
||||
{
|
||||
ztemp = znew;
|
||||
}
|
||||
// R->Mult(znew,ztemp);
|
||||
z += ztemp;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "smoother-util.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ElementSmoother: public Solver
|
||||
{
|
||||
private:
|
||||
int num_procs, myid;
|
||||
MPI_Comm comm;
|
||||
int nrelems;
|
||||
int dim;
|
||||
ParFiniteElementSpace * fes = nullptr;
|
||||
const Operator * Pr = nullptr;
|
||||
Coefficient * cf = nullptr;
|
||||
Array<int> eidx; // edge local index
|
||||
Array<int> edge_orient; // orientation of edges wrt the element
|
||||
Array<FDSolver *> elem_inv;
|
||||
Array<int> ess_tdof_list;
|
||||
Vector ovlp_count;
|
||||
Array<Array<int> * > dofmap;
|
||||
Array<ElementTPFunctionCoefficient *> tpcf;
|
||||
public:
|
||||
ElementSmoother(ParFiniteElementSpace * fes_, Array<int> ess_bdr, Coefficient * cf_=nullptr);
|
||||
virtual void SetOperator(const Operator &op) { }
|
||||
virtual void Mult(const Vector &r, Vector &z) const;
|
||||
virtual void MultTranspose(const Vector &r, Vector &z) const { Mult(r,z); }
|
||||
virtual ~ElementSmoother(){};
|
||||
};
|
||||
@@ -0,0 +1,289 @@
|
||||
// MFEM Example 26
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "exact_sol.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include "element-smoother.hpp"
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 0. 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);
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "l-shape-benchmark.mesh";
|
||||
int init_geometric_refinements = 0;
|
||||
int pinit_geometric_refinements = 0;
|
||||
int geometric_refinements = 0;
|
||||
int order_refinements = 2;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
int order = 1;
|
||||
int solver = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order", "Finite element order.");
|
||||
args.AddOption(&solver, "-solver", "--solver", "Solver: 0:MG-Cheb-Jac, 1: MG-Cheb-ElemSmoother");
|
||||
args.AddOption(&init_geometric_refinements, "-ref", "--initial-geometric-refinements",
|
||||
"Number of serial geometric refinements defining the coarse mesh.");
|
||||
args.AddOption(&pinit_geometric_refinements, "-pref", "--initial-geometric-refinements",
|
||||
"Number of parallel geometric refinements defining the coarse mesh.");
|
||||
args.AddOption(&geometric_refinements, "-gr", "--geometric-refinements",
|
||||
"Number of geometric refinements done prior to order refinements.");
|
||||
args.AddOption(&order_refinements, "-or", "--order-refinements",
|
||||
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
|
||||
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())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
for (int l = 0; l < init_geometric_refinements; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
mesh->EnsureNCMesh();
|
||||
ParMesh * pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
mesh->Clear();
|
||||
{
|
||||
for (int l = 0; l < pinit_geometric_refinements; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
FiniteElementCollection *fec = new H1_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
|
||||
Array<int> ess_bdr;
|
||||
if(pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
}
|
||||
|
||||
|
||||
ParGridFunction x(fespace);
|
||||
FunctionCoefficient ex_coeff(lshape_exact);
|
||||
x.ProjectCoefficient(ex_coeff);
|
||||
|
||||
// -------------------------------------------------
|
||||
// Bilinear and linear forms
|
||||
// -------------------------------------------------
|
||||
ConstantCoefficient cf(1.0);
|
||||
ParBilinearForm a(fespace);
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
DiffusionIntegrator * aa = new DiffusionIntegrator(cf);
|
||||
// int order1 = fespace->GetElementOrder(0);
|
||||
IntegrationRule *irs = TensorIntegrationRule(*fespace,order);
|
||||
aa->SetIntegrationRule(*irs);
|
||||
a.AddDomainIntegrator(aa);
|
||||
|
||||
ParLinearForm b(fespace);
|
||||
FunctionCoefficient rhscf(lshape_rhs);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(rhscf));
|
||||
// -------------------------------------------------
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
visualization = false;
|
||||
}
|
||||
|
||||
sout.precision(8);
|
||||
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
L2_FECollection flux_fec(order, dim);
|
||||
ParFiniteElementSpace flux_fes(pmesh, &flux_fec, dim);
|
||||
FiniteElementCollection *smooth_flux_fec = NULL;
|
||||
ParFiniteElementSpace *smooth_flux_fes = NULL;
|
||||
smooth_flux_fec = new RT_FECollection(order-1, dim);
|
||||
smooth_flux_fes = new ParFiniteElementSpace(pmesh, smooth_flux_fec, 1);
|
||||
L2ZienkiewiczZhuEstimator estimator(*aa, x, flux_fes, *smooth_flux_fes);
|
||||
ThresholdRefiner refiner(estimator);
|
||||
refiner.SetTotalErrorFraction(0.7);
|
||||
refiner.SetNCLimit(1);
|
||||
StopWatch chrono;
|
||||
Array<double> ts0, ts1, tsol;
|
||||
int ref_amr = 20;
|
||||
Array<int> iter;
|
||||
Array<int> dofs;
|
||||
|
||||
ostringstream file_name;
|
||||
file_name << "lshape-amr_" << order << ".csv";
|
||||
ofstream conv(file_name.str().c_str());
|
||||
conv << "DOFs " << ", " << "it-Cheb-Jac" << ", " << "it-ChebElemSmoother" << endl;
|
||||
|
||||
for (int it = 0; it < ref_amr ; it++)
|
||||
{
|
||||
HYPRE_BigInt global_dofs = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\nAMR iteration " << it << endl;
|
||||
cout << "Number of unknowns: " << global_dofs << endl;
|
||||
}
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
a.Assemble();
|
||||
b.Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
chrono.Stop();
|
||||
ts0.Append(chrono.RealTime());
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
Solver * prec1 = nullptr;
|
||||
Solver * prec2 = nullptr;
|
||||
Solver * S = nullptr;
|
||||
// if (solver)
|
||||
// {
|
||||
S = new ElementSmoother(fespace,ess_bdr, &cf);
|
||||
prec1 = new OperatorChebyshevSmoother(*A, *S, 4, MPI_COMM_WORLD,7);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
Vector diag(fespace->GetTrueVSize());
|
||||
a.AssembleDiagonal(diag);
|
||||
prec2 = new OperatorChebyshevSmoother(*A, diag,ess_tdof_list, 4, MPI_COMM_WORLD,10);
|
||||
// }
|
||||
|
||||
chrono.Stop();
|
||||
ts1.Append(chrono.RealTime());
|
||||
|
||||
|
||||
|
||||
int print_level = 3;
|
||||
int max_iter = 2000;
|
||||
double rtol = 1e-8;
|
||||
|
||||
CGSolver pcg(MPI_COMM_WORLD);
|
||||
pcg.SetPrintLevel(print_level);
|
||||
pcg.SetMaxIter(max_iter);
|
||||
pcg.SetRelTol(rtol);
|
||||
pcg.SetOperator(*A);
|
||||
pcg.SetPreconditioner(*prec1);
|
||||
|
||||
|
||||
// chrono.Clear();
|
||||
// chrono.Start();
|
||||
Vector Y = X;
|
||||
pcg.Mult(B,Y);
|
||||
int iter1 = pcg.GetNumIterations();
|
||||
|
||||
|
||||
pcg.SetPreconditioner(*prec2);
|
||||
pcg.Mult(B,X);
|
||||
int iter2 = pcg.GetNumIterations();
|
||||
|
||||
// chrono.Stop();
|
||||
// tsol.Append(chrono.RealTime());
|
||||
// iter.Append(pcg.GetNumIterations());
|
||||
// dofs.Append(global_dofs);
|
||||
|
||||
delete S;
|
||||
delete prec1;
|
||||
delete prec2;
|
||||
|
||||
conv << global_dofs << ", " << iter1 << ", " << iter2 << endl;
|
||||
|
||||
|
||||
a.RecoverFEMSolution(X,b,x);
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
refiner.Apply(*pmesh);
|
||||
if (refiner.Stop())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Stopping criterion satisfied. Stop." << endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
fespace->Update();
|
||||
x.Update();
|
||||
x.ProjectCoefficient(ex_coeff);
|
||||
|
||||
a.Update();
|
||||
b.Update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (myid==0)
|
||||
{
|
||||
cout << "ts0 total = " << ts0.Sum() << endl;
|
||||
cout << "ts1 total = " << ts1.Sum() << endl;
|
||||
cout << "tsol total = " << tsol.Sum() << endl;
|
||||
}
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "num iterations = "; iter.Print(cout, iter.Size());
|
||||
cout << "dofs = "; dofs.Print(cout, dofs.Size());
|
||||
}
|
||||
|
||||
delete smooth_flux_fes;
|
||||
delete smooth_flux_fec;
|
||||
delete pmesh;
|
||||
|
||||
// 13. Free the used memory.
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,670 @@
|
||||
// MFEM Example 26
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include "element-smoother.hpp"
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class DiffusionMultigrid : public GeometricMultigrid
|
||||
{
|
||||
private:
|
||||
Coefficient * cf = nullptr;
|
||||
int smoother_kind = 0;
|
||||
// 0: Jacobi, 1:Chebychev, 2: element-smoother(matrix-free)
|
||||
HypreBoomerAMG* amg;
|
||||
|
||||
public:
|
||||
// Constructs a diffusion multigrid for the ParFiniteElementSpaceHierarchy
|
||||
// and the array of essential boundaries
|
||||
DiffusionMultigrid(ParFiniteElementSpaceHierarchy& fespaces,
|
||||
Array<int>& ess_bdr, Coefficient * cf_,int smoother_ = 0)
|
||||
: GeometricMultigrid(fespaces), cf(cf_), smoother_kind(smoother_)
|
||||
{
|
||||
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr,cf);
|
||||
|
||||
for (int level = 1; level < fespaces.GetNumLevels(); ++level)
|
||||
{
|
||||
ConstructOperatorAndSmoother(fespaces.GetFESpaceAtLevel(level), ess_bdr,cf);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~DiffusionMultigrid()
|
||||
{
|
||||
delete amg;
|
||||
}
|
||||
|
||||
private:
|
||||
void ConstructBilinearForm(ParFiniteElementSpace& fespace, Array<int>& ess_bdr,
|
||||
bool partial_assembly, Coefficient * cf)
|
||||
{
|
||||
ParBilinearForm* form = new ParBilinearForm(&fespace);
|
||||
if (partial_assembly)
|
||||
{
|
||||
form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
}
|
||||
form->AddDomainIntegrator(new DiffusionIntegrator(*cf));
|
||||
form->Assemble();
|
||||
bfs.Append(form);
|
||||
|
||||
essentialTrueDofs.Append(new Array<int>());
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, *essentialTrueDofs.Last());
|
||||
}
|
||||
|
||||
void ConstructCoarseOperatorAndSolver(ParFiniteElementSpace& coarse_fespace,
|
||||
Array<int>& ess_bdr,
|
||||
Coefficient * cf)
|
||||
{
|
||||
ConstructBilinearForm(coarse_fespace, ess_bdr, false, cf);
|
||||
|
||||
HypreParMatrix* hypreCoarseMat = new HypreParMatrix();
|
||||
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), *hypreCoarseMat);
|
||||
|
||||
amg = new HypreBoomerAMG(*hypreCoarseMat);
|
||||
amg->SetPrintLevel(-1);
|
||||
|
||||
CGSolver* pcg = new CGSolver(MPI_COMM_WORLD);
|
||||
pcg->SetPrintLevel(-1);
|
||||
pcg->SetMaxIter(10);
|
||||
pcg->SetRelTol(sqrt(1e-8));
|
||||
pcg->SetAbsTol(0.0);
|
||||
pcg->SetOperator(*hypreCoarseMat);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
|
||||
AddLevel(hypreCoarseMat, pcg, true, true);
|
||||
}
|
||||
|
||||
void ConstructOperatorAndSmoother(ParFiniteElementSpace& fespace,
|
||||
Array<int>& ess_bdr, Coefficient *cf)
|
||||
{
|
||||
ConstructBilinearForm(fespace, ess_bdr, true, cf);
|
||||
|
||||
OperatorPtr opr;
|
||||
opr.SetType(Operator::ANY_TYPE);
|
||||
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
|
||||
opr.SetOperatorOwner(false);
|
||||
Solver * smoother = nullptr;
|
||||
Vector diag;
|
||||
if (smoother_kind < 2 )
|
||||
{
|
||||
diag.SetSize(fespace.GetTrueVSize());
|
||||
bfs.Last()->AssembleDiagonal(diag);
|
||||
}
|
||||
|
||||
switch (smoother_kind)
|
||||
{
|
||||
case 0:
|
||||
smoother = new OperatorJacobiSmoother(diag,*essentialTrueDofs.Last(),0.6667);
|
||||
break;
|
||||
case 1:
|
||||
smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
|
||||
*essentialTrueDofs.Last(), 5);
|
||||
break;
|
||||
case 2:
|
||||
smoother = new ElementSmoother(&fespace,ess_bdr,cf);
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
ElementSmoother * sm = new ElementSmoother(&fespace,ess_bdr,cf);
|
||||
smoother = new OperatorChebyshevSmoother(*opr,*sm,5,fespace.GetComm());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MFEM_ABORT("Wrong smoother choice");
|
||||
break;
|
||||
}
|
||||
AddLevel(opr.Ptr(), smoother, true, true);
|
||||
}
|
||||
};
|
||||
|
||||
int dim;
|
||||
int exact = 0;
|
||||
bool tpcoeff = true;
|
||||
|
||||
double f_exact(const Vector & x);
|
||||
double u_exact(const Vector & x);
|
||||
void usol(const Vector & x, double &u, Vector & Grad, double & d2u);
|
||||
|
||||
double DiffusionCoeff(const Vector & x);
|
||||
double TPDiffusionCoeff(const Vector & x, int coord);
|
||||
void DiffusionCoeffGrad(const Vector & x, Vector & Grad);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 0. 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);
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../data/inline-quad.mesh";
|
||||
int init_geometric_refinements = 0;
|
||||
int pinit_geometric_refinements = 0;
|
||||
int geometric_refinements = 0;
|
||||
int order_refinements = 2;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
int order = 1;
|
||||
double skew_factor = 0.0;
|
||||
double scale_factor = 1.0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order", "Finite element order.");
|
||||
args.AddOption(&init_geometric_refinements, "-ref", "--initial-geometric-refinements",
|
||||
"Number of serial geometric refinements defining the coarse mesh.");
|
||||
args.AddOption(&pinit_geometric_refinements, "-pref", "--initial-geometric-refinements",
|
||||
"Number of parallel geometric refinements defining the coarse mesh.");
|
||||
args.AddOption(&geometric_refinements, "-gr", "--geometric-refinements",
|
||||
"Number of geometric refinements done prior to order refinements.");
|
||||
args.AddOption(&order_refinements, "-or", "--order-refinements",
|
||||
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
|
||||
args.AddOption(&tpcoeff, "-tpcoeff", "--tp-coefficient", "-no-tpcoeff",
|
||||
"--no-tp-coefficient", "Tensor product diffusion coefficient or not");
|
||||
args.AddOption(&exact, "-exact", "--exact", "Exact Solution flag: 0: unknown");
|
||||
args.AddOption(&skew_factor, "-c", "--skew_factor", "Skew_factor");
|
||||
args.AddOption(&scale_factor, "-s", "--scale_factor", "Scale_factor");
|
||||
|
||||
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())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
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);
|
||||
if (myid == 0) { 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 = new Mesh(mesh_file, 1, 1);
|
||||
// int nx = pow(2,init_geometric_refinements);
|
||||
// int ny = pow(2,init_geometric_refinements);
|
||||
// Mesh *mesh = new Mesh(1,1,1,mfem::Element::HEXAHEDRON,true,1.0,2.0,3.0,false);
|
||||
// Mesh *mesh = new Mesh(1,1,mfem::Element::QUADRILATERAL,true,1.0,1.0,false);
|
||||
// Mesh *mesh = new Mesh(1,4.0);
|
||||
// move nodes
|
||||
dim = mesh->Dimension();
|
||||
mesh->EnsureNodes();
|
||||
|
||||
mesh->SetCurvature(3);
|
||||
GridFunction * nodes = mesh->GetNodes();
|
||||
// *nodes +=1.0;
|
||||
// *nodes *=0.5;
|
||||
double c = skew_factor;
|
||||
double s = scale_factor;
|
||||
if (dim == 2)
|
||||
{
|
||||
for (int i=0; i<nodes->Size()/2; i++)
|
||||
{
|
||||
// double temp = (*nodes)(2*i);
|
||||
// (*nodes)(2*i) += (*nodes)(2*i)*(*nodes)(2*i) + c*pow((*nodes)(2*i+1),2);
|
||||
// (*nodes)(2*i) += c*pow((*nodes)(2*i+1),2);
|
||||
(*nodes)(2*i) += c*pow((*nodes)(2*i+1),2);
|
||||
// (*nodes)(2*i+1) = (*nodes)(2*i+1)*(*nodes)(2*i+1) + c*pow(temp,2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<nodes->Size()/3; i++)
|
||||
{
|
||||
// (*nodes)(3*i) += c*pow((*nodes)(3*i+1),2);
|
||||
// (*nodes)(3*i+1) += c*pow((*nodes)(3*i+2),3);
|
||||
(*nodes)(3*i+2) += c*pow((*nodes)(3*i),2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<nodes->Size(); i++)
|
||||
{
|
||||
(*nodes)(i) *= s;
|
||||
}
|
||||
|
||||
dim = mesh->Dimension();
|
||||
// mesh->EnsureNCMesh();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution and order
|
||||
{
|
||||
for (int l = 0; l < init_geometric_refinements; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
ParMesh * pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
mesh->Clear();
|
||||
{
|
||||
for (int l = 0; l < pinit_geometric_refinements; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream mesh_sock(vishost, visport);
|
||||
mesh_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
mesh_sock.precision(8);
|
||||
mesh_sock << "mesh\n" << *pmesh << flush;
|
||||
}
|
||||
|
||||
FiniteElementCollection *fec = new H1_FECollection(order, dim);
|
||||
ParFiniteElementSpace *coarse_fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
ParFiniteElementSpaceHierarchy fespaces(pmesh, coarse_fespace, true, true);
|
||||
|
||||
|
||||
Coefficient * cf = nullptr;
|
||||
if (exact)
|
||||
{
|
||||
cf = new FunctionCoefficient(DiffusionCoeff);
|
||||
}
|
||||
else
|
||||
{
|
||||
cf = new ConstantCoefficient(1.0);
|
||||
}
|
||||
|
||||
Array<FiniteElementCollection*> collections;
|
||||
collections.Append(fec);
|
||||
for (int level = 0; level < geometric_refinements; ++level)
|
||||
{
|
||||
fespaces.AddUniformlyRefinedLevel();
|
||||
}
|
||||
for (int level = 0; level < order_refinements; ++level)
|
||||
{
|
||||
// order++;
|
||||
order *=2;
|
||||
// collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
|
||||
collections.Append(new H1_FECollection(order, dim));
|
||||
fespaces.AddOrderRefinedLevel(collections.Last());
|
||||
}
|
||||
|
||||
HYPRE_Int size = fespaces.GetFinestFESpace().GlobalTrueVSize();
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
cout << "Order = " << order << endl;
|
||||
}
|
||||
|
||||
// 6. 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.
|
||||
FunctionCoefficient f(f_exact);
|
||||
ConstantCoefficient one(1.0);
|
||||
|
||||
|
||||
Array<int> ess_bdr;
|
||||
if(pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
}
|
||||
ParGridFunction x(&fespaces.GetFinestFESpace());
|
||||
// GridFunction gf_coeff(&fespaces.GetFinestFESpace());
|
||||
ParMesh * ref_mesh = fespaces.GetFinestFESpace().GetParMesh();
|
||||
L2_FECollection * l2fec = new L2_FECollection(order,dim);
|
||||
ParFiniteElementSpace * l2fes = new ParFiniteElementSpace(ref_mesh,l2fec);
|
||||
ParGridFunction gf_coeff(l2fes);
|
||||
// gf_coeff.ProjectCoefficient(*cf);
|
||||
gf_coeff.ProjectDiscCoefficient(*cf,mfem::GridFunction::AvgType::ARITHMETIC);
|
||||
|
||||
int print_level = 3;
|
||||
int max_iter = 2000;
|
||||
double rtol = 1e-8;
|
||||
StopWatch chrono;
|
||||
// for (int i = 0; i<=6; i++)
|
||||
for (int i = 0; i<=6; i++)
|
||||
{
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
Solver * prec = nullptr;
|
||||
CGSolver pcg(MPI_COMM_WORLD);
|
||||
pcg.SetPrintLevel(print_level);
|
||||
pcg.SetMaxIter(max_iter);
|
||||
pcg.SetRelTol(rtol);
|
||||
// i=1; Chebychev-Jacobi-MG
|
||||
// i=2; Chebychev-Element-MG
|
||||
// i=3; Chebychev-Jacobi-Smoother
|
||||
// i=4; Element-Smoother
|
||||
// i=5; Chebychev-Element-Smoother
|
||||
ParLinearForm *b = new ParLinearForm(&fespaces.GetFinestFESpace());
|
||||
if (exact)
|
||||
{
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(f));
|
||||
}
|
||||
else
|
||||
{
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
}
|
||||
b->Assemble();
|
||||
FunctionCoefficient u_ex(u_exact);
|
||||
x = 0.0;
|
||||
if (exact) x.ProjectCoefficient(u_ex);
|
||||
|
||||
if (i<4)
|
||||
{
|
||||
prec = new DiffusionMultigrid(fespaces, ess_bdr, cf,i);
|
||||
dynamic_cast<DiffusionMultigrid *>(prec)->
|
||||
SetCycleType(Multigrid::CycleType::VCYCLE, 1, 1);
|
||||
dynamic_cast<DiffusionMultigrid *>(prec)->
|
||||
FormFineLinearSystem(x, *b, A, X, B);
|
||||
if (i == 0)
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "\nJacobi-MG " << endl;
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "\nJacobi-Chebychev-MG " << endl;
|
||||
}
|
||||
else if (i == 2)
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "\nElement-Smoother-MG " << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "\nElement-Chebychev-MG " << endl;
|
||||
}
|
||||
pcg.SetOperator(*A);
|
||||
if (prec) { pcg.SetPreconditioner(*prec); }
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
pcg.Mult(B,X);
|
||||
chrono.Stop();
|
||||
if (myid == 0)
|
||||
cout<< "PCG::mult time = " << chrono.RealTime() << endl;
|
||||
// Recover the solution as a finite element grid function.
|
||||
dynamic_cast<DiffusionMultigrid *>(prec)->RecoverFineFEMSolution(X, *b, x);
|
||||
delete prec;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParBilinearForm a(&fespaces.GetFinestFESpace());
|
||||
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
DiffusionIntegrator * aa = new DiffusionIntegrator(*cf);
|
||||
int order1 = fespaces.GetFinestFESpace().GetOrder(0);
|
||||
IntegrationRule *irs = TensorIntegrationRule(fespaces.GetFinestFESpace(),order1);
|
||||
aa->SetIntegrationRule(*irs);
|
||||
a.AddDomainIntegrator(aa);
|
||||
a.Assemble();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
fespaces.GetFinestFESpace().GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
a.FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
if (i==4)
|
||||
{
|
||||
Vector diag(fespaces.GetFinestFESpace().GetTrueVSize());
|
||||
a.AssembleDiagonal(diag);
|
||||
prec = new OperatorChebyshevSmoother(A.Ptr(), diag,ess_tdof_list, 1, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
cout << "\nJacobi-Chebychev " << endl;
|
||||
}
|
||||
else if (i==5)
|
||||
{
|
||||
prec = new ElementSmoother(&fespaces.GetFinestFESpace(),ess_bdr, cf);
|
||||
if (myid == 0)
|
||||
cout << "\nElementSmoother " << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ElementSmoother *S = new ElementSmoother(&fespaces.GetFinestFESpace(),ess_bdr, cf);
|
||||
prec = new OperatorChebyshevSmoother(*A, *S, 1, MPI_COMM_WORLD);
|
||||
if (myid == 0)
|
||||
cout << "\nElement-Chebychev " << endl;
|
||||
}
|
||||
pcg.SetOperator(*A);
|
||||
if (prec) { pcg.SetPreconditioner(*prec); }
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
pcg.Mult(B,X);
|
||||
chrono.Stop();
|
||||
if (myid == 0)
|
||||
cout<< "PCG::mult time = " << chrono.RealTime() << endl;
|
||||
delete prec;
|
||||
|
||||
a.RecoverFEMSolution(X,*b,x);
|
||||
}
|
||||
delete b;
|
||||
}
|
||||
|
||||
// 12. 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" << *fespaces.GetFinestFESpace().GetMesh() << x <<
|
||||
flush;
|
||||
socketstream coeff_sock(vishost, visport);
|
||||
coeff_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
coeff_sock.precision(8);
|
||||
coeff_sock << "solution\n" << *fespaces.GetFinestFESpace().GetMesh() << gf_coeff <<
|
||||
flush;
|
||||
}
|
||||
|
||||
// 13. Free the used memory.
|
||||
for (int level = 0; level < collections.Size(); ++level)
|
||||
{
|
||||
delete collections[level];
|
||||
}
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double f_exact(const Vector & x)
|
||||
{
|
||||
// -div (f * grad(u)) = (f * gradu(0))_x + (f*gradu(1))_y + + (f*gradu(2))_z
|
||||
// = f_x*gradu(0) + f * gradu(0)_x + f_y * gradu(1) + f* gradu(1)_y + f_z * gradu(2) + f* gradu(2)_z
|
||||
// = f_x*gradu(0) + f_y * gradu(1) + f_z * gradu(2) + f*d2u
|
||||
double u;
|
||||
double d2u;
|
||||
Vector gradu;
|
||||
usol(x,u,gradu,d2u);
|
||||
Vector gradf;
|
||||
DiffusionCoeffGrad(x,gradf);
|
||||
double f = DiffusionCoeff(x);
|
||||
double val = gradf * gradu + f*d2u;
|
||||
return -val;
|
||||
}
|
||||
|
||||
double u_exact(const Vector & x)
|
||||
{
|
||||
double u;
|
||||
Vector gradu;
|
||||
double d2u;
|
||||
usol(x,u,gradu,d2u);
|
||||
return u;
|
||||
}
|
||||
|
||||
void usol(const Vector & x, double &u, Vector & Grad, double & d2u)
|
||||
{
|
||||
Grad.SetSize(dim);
|
||||
if (exact == 1)
|
||||
{
|
||||
Vector alpha(dim); alpha = 5.0;
|
||||
// Vector alpha(dim); alpha = 0.5;
|
||||
double s = alpha * x; // dot product
|
||||
u = sin(M_PI*s);
|
||||
d2u = 0.0;
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
Grad[i] = alpha(i) * M_PI * cos(M_PI*s);
|
||||
d2u += alpha(i)*alpha(i);
|
||||
}
|
||||
d2u = - M_PI*M_PI * d2u * u;
|
||||
}
|
||||
else if (exact == 2)
|
||||
{
|
||||
double c_0 = 1.2;
|
||||
double k_0 = 3.0;
|
||||
double c_1 = 2.3;
|
||||
double k_1 = 5.0;
|
||||
double c_2 = 1.3;
|
||||
double k_2 = 1.0;
|
||||
|
||||
double alpha = c_0 + k_0 * x(0);
|
||||
double beta = c_1 + k_1 * x(1);
|
||||
double gamma = 1.0;
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
u = sin(M_PI * alpha) * sin(M_PI * beta);
|
||||
Grad[0] = M_PI*k_0 * cos(alpha) * sin(M_PI*beta);
|
||||
Grad[1] = M_PI*k_1 * cos(beta) * sin(M_PI*alpha);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
gamma = c_2 + k_2 * x(2);
|
||||
u = sin(M_PI * alpha) * sin(M_PI * beta) * sin(M_PI*gamma);
|
||||
Grad[0] = M_PI*k_0 * cos(alpha) * sin(M_PI*beta) * sin(M_PI*gamma);
|
||||
Grad[1] = M_PI*k_1 * cos(beta) * sin(M_PI*alpha) * sin(M_PI*gamma);
|
||||
Grad[2] = M_PI*k_2 * cos(gamma) * sin(M_PI*alpha) * sin(M_PI*beta);
|
||||
}
|
||||
|
||||
double u_xx = - M_PI * M_PI * k_0 * k_0 * u;
|
||||
double u_yy = - M_PI * M_PI * k_1 * k_1 * u;
|
||||
double u_zz = - M_PI * M_PI * k_2 * k_2 * u;
|
||||
d2u = u_xx + u_yy;
|
||||
if (dim == 3 ) d2u += u_zz;
|
||||
}
|
||||
}
|
||||
|
||||
double TPDiffusionCoeff(const Vector & x, int coord)
|
||||
{
|
||||
double val;
|
||||
switch (coord)
|
||||
{
|
||||
case 0: val = 4.+3.*x(0); break;
|
||||
case 1: val = 0.5+7.*x(1)*x(1); break;
|
||||
case 2: val = (0.1+2.*x(2)); break;
|
||||
default:
|
||||
val = (4.+3.*x(0))*(0.5+7.*x(1)*x(1));
|
||||
if (dim == 3 ) val *= (0.1+2.*x(2));
|
||||
break;
|
||||
// case 0: val = x(0); break;
|
||||
// case 1: val = 1.0; break;
|
||||
// case 2: val = 1.0; break;
|
||||
// default: val = x(0); break;
|
||||
// case 0: val = 3.0; break;
|
||||
// case 1: val = 2.0; break;
|
||||
// case 2: val = 1.0; break;
|
||||
// default: val = 6.0; break;
|
||||
}
|
||||
return val;
|
||||
// return 2.0;
|
||||
|
||||
}
|
||||
|
||||
double DiffusionCoeff(const Vector & x)
|
||||
{
|
||||
double val;
|
||||
if (tpcoeff)
|
||||
{
|
||||
val = (4.+3.*x(0))*(0.5+7.*x(1)*x(1));
|
||||
if (dim == 3) val *= (0.1+2.*x(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// val = 2.0+cos(x.Sum());
|
||||
Vector cf(dim);
|
||||
// cf(0) = 0.1; cf(1) = 3.;
|
||||
cf(0) = 1.0; cf(1) = 2.0;
|
||||
// if (dim == 3) cf(2) = -7.8;
|
||||
if (dim == 3) cf(2) = +1.8;
|
||||
// double dd = x * cf + 1.5* x(1)*x(1);
|
||||
double dd = x * cf;
|
||||
// // double dd = x * cf;
|
||||
// // val = exp(cos(dd));
|
||||
val = exp(dd);
|
||||
|
||||
// Vector alpha(dim); alpha = 5.0;
|
||||
// double s = alpha * x; // dot product
|
||||
// val = 2.0+sin(M_PI*s);
|
||||
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void DiffusionCoeffGrad(const Vector & x, Vector & Grad)
|
||||
{
|
||||
Grad.SetSize(dim);
|
||||
if (tpcoeff)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
Grad[0] = 3.* (0.5+7.*x(1)*x(1));
|
||||
Grad[1] = 14.* x(1) * (4.+3.*x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
Grad[0] = 3.* (0.5+7.*x(1)*x(1))*(0.1+2.*x(2));
|
||||
Grad[1] = 14.* x(1) * (4.+3.*x(0))*(0.1+2.*x(2));
|
||||
Grad[2] = 2.*(4.+3.*x(0))*(0.5+7.*x(1)*x(1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector cf(dim);
|
||||
// cf(0) = 0.1; cf(1) = 3.;
|
||||
cf(0) = 1.0; cf(1) = 2.0;
|
||||
// if (dim == 3) cf(2) = -7.8;
|
||||
if (dim == 3) cf(2) = 1.8;
|
||||
// double dd = x * cf + 1.5* x(1)*x(1);
|
||||
double dd = x * cf;
|
||||
Vector alpha(dim); alpha = 5.0;
|
||||
|
||||
// for (int d = 0; d<dim; d++)
|
||||
// {
|
||||
// // // Grad[d] = -sin(x.Sum());
|
||||
// // // Grad[d] = -cf(d) * exp(cos(dd))*sin(dd);
|
||||
// // Grad[d] = cf(d) * exp(dd);
|
||||
// Grad[d] = alpha(d) * M_PI * cos(M_PI*s);
|
||||
// }
|
||||
if (dim == 2)
|
||||
{
|
||||
Grad[0] = (cf(0) )*exp(dd);
|
||||
Grad[1] = (cf(1) + 3.0*x(1))*exp(dd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Grad[0] = (cf(0) + 1.5 * x(1))*exp(dd);
|
||||
Grad[0] = cf(0)*exp(dd);
|
||||
Grad[1] = cf(1)*exp(dd);
|
||||
Grad[2] = cf(2)*exp(dd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
#include "exact_sol.hpp"
|
||||
|
||||
double lshape_exact(const Vector & pt)
|
||||
{
|
||||
double x = pt[0];
|
||||
double y = pt[1];
|
||||
double r = sqrt(x*x + y*y);
|
||||
double alpha = 2. / 3.;
|
||||
|
||||
double theta = atan2(y, x);
|
||||
if (y < 0) { theta += 2 * M_PI; }
|
||||
|
||||
return pow(r,alpha) * sin(alpha * theta);
|
||||
}
|
||||
|
||||
void lshape_grad(const Vector & x, Vector & grad)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
double lshape_rhs(const Vector & x)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double wavefront_exact(const Vector & x)
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
|
||||
void wavefront_grad(const Vector & x, Vector & grad)
|
||||
{
|
||||
|
||||
}
|
||||
double wavefront_rhs(const Vector & x)
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
double lshape_exact(const Vector & x);
|
||||
void lshape_grad(const Vector & x, Vector & grad);
|
||||
double lshape_rhs(const Vector & x);
|
||||
|
||||
double wavefront_exact(const Vector & x);
|
||||
void wavefront_grad(const Vector & x, Vector & grad);
|
||||
double wavefront_rhs(const Vector & x);
|
||||
@@ -0,0 +1,44 @@
|
||||
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
|
||||
3
|
||||
1 3 0 3 4 1
|
||||
1 3 3 6 7 4
|
||||
1 3 4 5 2 1
|
||||
|
||||
boundary
|
||||
8
|
||||
1 1 0 1
|
||||
1 1 1 2
|
||||
1 1 2 5
|
||||
2 1 5 4
|
||||
2 1 4 7
|
||||
1 1 7 6
|
||||
1 1 6 3
|
||||
1 1 3 0
|
||||
|
||||
vertices
|
||||
8
|
||||
2
|
||||
-1 1
|
||||
0 1
|
||||
1 1
|
||||
-1 0
|
||||
0 0
|
||||
1 0
|
||||
-1 -1
|
||||
0 -1
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2010-2021, 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 = $(.,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex_diffusion
|
||||
PAR_EXAMPLES = ex_diffusionp ex_amr_diffusionp
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean
|
||||
.PRECIOUS: %.o
|
||||
|
||||
COMMON_O= element-smoother.o smoother-util.o exact_sol.o
|
||||
|
||||
# Remove built-in rules
|
||||
%: %.cpp
|
||||
%.o: %.cpp
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
# Rules for building the EXAMPLES
|
||||
|
||||
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(COMMON_O) $(MFEM_LIBS)
|
||||
|
||||
# Rules for compiling miniapp dependencies
|
||||
$(COMMON_O) $($(EXAMPLES)): \
|
||||
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
@@ -0,0 +1,746 @@
|
||||
|
||||
|
||||
#include "smoother-util.hpp"
|
||||
|
||||
IntegrationRule * TensorIntegrationRule(const FiniteElementSpace & fes, int order)
|
||||
{
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
IntegrationRule * ir;
|
||||
int ir_order = 2*order+2;
|
||||
IntegrationRules IntRule(0, Quadrature1D::GaussLobatto);
|
||||
// IntegrationRules IntRule(0, Quadrature1D::GaussLegendre);
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SEGMENT, ir_order));
|
||||
break;
|
||||
case 2:
|
||||
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SQUARE, ir_order));
|
||||
break;
|
||||
default:
|
||||
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::CUBE, ir_order));
|
||||
break;
|
||||
}
|
||||
return ir;
|
||||
}
|
||||
IntegrationRule * TensorIntegrationRule(int dim, int order)
|
||||
{
|
||||
IntegrationRule * ir;
|
||||
int ir_order = 2*order+2;
|
||||
// IntegrationRules IntRule(0, Quadrature1D::GaussLegendre);
|
||||
IntegrationRules IntRule(0, Quadrature1D::GaussLobatto);
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SEGMENT, ir_order));
|
||||
break;
|
||||
case 2:
|
||||
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SQUARE, ir_order));
|
||||
break;
|
||||
default:
|
||||
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::CUBE, ir_order));
|
||||
break;
|
||||
}
|
||||
return ir;
|
||||
}
|
||||
|
||||
void KronMult(const Vector & x, const Vector & y, Vector & z)
|
||||
{
|
||||
int n = x.Size();
|
||||
int m = y.Size();
|
||||
z.SetSize(n*m);
|
||||
for (int i=0; i<n; i++)
|
||||
{
|
||||
for (int j = 0; j<m; j++)
|
||||
{
|
||||
z(i*m+j) = x(i)*y(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KronMult(const Vector & x, const Vector & y, const Vector & z, Vector & w)
|
||||
{
|
||||
Vector xy;
|
||||
KronMult(x,y,xy);
|
||||
KronMult(xy,z,w);
|
||||
}
|
||||
|
||||
void AlterLS(DenseMatrix & T, Vector & vecA, Vector & vecB)
|
||||
{
|
||||
int n = T.Height();
|
||||
int m = T.Width();
|
||||
Vector x(n);
|
||||
Vector temp(m);
|
||||
double s;
|
||||
Vector y(m); y.Randomize(1); y /= y.Norml2();
|
||||
int maxit = 3;
|
||||
for (int i=0; i<maxit; i++)
|
||||
{
|
||||
T.Mult(y,x); x /= x.Norml2();
|
||||
T.MultTranspose(x,temp); y=temp; y/= y.Norml2();
|
||||
s = InnerProduct(y,temp);
|
||||
}
|
||||
vecA = x; vecA *= sqrt(s);
|
||||
vecB = y; vecB *= sqrt(s);
|
||||
}
|
||||
|
||||
void AlterLS(DenseTensor & T, Vector & vecA, Vector & vecB, Vector & vecC)
|
||||
{
|
||||
int n = T.SizeI();
|
||||
int m = T.SizeJ();
|
||||
int l = T.SizeK();
|
||||
|
||||
DenseMatrix A0(n,l*m);
|
||||
DenseMatrix A1(m,l*n);
|
||||
DenseMatrix A2(l,n*m);
|
||||
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
for (int j=0; j<m; ++j)
|
||||
{
|
||||
for (int k=0; k<l; ++k)
|
||||
{
|
||||
A0(i,j+k*m) = T(i,j,k);
|
||||
A1(j,i+k*n) = T(i,j,k);
|
||||
A2(k,i+j*n) = T(i,j,k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double s;
|
||||
Vector x(n);
|
||||
Vector temp;
|
||||
// Vector temp;
|
||||
Vector y(m); y.Randomize(1); y/=y.Norml2();
|
||||
Vector z(l); z.Randomize(2); z/=z.Norml2();
|
||||
|
||||
int maxit = 3;
|
||||
for (int i = 0; i<maxit; i++)
|
||||
{
|
||||
KronMult(z,y,temp); A0.Mult(temp,x); s=x.Norml2(); x/=s;
|
||||
KronMult(z,x,temp); A1.Mult(temp,y); s=y.Norml2(); y/=s;
|
||||
KronMult(y,x,temp); A2.Mult(temp,z); s=z.Norml2(); z/=s;
|
||||
}
|
||||
vecA = x; vecA *= cbrt(s);
|
||||
vecB = y; vecB *= cbrt(s);
|
||||
vecC = z; vecC *= cbrt(s);
|
||||
}
|
||||
|
||||
ElementTPFunctionCoefficient::ElementTPFunctionCoefficient(FiniteElementSpace &fes, int iel, Coefficient &cf)
|
||||
{
|
||||
coeff_avg = 0.0;
|
||||
dim = fes.GetMesh()->Dimension();
|
||||
ElementTransformation * Tr;
|
||||
const IntegrationRule * ir;
|
||||
const FiniteElement * fe = fes.GetFE(iel);
|
||||
ir = TensorIntegrationRule(fes,fe->GetOrder());
|
||||
int nint1D = (dim == 2) ? sqrt(ir->GetNPoints()) : cbrt(ir->GetNPoints());
|
||||
int nrintx = nint1D;
|
||||
int nrinty = nint1D;
|
||||
int nrintz = (dim ==2) ? 0 : nint1D;
|
||||
if (dim == 2)
|
||||
{
|
||||
A.SetSize(nrintx,nrinty);
|
||||
Tr = fes.GetElementTransformation(iel);
|
||||
nint = ir->GetNPoints();
|
||||
for (int i = 0; i < nint; i++)
|
||||
{
|
||||
int nint1D = sqrt(ir->GetNPoints());
|
||||
int iy = i/nint1D;
|
||||
int ix = i%nint1D;
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Vector x(dim);
|
||||
Tr->Transform(ip,x);
|
||||
double val = cf.Eval(*Tr, ip);
|
||||
A(ix,iy) = val;
|
||||
coeff_avg += val;
|
||||
}
|
||||
AlterLS(A,VecX,VecY);
|
||||
coeff_avg /= (double)nint;
|
||||
}
|
||||
else
|
||||
{
|
||||
T.SetSize(nrintx,nrinty,nrintz);
|
||||
Tr = fes.GetElementTransformation(iel);
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
int nint1D = cbrt(ir->GetNPoints());
|
||||
int iz = i/(nint1D*nint1D);
|
||||
int iy = (i - iz*nint1D*nint1D)/nint1D;
|
||||
int ix = (i - iz*nint1D*nint1D)%nint1D;
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Tr->SetIntPoint (&ip);
|
||||
double val = cf.Eval(*Tr, ip);
|
||||
T(ix,iy,iz) = val;
|
||||
}
|
||||
AlterLS(T,VecX,VecY,VecZ);
|
||||
}
|
||||
delete ir;
|
||||
}
|
||||
|
||||
|
||||
double ElementTPFunctionCoefficient::Eval(ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{
|
||||
double val = 0.0;
|
||||
switch (coord)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (orient == 1)
|
||||
{
|
||||
val = VecX(nintx++);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nend = VecX.Size();
|
||||
val = VecX(nend-1-nintx++);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
if (orient == 1)
|
||||
{
|
||||
val = VecY(ninty++);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nend = VecY.Size();
|
||||
val = VecY(nend-1-ninty++);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
if (orient == 1)
|
||||
{
|
||||
val = VecZ(nintz++);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nend = VecZ.Size();
|
||||
val = VecZ(nend-1-nintz++);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
{
|
||||
int nint1D = sqrt(nint);
|
||||
int iy = mint/nint1D;
|
||||
int ix = mint%nint1D;
|
||||
val = VecX(ix) * VecY(iy);
|
||||
mint++;
|
||||
}
|
||||
break;
|
||||
case -2:
|
||||
{
|
||||
int nint1D = cbrt(nint);
|
||||
int iz = mint/(nint1D*nint1D);
|
||||
int iy = (mint - iz*nint1D*nint1D)/nint1D;
|
||||
int ix = (mint - iz*nint1D*nint1D)%nint1D;
|
||||
val = VecX(ix) * VecY(iy) * VecZ(iz);
|
||||
mint++;
|
||||
}
|
||||
break;
|
||||
default: MFEM_ABORT("ElementTPFunctionCoefficient::Eval: Wrong coord choice");
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
void TPElementTransformation::Setup2D()
|
||||
{
|
||||
Mesh * mesh = fes->GetMesh();
|
||||
MFEM_VERIFY(dim == 2, "Wrong dimension");
|
||||
int nel = mesh->GetNE();
|
||||
TransA1D.SetSize(nel,dim);
|
||||
TransB1D.SetSize(nel,dim);
|
||||
// Get ElementTransformations for the 2D elements
|
||||
for (int iel = 0; iel <nel; iel++)
|
||||
{
|
||||
// allocate memory for Trans1D
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
TransA1D[iel][d] = new Vector;
|
||||
TransB1D[iel][d] = new Vector;
|
||||
}
|
||||
ElementTransformation * T = mesh->GetElementTransformation(iel);
|
||||
// Populate integration points and get the K = adj(J)/sqrt(detJ);
|
||||
// Store K_11^2 + K_12^2
|
||||
// K_21^2 + K_22^2
|
||||
|
||||
const FiniteElement * fe = fes->GetFE(iel);
|
||||
const IntegrationRule * ir = TensorIntegrationRule(*fes,fe->GetOrder());
|
||||
int nint = ir->GetNPoints();
|
||||
int nint1D = sqrt(nint);
|
||||
DenseMatrix A, B;
|
||||
A.SetSize(nint1D,nint1D);
|
||||
B.SetSize(nint1D,nint1D);
|
||||
|
||||
for (int i = 0; i < nint; i++)
|
||||
{
|
||||
int iy = i/nint1D;
|
||||
int ix = i%nint1D;
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
double detJ = T->Weight();
|
||||
const DenseMatrix & adjJ = T->AdjugateJacobian();
|
||||
DenseMatrix JtJ(adjJ.Height());
|
||||
MultAtB(adjJ,adjJ,JtJ);
|
||||
JtJ *= 1.0/abs(detJ);
|
||||
// DenseMatrix adjtt(adjJ.Height());
|
||||
// MultAtB(adjJ, adjJ, adjtt);
|
||||
double valA = pow(abs(adjJ(0,0))+abs(adjJ(0,1)),2)/abs(detJ);
|
||||
// double valA = abs(JtJ(0,0))+abs(JtJ(0,1));
|
||||
double valB = pow(abs(adjJ(1,1))+abs(adjJ(1,0)),2)/abs(detJ);
|
||||
// double valB = abs(JtJ(1,1))+abs(JtJ(1,0));
|
||||
A(ix,iy) = valA;
|
||||
B(ix,iy) = valB;
|
||||
}
|
||||
AlterLS(A,*TransA1D[iel][0],*TransA1D[iel][1]);
|
||||
AlterLS(B,*TransB1D[iel][0],*TransB1D[iel][1]);
|
||||
}
|
||||
}
|
||||
void TPElementTransformation::Setup3D()
|
||||
{
|
||||
Mesh * mesh = fes->GetMesh();
|
||||
MFEM_VERIFY(dim == 3, "Wrong dimension");
|
||||
int nel = mesh->GetNE();
|
||||
TransA1D.SetSize(nel,dim);
|
||||
TransB1D.SetSize(nel,dim);
|
||||
TransC1D.SetSize(nel,dim);
|
||||
// Get ElementTransformations for the 3D elements
|
||||
for (int iel = 0; iel <nel; iel++)
|
||||
{
|
||||
// allocate memory for Trans1D
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
TransA1D[iel][d] = new Vector;
|
||||
TransB1D[iel][d] = new Vector;
|
||||
TransC1D[iel][d] = new Vector;
|
||||
}
|
||||
ElementTransformation * T = mesh->GetElementTransformation(iel);
|
||||
// Populate integrations points and get the K = adj(J)/sqrt(detJ);
|
||||
// Store K_11^2 + K_12^2 + K_13^2
|
||||
// K_21^2 + K_22^2 + K_23^2
|
||||
// K_31^2 + K_32^2 + K_33^2
|
||||
|
||||
const FiniteElement * fe = fes->GetFE(iel);
|
||||
const IntegrationRule * ir = TensorIntegrationRule(*fes,fe->GetOrder());
|
||||
int nint = ir->GetNPoints();
|
||||
int nint1D = cbrt(nint);
|
||||
DenseTensor A, B, C;
|
||||
A.SetSize(nint1D,nint1D,nint1D);
|
||||
B.SetSize(nint1D,nint1D,nint1D);
|
||||
C.SetSize(nint1D,nint1D,nint1D);
|
||||
|
||||
for (int i = 0; i < nint; i++)
|
||||
{
|
||||
int iz = i/(nint1D*nint1D);
|
||||
int iy = (i - iz*nint1D*nint1D)/nint1D;
|
||||
int ix = (i - iz*nint1D*nint1D)%nint1D;
|
||||
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
T->SetIntPoint(&ip);
|
||||
|
||||
double detJ = T->Weight();
|
||||
const DenseMatrix & adjJ = T->AdjugateJacobian();
|
||||
// DenseMatrix JtJ(adjJ.Height());
|
||||
// MultAtB(adjJ,adjJ,JtJ);
|
||||
// JtJ *= 1.0/abs(detJ);
|
||||
// double valA = abs(JtJ(0,0))+abs(JtJ(0,1))+abs(JtJ(0,2));
|
||||
double valA = pow(abs(adjJ(0,0))+abs(adjJ(0,1))+abs(adjJ(0,2)),2)/abs(detJ);
|
||||
// double valB = abs(JtJ(1,0))+abs(JtJ(1,1))+abs(JtJ(1,2));
|
||||
double valB = pow(abs(adjJ(1,0))+abs(adjJ(1,1))+abs(adjJ(1,2)),2)/abs(detJ);
|
||||
// double valC = abs(JtJ(2,0))+abs(JtJ(2,1))+abs(JtJ(2,2));
|
||||
double valC = pow(abs(adjJ(2,0))+abs(adjJ(2,1))+abs(adjJ(2,2)),2)/abs(detJ);
|
||||
A(ix,iy,iz) = valA;
|
||||
B(ix,iy,iz) = valB;
|
||||
C(ix,iy,iz) = valC;
|
||||
}
|
||||
AlterLS(A,*TransA1D[iel][0],*TransA1D[iel][1],*TransA1D[iel][2]);
|
||||
AlterLS(B,*TransB1D[iel][0],*TransB1D[iel][1],*TransB1D[iel][2]);
|
||||
AlterLS(C,*TransC1D[iel][0],*TransC1D[iel][1],*TransC1D[iel][2]);
|
||||
// for (int i = 0; i<dim; i++)
|
||||
// {
|
||||
// cout << "TransA["<<i<<"] = "; TransA1D[iel][i]->Print(cout, TransA1D[iel][i]->Size());
|
||||
// cout << "TransB["<<i<<"] = "; TransB1D[iel][i]->Print(cout, TransB1D[iel][i]->Size());
|
||||
// cout << "TransC["<<i<<"] = "; TransC1D[iel][i]->Print(cout, TransC1D[iel][i]->Size());
|
||||
// }
|
||||
// cin.get();
|
||||
}
|
||||
}
|
||||
|
||||
TPElementTransformation::TPElementTransformation(FiniteElementSpace &fes_)
|
||||
: fes(&fes_)
|
||||
{
|
||||
dim = fes->GetMesh()->Dimension();
|
||||
if (dim == 2)
|
||||
{
|
||||
Setup2D();
|
||||
}
|
||||
else
|
||||
{
|
||||
Setup3D();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GetVertexToEdgeCount(const Mesh * mesh, DenseMatrix & edge_counts)
|
||||
{
|
||||
// serial
|
||||
int dim = mesh->Dimension();
|
||||
int nv = mesh->GetNV();
|
||||
int ne = mesh->GetNEdges();
|
||||
int nel = mesh->GetNE();
|
||||
|
||||
Array<int> ibeg(dim), iend(dim), inc(dim);
|
||||
// loop through axis
|
||||
for (int axis = 0; axis<dim; axis++)
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case 0: ibeg[axis] = 0; inc[axis] = 2; iend[axis] = (dim == 2) ? 4 : 8 ; break; // "x" edges
|
||||
case 1: ibeg[axis] = 1; inc[axis] = 2; iend[axis] = (dim == 2) ? 4 : 8 ; break; // "y" edges
|
||||
case 2: ibeg[axis] = 8; inc[axis] = 1; iend[axis] = 12; break; // "z edges"
|
||||
default: MFEM_ABORT("This should be unreachable"); break;
|
||||
}
|
||||
}
|
||||
|
||||
Array<bool> edge_marker(ne);
|
||||
edge_counts.SetSize(nv,dim);
|
||||
edge_counts = 0.0;
|
||||
Array<int> edge_owned;
|
||||
bool par = false;
|
||||
#ifdef MFEM_USE_MPI
|
||||
const ParMesh * pmesh = dynamic_cast<const ParMesh *>(mesh);
|
||||
if (pmesh) par = true;
|
||||
#endif
|
||||
edge_owned.SetSize(ne); edge_owned = 0;
|
||||
ND_FECollection fec(1, dim);
|
||||
if (par)
|
||||
{
|
||||
ParFiniteElementSpace aux_fes(const_cast<ParMesh *>(pmesh), &fec);
|
||||
int mytoffset = aux_fes.GetMyTDofOffset();
|
||||
int tsize = aux_fes.GetTrueVSize();
|
||||
Array<int> dofs;
|
||||
for (int i=0; i<ne; ++i)
|
||||
{
|
||||
aux_fes.GetEdgeDofs(i, dofs);
|
||||
const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
|
||||
int sign = aux_fes.GetLocalTDofNumber(ldof);
|
||||
if (sign == -1) continue; // just a hack for now to work with AMR, need to rethink this.
|
||||
int gdof = aux_fes.GetGlobalTDofNumber(ldof);
|
||||
if (gdof >= mytoffset && gdof < mytoffset+tsize)
|
||||
{
|
||||
edge_owned[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
edge_owned = 1;
|
||||
if (mesh->Nonconforming())
|
||||
{
|
||||
NCMesh * ncmesh = mesh->ncmesh;
|
||||
const mfem::NCMesh::NCList &nclist = ncmesh->GetEdgeList();
|
||||
Array<mfem::NCMesh::Slave> ncslaves = nclist.slaves;
|
||||
int ns = ncslaves.Size();
|
||||
for (int i=0; i<ns; ++i)
|
||||
{
|
||||
int j = ncslaves[i].index;
|
||||
edge_owned[j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
edge_marker = false;
|
||||
for (int iel = 0; iel<nel; iel++)
|
||||
{
|
||||
Array<int> elem_edges, cor;
|
||||
mesh->GetElementEdges(iel,elem_edges,cor);
|
||||
for (int i = ibeg[d]; i<iend[d]; i += inc[d])
|
||||
{
|
||||
int edge = elem_edges[i];
|
||||
if (edge_owned.Size())
|
||||
{
|
||||
// skip contributions from edge not owned by the proc
|
||||
if (!edge_owned[edge]) continue;
|
||||
}
|
||||
if (edge_marker[edge]) continue;
|
||||
Array<int>edge_vert;
|
||||
mesh->GetEdgeVertices(edge,edge_vert);
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
int vertex = edge_vert[j];
|
||||
edge_counts(vertex,d) += 1.0;
|
||||
}
|
||||
edge_marker[edge] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetDiffusionEdgeMatrix(int iedge, FiniteElementSpace * fes,
|
||||
Vector & Jac1D, Vector & Coeff1D,
|
||||
const IntegrationRule *ir,
|
||||
DenseMatrix &elmat, int orient)
|
||||
{
|
||||
const FiniteElement * el = fes->GetEdgeElement(iedge);
|
||||
int nd = el->GetDof();
|
||||
int dim = el->GetDim();
|
||||
|
||||
DenseMatrix dshape(nd,dim);
|
||||
elmat.SetSize(nd);
|
||||
elmat = 0.0;
|
||||
int nint = ir->GetNPoints();
|
||||
|
||||
for (int i = 0; i < nint; i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
double w = ip.weight;
|
||||
el->CalcDShape(ip, dshape);
|
||||
int j = orient == -1 ? nint-i-1 : i;
|
||||
double val = Coeff1D(j) * Jac1D(i);
|
||||
w *= val;
|
||||
AddMult_a_AAt(w, dshape, elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void GetMassEdgeMatrix(int iedge, FiniteElementSpace * fes,
|
||||
Vector & Jac1D, Vector & Coeff1D,
|
||||
const IntegrationRule *ir,
|
||||
DenseMatrix &elmat, int orient)
|
||||
{
|
||||
const FiniteElement * el = fes->GetEdgeElement(iedge);
|
||||
int nd = el->GetDof();
|
||||
Vector shape(nd);
|
||||
elmat.SetSize(nd);
|
||||
elmat = 0.0;
|
||||
int nint = ir->GetNPoints();
|
||||
for (int i = 0; i < nint; i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
double w = ip.weight;
|
||||
el->CalcShape(ip, shape);
|
||||
int j = orient == -1 ? nint-i-1 : i;
|
||||
double val = Coeff1D(j) * Jac1D(i);
|
||||
w *= val;
|
||||
AddMult_a_VVt(w, shape, elmat);
|
||||
}
|
||||
}
|
||||
|
||||
void Get1DMatrices(FiniteElementSpace * fes, int iedge, int orient,
|
||||
Vector & JacL, Vector & JacM, Vector & Coeff1D,
|
||||
const IntegrationRule *ir,
|
||||
DenseMatrix &L, DenseMatrix & M)
|
||||
{
|
||||
const FiniteElement * el = fes->GetEdgeElement(iedge);
|
||||
int dim = el->GetDim();
|
||||
int nd = el->GetDof();
|
||||
|
||||
DenseMatrix dshape(nd,dim);
|
||||
Vector shape(nd);
|
||||
L.SetSize(nd); L = 0.0;
|
||||
M.SetSize(nd); M = 0.0;
|
||||
int nint = ir->GetNPoints();
|
||||
for (int i = 0; i < nint; i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
double w = ip.weight;
|
||||
el->CalcDShape(ip, dshape);
|
||||
el->CalcShape(ip, shape);
|
||||
int j = orient == -1 ? nint-i-1 : i;
|
||||
double wL = w*Coeff1D(j) * JacL(j);
|
||||
double wM = w*Coeff1D(j) * JacM(j);
|
||||
AddMult_a_AAt(wL, dshape, L);
|
||||
AddMult_a_VVt(wM, shape, M);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TensorProductEssentialDofsMaps(const Array<int> & ess_tdof_list,
|
||||
const ParFiniteElementSpace * fes,
|
||||
Array<Array<int> *> & tmap, // local edge map
|
||||
Array<Array<int>* > & non_ess_dofs) // element map
|
||||
{
|
||||
|
||||
MPI_Comm comm = fes->GetComm();
|
||||
int num_procs,myid;
|
||||
MPI_Comm_size(comm, &num_procs);
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
|
||||
// 1. Find the element local dofs that are essential and then identify
|
||||
// edge vertices (in local numbering) that are produced from
|
||||
// Gather the vertex dofs to be eliminated for each edge in tmap.
|
||||
// 2. Reconstruct the essential dofs for each element (this list might be different
|
||||
// for each element e.g. l-shape/fichera mesh, where to keep the kronecker product
|
||||
// structure an essential dof is not eliminated, see vertex (v) below)
|
||||
// . . .
|
||||
// . . .
|
||||
// . . v . .
|
||||
// . . . . .
|
||||
// . . . . .
|
||||
// ----------------------------------------------------------------------
|
||||
//
|
||||
ParMesh * pmesh = fes->GetParMesh();
|
||||
int dim = pmesh->Dimension();
|
||||
int nredges = pmesh->GetNEdges();
|
||||
int tsize = fes->GetTrueVSize();
|
||||
int vsize = fes->GetVSize();
|
||||
Vector tess_tdof_marker(tsize); tess_tdof_marker = 0.0;
|
||||
Vector ess_tdof_marker(vsize);
|
||||
|
||||
for (int i = 0; i<ess_tdof_list.Size(); i++)
|
||||
{
|
||||
int tdof = ess_tdof_list[i];
|
||||
|
||||
tess_tdof_marker[tdof] = 1.0;
|
||||
}
|
||||
|
||||
fes->GetProlongationMatrix()->Mult(tess_tdof_marker, ess_tdof_marker);
|
||||
|
||||
tmap.SetSize(nredges);
|
||||
for (int i = 0; i<nredges; i++) { tmap[i] = new Array<int>(0); }
|
||||
int nel = pmesh->GetNE();
|
||||
non_ess_dofs.SetSize(nel);
|
||||
for (int iel = 0; iel<nel; iel++)
|
||||
{
|
||||
non_ess_dofs[iel] = new Array<int>(0);
|
||||
Array<int> local_dofs;
|
||||
Array<int> local_tdofs;
|
||||
const FiniteElement &fe = *fes->GetFE(iel);
|
||||
// mfem to Tensor basis map
|
||||
const Array<int> &dmap =
|
||||
dynamic_cast<const TensorBasisElement&>(fe).GetDofMap();
|
||||
Array<int> dmapt(dmap.Size());
|
||||
for (int i = 0; i<dmapt.Size(); i++)
|
||||
{
|
||||
dmapt[dmap[i]] = i;
|
||||
}
|
||||
Array<int> elem_dofs;
|
||||
fes->GetElementDofs(iel,elem_dofs);
|
||||
// get local index of ess_dofs
|
||||
int n = elem_dofs.Size();
|
||||
// loop through the vertices
|
||||
for (int i = 0; i< elem_dofs.Size(); i++)
|
||||
{
|
||||
int ldof = elem_dofs[i];
|
||||
if (!ess_tdof_marker[ldof]) // if not essential dof
|
||||
{
|
||||
local_dofs.Append(dmapt[i]); // append in local element dofs
|
||||
local_tdofs.Append(ldof); // append in local element dofs
|
||||
}
|
||||
}
|
||||
|
||||
// Find the possible vertex local dofs on the edges to be removed
|
||||
int n1D = (dim == 2) ? sqrt(n) : cbrt(n);
|
||||
Array<int> edges, cor;
|
||||
pmesh->GetElementEdges(iel,edges,cor);
|
||||
Array<int> eidx(dim);
|
||||
eidx[0] = 0;
|
||||
eidx[1] = 1;
|
||||
if (dim == 3) eidx[2] = 8;
|
||||
// mark edge local dofs
|
||||
Array<Array<int> *> markers(dim);
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
Array<int> marker(n1D); marker = 0;
|
||||
for (int i = 0; i<local_dofs.Size(); i++)
|
||||
{
|
||||
int j = local_dofs[i];
|
||||
int c = j/(n1D*n1D);
|
||||
int l = c*n1D*n1D;
|
||||
int k = (d == 0) ? (j-l)%n1D : d == 1 ? (j-l)/n1D : c;
|
||||
marker[k] = 1;
|
||||
}
|
||||
markers[d] = new Array<int>(marker);
|
||||
// pick up the edge and orientation
|
||||
int edge = edges[eidx[d]];
|
||||
int orient = cor[eidx[d]];
|
||||
const FiniteElement &fe = *fes->GetEdgeElement(edge);
|
||||
// edge tensor product map
|
||||
const Array<int> &emap =
|
||||
dynamic_cast<const TensorBasisElement&>(fe).GetDofMap();
|
||||
Array<int> edge_ldofs;
|
||||
for (int i = 0; i<marker.Size(); i++)
|
||||
{
|
||||
if (!marker[i])
|
||||
{
|
||||
if (orient == 1)
|
||||
{
|
||||
edge_ldofs.Append(emap[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int k = (emap[i] == 1) ? 0 : 1;
|
||||
edge_ldofs.Append(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
edge_ldofs.Sort(); edge_ldofs.Unique();
|
||||
tmap[edge]->Append(edge_ldofs);
|
||||
tmap[edge]->Sort();
|
||||
tmap[edge]->Unique();
|
||||
}
|
||||
if (dim == 2)
|
||||
{
|
||||
for (int j = 0; j<n1D; j++)
|
||||
{
|
||||
if ((*markers[1])[j])
|
||||
{
|
||||
for (int i = 0; i<n1D; i++)
|
||||
{
|
||||
if ((*markers[0])[i])
|
||||
{
|
||||
int ldof = n1D*j+i;
|
||||
non_ess_dofs[iel]->Append(ldof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int k = 0; k<n1D; k++)
|
||||
{
|
||||
if ((*markers[2])[k])
|
||||
{
|
||||
for (int j = 0; j<n1D; j++)
|
||||
{
|
||||
if ((*markers[1])[j])
|
||||
{
|
||||
for (int i = 0; i<n1D; i++)
|
||||
{
|
||||
if ((*markers[0])[i])
|
||||
{
|
||||
int ldof = n1D*n1D*k + n1D*j+i;
|
||||
non_ess_dofs[iel]->Append(ldof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
non_ess_dofs[iel]->Sort();
|
||||
non_ess_dofs[iel]->Unique();
|
||||
for (int i = 0; i<non_ess_dofs[iel]->Size(); i++)
|
||||
{
|
||||
int ldof = (*non_ess_dofs[iel])[i];
|
||||
int tdof = elem_dofs[dmap[ldof]];
|
||||
(*non_ess_dofs[iel])[i] = tdof;
|
||||
}
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
delete markers[d];
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
|
||||
#pragma once
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
IntegrationRule * TensorIntegrationRule(const FiniteElementSpace & fes, int order);
|
||||
IntegrationRule * TensorIntegrationRule(int dim, int order);
|
||||
|
||||
void KronMult(const Vector & x, const Vector & y, Vector & z);
|
||||
void KronMult(const Vector & x, const Vector & y, const Vector & z, Vector & w);
|
||||
|
||||
|
||||
void AlterLS(DenseMatrix & T, Vector & vecA, Vector & vecB);
|
||||
|
||||
void AlterLS(DenseTensor & T, Vector & vecA, Vector & vecB, Vector & vecC);
|
||||
|
||||
class ElementTPFunctionCoefficient : public Coefficient//
|
||||
{
|
||||
private:
|
||||
int dim;
|
||||
DenseMatrix A;
|
||||
DenseTensor T;
|
||||
double coeff_avg;
|
||||
Vector VecX;
|
||||
Vector VecY;
|
||||
Vector VecZ;
|
||||
int orient;
|
||||
int nint; // total num of integrations points
|
||||
int mint=0; // counter for all the integrations points
|
||||
int nintx = 0; // counter for the x integrations points
|
||||
int ninty = 0; // counter for the y integration points
|
||||
int nintz = 0; // counter for the z integration points
|
||||
int coord = 0; // (indication flag for x,y or z coordinate)
|
||||
|
||||
public:
|
||||
ElementTPFunctionCoefficient(FiniteElementSpace &fes, int iel, Coefficient &cf);
|
||||
double GetCoeffAvg() {return coeff_avg;}
|
||||
void ResetCounters() { mint = nintx = ninty = nintz = 0; }
|
||||
void ResetCounter(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 0: nintx = 0; break;
|
||||
case 1: ninty = 0; break;
|
||||
case 2: nintz = 0; break;
|
||||
default: mint = 0; break;
|
||||
}
|
||||
}
|
||||
void SetCoord(int coord_) { coord = coord_; }
|
||||
void SetOrient(int orient_) { orient = orient_; }
|
||||
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
|
||||
Vector * GetVecX(){return &VecX;}
|
||||
Vector * GetVecY(){return &VecY;}
|
||||
Vector * GetVecZ(){return &VecZ;}
|
||||
virtual ~ElementTPFunctionCoefficient() { }
|
||||
};
|
||||
|
||||
|
||||
class TPElementTransformation
|
||||
{
|
||||
private:
|
||||
int dim;
|
||||
FiniteElementSpace * fes = nullptr;
|
||||
Array2D<Vector *> TransA1D;
|
||||
Array2D<Vector *> TransB1D;
|
||||
Array2D<Vector *> TransC1D;
|
||||
void Setup2D();
|
||||
void Setup3D();
|
||||
public:
|
||||
TPElementTransformation(FiniteElementSpace &fes_);
|
||||
Vector * GetTPTransformation(int iel, int coord, int which_coeff)
|
||||
{
|
||||
switch(which_coeff)
|
||||
{
|
||||
case 0: return TransA1D[iel][coord]; break;
|
||||
case 1: return TransB1D[iel][coord]; break;
|
||||
case 2:
|
||||
{
|
||||
MFEM_VERIFY(dim == 3, "Wrong coeff for this dimension");
|
||||
return TransC1D[iel][coord];
|
||||
break;
|
||||
}
|
||||
default: MFEM_ABORT("Wrong coeff selection"); return 0; break;
|
||||
}
|
||||
}
|
||||
~TPElementTransformation() { }
|
||||
};
|
||||
|
||||
void GetVertexToEdgeCount(const Mesh * mesh, DenseMatrix & edge_counts);
|
||||
|
||||
void Get1DMatrices(FiniteElementSpace * fes, int iedge, int orient,
|
||||
Vector & JacL, Vector & JacM, Vector & Coeff1D,
|
||||
const IntegrationRule *ir,
|
||||
DenseMatrix &L, DenseMatrix & M);
|
||||
|
||||
void GetDiffusionEdgeMatrix(int iedge, FiniteElementSpace * fes,
|
||||
Vector & Jac1D, Vector & Coeff1D,
|
||||
const IntegrationRule *ir,
|
||||
DenseMatrix &elmat, int orient);
|
||||
void GetMassEdgeMatrix(int iedge, FiniteElementSpace * fes,
|
||||
Vector & Jac1D, Vector & Coeff1D,
|
||||
const IntegrationRule *ir,
|
||||
DenseMatrix &elmat, int orient);
|
||||
|
||||
void TensorProductEssentialDofsMaps(const Array<int> & ess_tdof_list,
|
||||
const ParFiniteElementSpace * fes,
|
||||
Array<Array<int> *> & tmap, // local edge map
|
||||
Array<Array<int>* > & non_ess_dofs); // element map
|
||||
+171
-148
@@ -31,7 +31,7 @@ void BilinearForm::AllocMat()
|
||||
const Table &elem_dof = fes->GetElementToDofTable();
|
||||
Table dof_dof;
|
||||
|
||||
if (fbfi.Size() > 0)
|
||||
if (interior_face_integs.Size() > 0)
|
||||
{
|
||||
// the sparsity pattern is defined from the map: face->element->dof
|
||||
Table face_dof, dof_face;
|
||||
@@ -99,15 +99,15 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
|
||||
ext = NULL;
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
dbfi = bf->dbfi;
|
||||
domain_integs = bf->domain_integs;
|
||||
|
||||
bbfi = bf->bbfi;
|
||||
bbfi_marker = bf->bbfi_marker;
|
||||
boundary_integs = bf->boundary_integs;
|
||||
boundary_integs_marker = bf->boundary_integs_marker;
|
||||
|
||||
fbfi = bf->fbfi;
|
||||
interior_face_integs = bf->interior_face_integs;
|
||||
|
||||
bfbfi = bf->bfbfi;
|
||||
bfbfi_marker = bf->bfbfi_marker;
|
||||
boundary_face_integs = bf->boundary_face_integs;
|
||||
boundary_face_integs_marker = bf->boundary_face_integs_marker;
|
||||
|
||||
AllocMat();
|
||||
}
|
||||
@@ -234,46 +234,47 @@ void BilinearForm::Finalize (int skip_zeros)
|
||||
|
||||
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
|
||||
{
|
||||
dbfi.Append(bfi);
|
||||
dbfi_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
domain_integs.Append(bfi);
|
||||
domain_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &elem_marker)
|
||||
{
|
||||
dbfi.Append(bfi);
|
||||
dbfi_marker.Append(&elem_marker);
|
||||
domain_integs.Append(bfi);
|
||||
domain_integs_marker.Append(&elem_marker);
|
||||
}
|
||||
|
||||
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
bbfi.Append (bfi);
|
||||
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
boundary_integs.Append (bfi);
|
||||
boundary_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker)
|
||||
{
|
||||
bbfi.Append (bfi);
|
||||
bbfi_marker.Append(&bdr_marker);
|
||||
boundary_integs.Append (bfi);
|
||||
boundary_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void BilinearForm::AddInteriorFaceIntegrator(BilinearFormIntegrator * bfi)
|
||||
{
|
||||
fbfi.Append (bfi);
|
||||
interior_face_integs.Append (bfi);
|
||||
}
|
||||
|
||||
void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi)
|
||||
{
|
||||
bfbfi.Append(bfi);
|
||||
bfbfi_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
boundary_face_integs.Append(bfi);
|
||||
// NULL marker means apply everywhere
|
||||
boundary_face_integs_marker.Append(NULL);
|
||||
}
|
||||
|
||||
void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &bdr_marker)
|
||||
{
|
||||
bfbfi.Append(bfi);
|
||||
bfbfi_marker.Append(&bdr_marker);
|
||||
boundary_face_integs.Append(bfi);
|
||||
boundary_face_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
|
||||
@@ -285,14 +286,14 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbfi.Size())
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
const FiniteElement &fe = *fes->GetFE(i);
|
||||
ElementTransformation *eltrans = fes->GetElementTransformation(i);
|
||||
dbfi[0]->AssembleElementMatrix(fe, *eltrans, elmat);
|
||||
for (int k = 1; k < dbfi.Size(); k++)
|
||||
domain_integs[0]->AssembleElementMatrix(fe, *eltrans, elmat);
|
||||
for (int k = 1; k < domain_integs.Size(); k++)
|
||||
{
|
||||
dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
@@ -306,14 +307,14 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
|
||||
|
||||
void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
|
||||
{
|
||||
if (bbfi.Size())
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
ElementTransformation *eltrans = fes->GetBdrElementTransformation(i);
|
||||
bbfi[0]->AssembleElementMatrix(be, *eltrans, elmat);
|
||||
for (int k = 1; k < bbfi.Size(); k++)
|
||||
boundary_integs[0]->AssembleElementMatrix(be, *eltrans, elmat);
|
||||
for (int k = 1; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
@@ -407,13 +408,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dbfi.Size())
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
for (int k = 0; k < dbfi.Size(); k++)
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if (dbfi_marker[k] != NULL)
|
||||
if (domain_integs_marker[k] != NULL)
|
||||
{
|
||||
MFEM_VERIFY(mesh->attributes.Size() == dbfi_marker[k]->Size(),
|
||||
MFEM_VERIFY(mesh->attributes.Size() ==
|
||||
domain_integs_marker[k]->Size(),
|
||||
"invalid element marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
}
|
||||
@@ -430,14 +432,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
else
|
||||
{
|
||||
elmat.SetSize(0);
|
||||
for (int k = 0; k < dbfi.Size(); k++)
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if ( dbfi_marker[k] == NULL ||
|
||||
(*(dbfi_marker[k]))[elem_attr-1] == 1)
|
||||
if ( domain_integs_marker[k] == NULL ||
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
|
||||
{
|
||||
const FiniteElement &fe = *fes->GetFE(i);
|
||||
eltrans = fes->GetElementTransformation(i);
|
||||
dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
if (elmat.Size() == 0)
|
||||
{
|
||||
elmat = elemmat;
|
||||
@@ -472,20 +474,20 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
if (bbfi.Size())
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
// Which boundary attributes need to be processed?
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < bbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (bbfi_marker[k] == NULL)
|
||||
if (boundary_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *bbfi_marker[k];
|
||||
Array<int> &bdr_marker = *boundary_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary integrator #"
|
||||
<< k << ", counting from zero");
|
||||
@@ -504,21 +506,21 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
fes -> GetBdrElementVDofs (i, vdofs);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
int k = 0;
|
||||
for (; k < bbfi.Size(); k++)
|
||||
for (; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (bbfi_marker[k] &&
|
||||
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_integs_marker[k] &&
|
||||
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bbfi[k]->AssembleElementMatrix(be, *eltrans, elmat);
|
||||
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elmat);
|
||||
k++;
|
||||
break;
|
||||
}
|
||||
for (; k < bbfi.Size(); k++)
|
||||
for (; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (bbfi_marker[k] &&
|
||||
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_integs_marker[k] &&
|
||||
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (!static_cond)
|
||||
@@ -536,7 +538,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
}
|
||||
|
||||
if (fbfi.Size())
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *tr;
|
||||
Array<int> vdofs2;
|
||||
@@ -550,18 +552,19 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
fes -> GetElementVDofs (tr -> Elem2No, vdofs2);
|
||||
vdofs.Append (vdofs2);
|
||||
for (int k = 0; k < fbfi.Size(); k++)
|
||||
for (int k = 0; k < interior_face_integs.Size(); k++)
|
||||
{
|
||||
fbfi[k] -> AssembleFaceMatrix (*fes -> GetFE (tr -> Elem1No),
|
||||
*fes -> GetFE (tr -> Elem2No),
|
||||
*tr, elemmat);
|
||||
interior_face_integs[k]->
|
||||
AssembleFaceMatrix(*fes->GetFE(tr->Elem1No),
|
||||
*fes->GetFE(tr->Elem2No),
|
||||
*tr, elemmat);
|
||||
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bfbfi.Size())
|
||||
if (boundary_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *tr;
|
||||
const FiniteElement *fe1, *fe2;
|
||||
@@ -570,14 +573,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < bfbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (bfbfi_marker[k] == NULL)
|
||||
if (boundary_face_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *bfbfi_marker[k];
|
||||
Array<int> &bdr_marker = *boundary_face_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary face integrator #"
|
||||
<< k << ", counting from zero");
|
||||
@@ -601,12 +604,14 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
// but we can't dereference a NULL pointer, and we don't want to
|
||||
// actually make a fake element.
|
||||
fe2 = fe1;
|
||||
for (int k = 0; k < bfbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (bfbfi_marker[k] &&
|
||||
(*bfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_face_integs_marker[k] &&
|
||||
(*boundary_face_integs_marker[k])[bdr_attr-1] == 0)
|
||||
{ continue; }
|
||||
|
||||
bfbfi[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr, elemmat);
|
||||
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
|
||||
elemmat);
|
||||
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -857,7 +862,7 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
|
||||
|
||||
void BilinearForm::ComputeElementMatrices()
|
||||
{
|
||||
if (element_matrices || dbfi.Size() == 0 || fes->GetNE() == 0)
|
||||
if (element_matrices || domain_integs.Size() == 0 || fes->GetNE() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -886,11 +891,11 @@ void BilinearForm::ComputeElementMatrices()
|
||||
#endif
|
||||
fes->GetElementTransformation(i, &eltrans);
|
||||
|
||||
dbfi[0]->AssembleElementMatrix(fe, eltrans, elmat);
|
||||
for (int k = 1; k < dbfi.Size(); k++)
|
||||
domain_integs[0]->AssembleElementMatrix(fe, eltrans, elmat);
|
||||
for (int k = 1; k < domain_integs.Size(); k++)
|
||||
{
|
||||
// note: some integrators may not be thread-safe
|
||||
dbfi[k]->AssembleElementMatrix(fe, eltrans, tmp);
|
||||
domain_integs[k]->AssembleElementMatrix(fe, eltrans, tmp);
|
||||
elmat += tmp;
|
||||
}
|
||||
elmat.ClearExternalData();
|
||||
@@ -1105,10 +1110,12 @@ BilinearForm::~BilinearForm()
|
||||
if (!extern_bfs)
|
||||
{
|
||||
int k;
|
||||
for (k=0; k < dbfi.Size(); k++) { delete dbfi[k]; }
|
||||
for (k=0; k < bbfi.Size(); k++) { delete bbfi[k]; }
|
||||
for (k=0; k < fbfi.Size(); k++) { delete fbfi[k]; }
|
||||
for (k=0; k < bfbfi.Size(); k++) { delete bfbfi[k]; }
|
||||
for (k=0; k < domain_integs.Size(); k++) { delete domain_integs[k]; }
|
||||
for (k=0; k < boundary_integs.Size(); k++) { delete boundary_integs[k]; }
|
||||
for (k=0; k < interior_face_integs.Size(); k++)
|
||||
{ delete interior_face_integs[k]; }
|
||||
for (k=0; k < boundary_face_integs.Size(); k++)
|
||||
{ delete boundary_face_integs[k]; }
|
||||
}
|
||||
|
||||
delete ext;
|
||||
@@ -1141,13 +1148,13 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
|
||||
ext = NULL;
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
dbfi = mbf->dbfi;
|
||||
bbfi = mbf->bbfi;
|
||||
tfbfi = mbf->tfbfi;
|
||||
btfbfi = mbf->btfbfi;
|
||||
domain_integs = mbf->domain_integs;
|
||||
boundary_integs = mbf->boundary_integs;
|
||||
trace_face_integs = mbf->trace_face_integs;
|
||||
boundary_trace_face_integs = mbf->boundary_trace_face_integs;
|
||||
|
||||
bbfi_marker = mbf->bbfi_marker;
|
||||
btfbfi_marker = mbf->btfbfi_marker;
|
||||
boundary_integs_marker = mbf->boundary_integs_marker;
|
||||
boundary_trace_face_integs_marker = mbf->boundary_trace_face_integs_marker;
|
||||
|
||||
assembly = AssemblyLevel::LEGACY;
|
||||
ext = NULL;
|
||||
@@ -1236,7 +1243,8 @@ MatrixInverse * MixedBilinearForm::Inverse() const
|
||||
{
|
||||
if (assembly != AssemblyLevel::LEGACY)
|
||||
{
|
||||
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this assembly level!");
|
||||
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this "
|
||||
"assembly level!");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -1267,38 +1275,39 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
|
||||
|
||||
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
dbfi.Append (bfi);
|
||||
domain_integs.Append (bfi);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
bbfi.Append (bfi);
|
||||
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
boundary_integs.Append (bfi);
|
||||
boundary_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker)
|
||||
{
|
||||
bbfi.Append (bfi);
|
||||
bbfi_marker.Append(&bdr_marker);
|
||||
boundary_integs.Append (bfi);
|
||||
boundary_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
tfbfi.Append (bfi);
|
||||
trace_face_integs.Append (bfi);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi)
|
||||
{
|
||||
btfbfi.Append(bfi);
|
||||
btfbfi_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
boundary_trace_face_integs.Append(bfi);
|
||||
// NULL marker means apply everywhere
|
||||
boundary_trace_face_integs_marker.Append(NULL);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &bdr_marker)
|
||||
{
|
||||
btfbfi.Append(bfi);
|
||||
btfbfi_marker.Append(&bdr_marker);
|
||||
boundary_trace_face_integs.Append(bfi);
|
||||
boundary_trace_face_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
@@ -1320,37 +1329,37 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
mat = new SparseMatrix(height, width);
|
||||
}
|
||||
|
||||
if (dbfi.Size())
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
for (int i = 0; i < test_fes -> GetNE(); i++)
|
||||
{
|
||||
trial_fes -> GetElementVDofs (i, tr_vdofs);
|
||||
test_fes -> GetElementVDofs (i, te_vdofs);
|
||||
eltrans = test_fes -> GetElementTransformation (i);
|
||||
for (int k = 0; k < dbfi.Size(); k++)
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
dbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
|
||||
*test_fes -> GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
|
||||
*test_fes -> GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bbfi.Size())
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
// Which boundary attributes need to be processed?
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < bbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (bbfi_marker[k] == NULL)
|
||||
if (boundary_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *bbfi_marker[k];
|
||||
Array<int> &bdr_marker = *boundary_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary integrator #"
|
||||
<< k << ", counting from zero");
|
||||
@@ -1368,20 +1377,20 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
|
||||
test_fes -> GetBdrElementVDofs (i, te_vdofs);
|
||||
eltrans = test_fes -> GetBdrElementTransformation (i);
|
||||
for (int k = 0; k < bbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (bbfi_marker[k] &&
|
||||
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_integs_marker[k] &&
|
||||
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
bbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetBE(i),
|
||||
*test_fes -> GetBE(i),
|
||||
*eltrans, elemmat);
|
||||
boundary_integs[k]->AssembleElementMatrix2 (*trial_fes -> GetBE(i),
|
||||
*test_fes -> GetBE(i),
|
||||
*eltrans, elemmat);
|
||||
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tfbfi.Size())
|
||||
if (trace_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Array<int> te_vdofs2;
|
||||
@@ -1408,16 +1417,16 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
// want to actually make a fake element.
|
||||
test_fe2 = test_fe1;
|
||||
}
|
||||
for (int k = 0; k < tfbfi.Size(); k++)
|
||||
for (int k = 0; k < trace_face_integs.Size(); k++)
|
||||
{
|
||||
tfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
|
||||
*ftr, elemmat);
|
||||
trace_face_integs[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1,
|
||||
*test_fe2, *ftr, elemmat);
|
||||
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (btfbfi.Size())
|
||||
if (boundary_trace_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Array<int> te_vdofs2;
|
||||
@@ -1427,17 +1436,17 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < btfbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_trace_face_integs.Size(); k++)
|
||||
{
|
||||
if (btfbfi_marker[k] == NULL)
|
||||
if (boundary_trace_face_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *btfbfi_marker[k];
|
||||
Array<int> &bdr_marker = *boundary_trace_face_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary trace face integrator #"
|
||||
<< k << ", counting from zero");
|
||||
"invalid boundary marker for boundary trace face"
|
||||
"integrator #" << k << ", counting from zero");
|
||||
for (int i = 0; i < bdr_attr_marker.Size(); i++)
|
||||
{
|
||||
bdr_attr_marker[i] |= bdr_marker[i];
|
||||
@@ -1460,13 +1469,16 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
// boundaries, but we can't dereference a NULL pointer, and we don't
|
||||
// want to actually make a fake element.
|
||||
test_fe2 = test_fe1;
|
||||
for (int k = 0; k < btfbfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_trace_face_integs.Size(); k++)
|
||||
{
|
||||
if (btfbfi_marker[k] &&
|
||||
(*btfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_trace_face_integs_marker[k] &&
|
||||
(*boundary_trace_face_integs_marker[k])[bdr_attr-1] == 0)
|
||||
{ continue; }
|
||||
|
||||
btfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
|
||||
*ftr, elemmat);
|
||||
boundary_trace_face_integs[k]->AssembleFaceMatrix(*trial_face_fe,
|
||||
*test_fe1,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
@@ -1557,15 +1569,17 @@ void MixedBilinearForm::ConformingAssemble()
|
||||
|
||||
void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
|
||||
{
|
||||
if (dbfi.Size())
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
|
||||
const FiniteElement &test_fe = *test_fes->GetFE(i);
|
||||
ElementTransformation *eltrans = test_fes->GetElementTransformation(i);
|
||||
dbfi[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elmat);
|
||||
for (int k = 1; k < dbfi.Size(); k++)
|
||||
domain_integs[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans,
|
||||
elmat);
|
||||
for (int k = 1; k < domain_integs.Size(); k++)
|
||||
{
|
||||
dbfi[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elemmat);
|
||||
domain_integs[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans,
|
||||
elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
@@ -1580,15 +1594,17 @@ void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
|
||||
|
||||
void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
|
||||
{
|
||||
if (bbfi.Size())
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
const FiniteElement &trial_be = *trial_fes->GetBE(i);
|
||||
const FiniteElement &test_be = *test_fes->GetBE(i);
|
||||
ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i);
|
||||
bbfi[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elmat);
|
||||
for (int k = 1; k < bbfi.Size(); k++)
|
||||
boundary_integs[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans,
|
||||
elmat);
|
||||
for (int k = 1; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
bbfi[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elemmat);
|
||||
boundary_integs[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans,
|
||||
elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
@@ -1688,10 +1704,10 @@ void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
|
||||
}
|
||||
}
|
||||
|
||||
void MixedBilinearForm::FormRectangularSystemMatrix(const Array<int>
|
||||
&trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
OperatorHandle &A)
|
||||
void MixedBilinearForm::FormRectangularSystemMatrix(
|
||||
const Array<int> &trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
OperatorHandle &A)
|
||||
|
||||
{
|
||||
if (ext)
|
||||
@@ -1729,17 +1745,17 @@ void MixedBilinearForm::FormRectangularSystemMatrix(const Array<int>
|
||||
A.Reset(mat, false);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::FormRectangularLinearSystem(const Array<int>
|
||||
&trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A,
|
||||
Vector &X, Vector &B)
|
||||
void MixedBilinearForm::FormRectangularLinearSystem(
|
||||
const Array<int> &trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A,
|
||||
Vector &X, Vector &B)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, A, X,
|
||||
B);
|
||||
ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list,
|
||||
x, b, A, X, B);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1777,10 +1793,13 @@ MixedBilinearForm::~MixedBilinearForm()
|
||||
if (!extern_bfs)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dbfi.Size(); i++) { delete dbfi[i]; }
|
||||
for (i = 0; i < bbfi.Size(); i++) { delete bbfi[i]; }
|
||||
for (i = 0; i < tfbfi.Size(); i++) { delete tfbfi[i]; }
|
||||
for (i = 0; i < btfbfi.Size(); i++) { delete btfbfi[i]; }
|
||||
for (i = 0; i < domain_integs.Size(); i++) { delete domain_integs[i]; }
|
||||
for (i = 0; i < boundary_integs.Size(); i++)
|
||||
{ delete boundary_integs[i]; }
|
||||
for (i = 0; i < trace_face_integs.Size(); i++)
|
||||
{ delete trace_face_integs[i]; }
|
||||
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
|
||||
{ delete boundary_trace_face_integs[i]; }
|
||||
}
|
||||
delete ext;
|
||||
}
|
||||
@@ -1830,7 +1849,7 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
mat = new SparseMatrix(height, width);
|
||||
}
|
||||
|
||||
if (dbfi.Size() > 0)
|
||||
if (domain_integs.Size() > 0)
|
||||
{
|
||||
for (int i = 0; i < test_fes->GetNE(); i++)
|
||||
{
|
||||
@@ -1840,17 +1859,19 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
dom_fe = trial_fes->GetFE(i);
|
||||
ran_fe = test_fes->GetFE(i);
|
||||
|
||||
dbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
|
||||
for (int j = 1; j < dbfi.Size(); j++)
|
||||
domain_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
totelmat);
|
||||
for (int j = 1; j < domain_integs.Size(); j++)
|
||||
{
|
||||
dbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
|
||||
domain_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
elmat);
|
||||
totelmat += elmat;
|
||||
}
|
||||
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
if (tfbfi.Size())
|
||||
if (trace_face_integs.Size())
|
||||
{
|
||||
const int nfaces = test_fes->GetMesh()->GetNumFaces();
|
||||
for (int i = 0; i < nfaces; i++)
|
||||
@@ -1861,10 +1882,12 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
dom_fe = trial_fes->GetFaceElement(i);
|
||||
ran_fe = test_fes->GetFaceElement(i);
|
||||
|
||||
tfbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
|
||||
for (int j = 1; j < tfbfi.Size(); j++)
|
||||
trace_face_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
totelmat);
|
||||
for (int j = 1; j < trace_face_integs.Size(); j++)
|
||||
{
|
||||
tfbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
|
||||
trace_face_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
elmat);
|
||||
totelmat += elmat;
|
||||
}
|
||||
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
|
||||
|
||||
+36
-30
@@ -84,28 +84,29 @@ protected:
|
||||
the BilinearForm. */
|
||||
long sequence;
|
||||
|
||||
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
|
||||
#fbfi, and #bfbfi are owned by another BilinearForm. */
|
||||
/** @brief Indicates the BilinearFormIntegrator%s stored in #domain_integs,
|
||||
#boundary_integs, #interior_face_integs, and #boundary_face_integs are
|
||||
owned by another BilinearForm. */
|
||||
int extern_bfs;
|
||||
|
||||
/// Set of Domain Integrators to be applied.
|
||||
Array<BilinearFormIntegrator*> dbfi;
|
||||
Array<BilinearFormIntegrator*> domain_integs;
|
||||
/// Element attribute marker (should be of length mesh->attributes)
|
||||
/// Includes all by default.
|
||||
/// 0 - ignore attribute
|
||||
/// 1 - include attribute
|
||||
Array<Array<int>*> dbfi_marker;
|
||||
Array<Array<int>*> domain_integs_marker;
|
||||
|
||||
/// Set of Boundary Integrators to be applied.
|
||||
Array<BilinearFormIntegrator*> bbfi;
|
||||
Array<Array<int>*> bbfi_marker; ///< Entries are not owned.
|
||||
Array<BilinearFormIntegrator*> boundary_integs;
|
||||
Array<Array<int>*> boundary_integs_marker; ///< Entries are not owned.
|
||||
|
||||
/// Set of interior face Integrators to be applied.
|
||||
Array<BilinearFormIntegrator*> fbfi;
|
||||
Array<BilinearFormIntegrator*> interior_face_integs;
|
||||
|
||||
/// Set of boundary face Integrators to be applied.
|
||||
Array<BilinearFormIntegrator*> bfbfi;
|
||||
Array<Array<int>*> bfbfi_marker; ///< Entries are not owned.
|
||||
Array<BilinearFormIntegrator*> boundary_face_integs;
|
||||
Array<Array<int>*> boundary_face_integs_marker; ///< Entries are not owned.
|
||||
|
||||
DenseMatrix elemmat;
|
||||
Array<int> vdofs;
|
||||
@@ -231,24 +232,25 @@ public:
|
||||
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
|
||||
|
||||
/// Access all the integrators added with AddDomainIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetDBFI() { return &dbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetDBFI() { return &domain_integs; }
|
||||
|
||||
/// Access all the integrators added with AddBoundaryIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBBFI() { return &bbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetBBFI() { return &boundary_integs; }
|
||||
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBBFI_Marker() { return &bbfi_marker; }
|
||||
Array<Array<int>*> *GetBBFI_Marker() { return &boundary_integs_marker; }
|
||||
|
||||
/// Access all integrators added with AddInteriorFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetFBFI() { return &fbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetFBFI() { return &interior_face_integs; }
|
||||
|
||||
/// Access all integrators added with AddBdrFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBFBFI() { return &bfbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetBFBFI() { return &boundary_face_integs; }
|
||||
/** @brief Access all boundary markers added with AddBdrFaceIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBFBFI_Marker() { return &bfbfi_marker; }
|
||||
Array<Array<int>*> *GetBFBFI_Marker()
|
||||
{ return &boundary_face_integs_marker; }
|
||||
|
||||
/// Returns a reference to: \f$ M_{ij} \f$
|
||||
const double &operator()(int i, int j) { return (*mat)(i,j); }
|
||||
@@ -652,23 +654,25 @@ protected:
|
||||
Partial Assembly (PA), or Matrix Free assembly (MF). */
|
||||
MixedBilinearFormExtension *ext;
|
||||
|
||||
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
|
||||
#tfbfi and #btfbfi are owned by another MixedBilinearForm. */
|
||||
/** @brief Indicates the BilinearFormIntegrator%s stored in #domain_integs,
|
||||
#boundary_integs, #trace_face_integs and #boundary_trace_face_integs
|
||||
are owned by another MixedBilinearForm. */
|
||||
int extern_bfs;
|
||||
|
||||
/// Domain integrators.
|
||||
Array<BilinearFormIntegrator*> dbfi;
|
||||
Array<BilinearFormIntegrator*> domain_integs;
|
||||
|
||||
/// Boundary integrators.
|
||||
Array<BilinearFormIntegrator*> bbfi;
|
||||
Array<Array<int>*> bbfi_marker;///< Entries are not owned.
|
||||
Array<BilinearFormIntegrator*> boundary_integs;
|
||||
Array<Array<int>*> boundary_integs_marker; ///< Entries are not owned.
|
||||
|
||||
/// Trace face (skeleton) integrators.
|
||||
Array<BilinearFormIntegrator*> tfbfi;
|
||||
Array<BilinearFormIntegrator*> trace_face_integs;
|
||||
|
||||
/// Boundary trace face (skeleton) integrators.
|
||||
Array<BilinearFormIntegrator*> btfbfi;
|
||||
Array<Array<int>*> btfbfi_marker;///< Entries are not owned.
|
||||
Array<BilinearFormIntegrator*> boundary_trace_face_integs;
|
||||
/// Entries are not owned.
|
||||
Array<Array<int>*> boundary_trace_face_integs_marker;
|
||||
|
||||
DenseMatrix elemmat;
|
||||
Array<int> trial_vdofs, test_vdofs;
|
||||
@@ -762,24 +766,26 @@ public:
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
/// Access all integrators added with AddDomainIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetDBFI() { return &dbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetDBFI() { return &domain_integs; }
|
||||
|
||||
/// Access all integrators added with AddBoundaryIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBBFI() { return &bbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetBBFI() { return &boundary_integs; }
|
||||
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBBFI_Marker() { return &bbfi_marker; }
|
||||
Array<Array<int>*> *GetBBFI_Marker() { return &boundary_integs_marker; }
|
||||
|
||||
/// Access all integrators added with AddTraceFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetTFBFI() { return &tfbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetTFBFI() { return &trace_face_integs; }
|
||||
|
||||
/// Access all integrators added with AddBdrTraceFaceIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBTFBFI() { return &btfbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetBTFBFI()
|
||||
{ return &boundary_trace_face_integs; }
|
||||
/** @brief Access all boundary markers added with AddBdrTraceFaceIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetBTFBFI_Marker() { return &btfbfi_marker; }
|
||||
Array<Array<int>*> *GetBTFBFI_Marker()
|
||||
{ return &boundary_trace_face_integs_marker; }
|
||||
|
||||
/// Sets all sparse values of \f$ M \f$ to @a a.
|
||||
void operator=(const double a) { *mat = a; }
|
||||
@@ -1004,7 +1010,7 @@ public:
|
||||
{ AddTraceFaceIntegrator(di); }
|
||||
|
||||
/// Access all interpolators added with AddDomainInterpolator().
|
||||
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
|
||||
Array<BilinearFormIntegrator*> *GetDI() { return &domain_integs; }
|
||||
|
||||
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
|
||||
/** This method must be called before assembly. */
|
||||
|
||||
+12
-12
@@ -160,7 +160,7 @@ void MFBilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
intFaceIntegrators[i]->AddMultMF(faceIntX, faceIntY);
|
||||
}
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ void MFBilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
bdrFaceIntegrators[i]->AddMultMF(faceBdrX, faceBdrY);
|
||||
}
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ void MFBilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
intFaceIntegrators[i]->AddMultTransposeMF(faceIntX, faceIntY);
|
||||
}
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ void MFBilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
bdrFaceIntegrators[i]->AddMultTransposeMF(faceBdrX, faceBdrY);
|
||||
}
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,7 +417,7 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
intFaceIntegrators[i]->AddMultPA(faceIntX, faceIntY);
|
||||
}
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
bdrFaceIntegrators[i]->AddMultPA(faceBdrX, faceBdrY);
|
||||
}
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,7 +474,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
intFaceIntegrators[i]->AddMultTransposePA(faceIntX, faceIntY);
|
||||
}
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
bdrFaceIntegrators[i]->AddMultTransposePA(faceBdrX, faceBdrY);
|
||||
}
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -657,7 +657,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
// Apply the Interior Face Restriction transposed
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,7 +688,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -783,7 +783,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
Y(j, 0, f) += res;
|
||||
});
|
||||
// Apply the Interior Face Restriction transposed
|
||||
int_face_restrict_lex->MultTranspose(faceIntY, y);
|
||||
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,7 +814,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
Y(j, f) += res;
|
||||
});
|
||||
// Apply the Boundary Face Restriction transposed
|
||||
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
|
||||
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ protected:
|
||||
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
|
||||
const FaceRestriction *int_face_restrict_lex; // Not owned
|
||||
const FaceRestriction *bdr_face_restrict_lex; // Not owned
|
||||
|
||||
public:
|
||||
PABilinearFormExtension(BilinearForm*);
|
||||
@@ -143,8 +143,8 @@ protected:
|
||||
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
|
||||
const FaceRestriction *int_face_restrict_lex; // Not owned
|
||||
const FaceRestriction *bdr_face_restrict_lex; // Not owned
|
||||
|
||||
public:
|
||||
MFBilinearFormExtension(BilinearForm *form);
|
||||
|
||||
+29
-2
@@ -175,6 +175,11 @@ void BilinearFormIntegrator::AssembleFaceVector(
|
||||
elmat.Mult(elfun, elvect);
|
||||
}
|
||||
|
||||
void TransposeIntegrator::SetIntRule(const IntegrationRule *ir)
|
||||
{
|
||||
IntRule = ir;
|
||||
bfi->SetIntRule(ir);
|
||||
}
|
||||
|
||||
void TransposeIntegrator::AssembleElementMatrix (
|
||||
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
@@ -202,6 +207,12 @@ void TransposeIntegrator::AssembleFaceMatrix (
|
||||
elmat.Transpose (bfi_elmat);
|
||||
}
|
||||
|
||||
void LumpedIntegrator::SetIntRule(const IntegrationRule *ir)
|
||||
{
|
||||
IntRule = ir;
|
||||
bfi->SetIntRule(ir);
|
||||
}
|
||||
|
||||
void LumpedIntegrator::AssembleElementMatrix (
|
||||
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
@@ -209,6 +220,12 @@ void LumpedIntegrator::AssembleElementMatrix (
|
||||
elmat.Lump();
|
||||
}
|
||||
|
||||
void InverseIntegrator::SetIntRule(const IntegrationRule *ir)
|
||||
{
|
||||
IntRule = ir;
|
||||
integrator->SetIntRule(ir);
|
||||
}
|
||||
|
||||
void InverseIntegrator::AssembleElementMatrix(
|
||||
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
@@ -216,6 +233,15 @@ void InverseIntegrator::AssembleElementMatrix(
|
||||
elmat.Invert();
|
||||
}
|
||||
|
||||
void SumIntegrator::SetIntRule(const IntegrationRule *ir)
|
||||
{
|
||||
IntRule = ir;
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->SetIntRule(ir);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleElementMatrix(
|
||||
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
@@ -1751,15 +1777,16 @@ void DerivativeIntegrator::AssembleElementMatrix2 (
|
||||
int dim = trial_fe.GetDim();
|
||||
int trial_nd = trial_fe.GetDof();
|
||||
int test_nd = test_fe.GetDof();
|
||||
int spaceDim = Trans.GetSpaceDim();
|
||||
|
||||
int i, l;
|
||||
double det;
|
||||
|
||||
elmat.SetSize (test_nd,trial_nd);
|
||||
dshape.SetSize (trial_nd,dim);
|
||||
dshapedxt.SetSize(trial_nd,dim);
|
||||
dshapedxt.SetSize(trial_nd, spaceDim);
|
||||
dshapedxi.SetSize(trial_nd);
|
||||
invdfdx.SetSize(dim);
|
||||
invdfdx.SetSize(dim, spaceDim);
|
||||
shape.SetSize (test_nd);
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
|
||||
@@ -261,6 +261,8 @@ public:
|
||||
TransposeIntegrator (BilinearFormIntegrator *bfi_, int own_bfi_ = 1)
|
||||
{ bfi = bfi_; own_bfi = own_bfi_; }
|
||||
|
||||
virtual void SetIntRule(const IntegrationRule *ir);
|
||||
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
@@ -328,6 +330,8 @@ public:
|
||||
LumpedIntegrator (BilinearFormIntegrator *bfi_, int own_bfi_ = 1)
|
||||
{ bfi = bfi_; own_bfi = own_bfi_; }
|
||||
|
||||
virtual void SetIntRule(const IntegrationRule *ir);
|
||||
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
@@ -346,6 +350,8 @@ public:
|
||||
InverseIntegrator(BilinearFormIntegrator *integ, int own_integ = 1)
|
||||
{ integrator = integ; own_integrator = own_integ; }
|
||||
|
||||
virtual void SetIntRule(const IntegrationRule *ir);
|
||||
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
@@ -364,6 +370,8 @@ private:
|
||||
public:
|
||||
SumIntegrator(int own_integs = 1) { own_integrators = own_integs; }
|
||||
|
||||
virtual void SetIntRule(const IntegrationRule *ir);
|
||||
|
||||
void AddIntegrator(BilinearFormIntegrator *integ)
|
||||
{ integrators.Append(integ); }
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ Solver *BuildSmootherFromCeed(ConstrainedOperator &op, bool chebyshev)
|
||||
if (chebyshev)
|
||||
{
|
||||
const int cheb_order = 3;
|
||||
out = new OperatorChebyshevSmoother(&op, t_diag, ess_tdofs, cheb_order);
|
||||
out = new OperatorChebyshevSmoother(op, t_diag, ess_tdofs, cheb_order);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+58
-8
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <cerrno> // errno
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/stat.h> // mkdir
|
||||
@@ -764,7 +765,8 @@ ParaViewDataCollection::ParaViewDataCollection(const std::string&
|
||||
: DataCollection(collection_name, mesh_),
|
||||
levels_of_detail(1),
|
||||
pv_data_format(VTKFormat::BINARY),
|
||||
high_order_output(false)
|
||||
high_order_output(false),
|
||||
restart_mode(false)
|
||||
{
|
||||
#ifdef MFEM_USE_ZLIB
|
||||
compression = -1; // default zlib compression level, equivalent to 6
|
||||
@@ -842,17 +844,60 @@ void ParaViewDataCollection::Save()
|
||||
}
|
||||
// the directory is created
|
||||
|
||||
// create pvd file if needed
|
||||
// create pvd file if needed. If we are not in restart mode, a new pvd file
|
||||
// is always created. In restart mode, we keep any previously defined
|
||||
// timestep values as long as they are less than the currently defined time.
|
||||
|
||||
if (myid == 0 && !pvd_stream.is_open())
|
||||
{
|
||||
std::string dpath=GenerateCollectionPath();
|
||||
std::string pvdname=dpath+"/"+GeneratePVDFileName();
|
||||
pvd_stream.open(pvdname.c_str(),std::ios::out);
|
||||
// initialize the file
|
||||
pvd_stream << "<?xml version=\"1.0\"?>\n";
|
||||
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
|
||||
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
pvd_stream << "<Collection>" << std::endl;
|
||||
|
||||
std::ifstream pvd_in;
|
||||
if (restart_mode && (pvd_in.open(pvdname,std::ios::binary),pvd_in.good()))
|
||||
{
|
||||
// PVD file exists and restart mode enabled: preserve existing time
|
||||
// steps less than the current time.
|
||||
std::fstream::pos_type pos_begin = pvd_in.tellg();
|
||||
std::fstream::pos_type pos_end = pos_begin;
|
||||
|
||||
std::regex regexp("timestep=\"([^[:space:]]+)\".*file=\"Cycle(\\d+)");
|
||||
std::smatch match;
|
||||
|
||||
std::string line;
|
||||
while (getline(pvd_in,line))
|
||||
{
|
||||
if (regex_search(line,match,regexp))
|
||||
{
|
||||
MFEM_ASSERT(match.size() == 3, "Unable to parse DataSet");
|
||||
double tvalue = std::stod(match[1]);
|
||||
if (tvalue >= GetTime()) { break; }
|
||||
int cvalue = std::stoi(match[2]);
|
||||
MFEM_VERIFY(cvalue < GetCycle(), "Cycle " << GetCycle() <<
|
||||
" is too small for restart mode: trying to overwrite"
|
||||
" existing data.");
|
||||
pos_end = pvd_in.tellg();
|
||||
}
|
||||
}
|
||||
size_t count = pos_end - pos_begin;
|
||||
std::vector<char> buf(count);
|
||||
pvd_in.clear();
|
||||
pvd_in.seekg(pos_begin);
|
||||
pvd_in.read(buf.data(), count);
|
||||
pvd_in.close();
|
||||
pvd_stream.open(pvdname.c_str(),std::ios::out);
|
||||
pvd_stream.write(buf.data(), count);
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize new pvd file
|
||||
pvd_stream.open(pvdname.c_str(),std::ios::out);
|
||||
// initialize the file
|
||||
pvd_stream << "<?xml version=\"1.0\"?>\n";
|
||||
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
|
||||
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
|
||||
pvd_stream << "<Collection>" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// define the vtu file
|
||||
@@ -1091,6 +1136,11 @@ void ParaViewDataCollection::SetCompression(bool compression_)
|
||||
}
|
||||
}
|
||||
|
||||
void ParaViewDataCollection::UseRestartMode(bool restart_mode_)
|
||||
{
|
||||
restart_mode = restart_mode_;
|
||||
}
|
||||
|
||||
const char *ParaViewDataCollection::GetDataFormatString() const
|
||||
{
|
||||
if (pv_data_format == VTKFormat::ASCII)
|
||||
|
||||
@@ -488,6 +488,7 @@ private:
|
||||
std::fstream pvd_stream;
|
||||
VTKFormat pv_data_format;
|
||||
bool high_order_output;
|
||||
bool restart_mode;
|
||||
|
||||
protected:
|
||||
void SaveDataVTU(std::ostream &out, int ref);
|
||||
@@ -545,6 +546,11 @@ public:
|
||||
/// by default). Reading high-order data requires ParaView 5.5 or later.
|
||||
void SetHighOrderOutput(bool high_order_output_);
|
||||
|
||||
/// Enable or disable restart mode. If restart is enabled, new writes will
|
||||
/// preserve timestep metadata for any solutions prior to the currently
|
||||
/// defined time.
|
||||
void UseRestartMode(bool restart_mode_);
|
||||
|
||||
/// Load the collection - not implemented in the ParaView writer
|
||||
virtual void Load(int cycle_ = 0) override;
|
||||
};
|
||||
|
||||
+23
-4
@@ -7948,7 +7948,27 @@ VectorTensorFiniteElement::VectorTensorFiniteElement(const int dims,
|
||||
p, M, FunctionSpace::Qk),
|
||||
TensorBasisElement(dims, p, VerifyNodal(cbtype), dmtype),
|
||||
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cbtype))),
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(obtype))) { }
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(obtype)))
|
||||
{
|
||||
MFEM_VERIFY(dims > 1, "Constructor for VectorTensorFiniteElement with both "
|
||||
"open and closed bases is not valid for 1D elements.");
|
||||
}
|
||||
|
||||
VectorTensorFiniteElement::VectorTensorFiniteElement(const int dims,
|
||||
const int d,
|
||||
const int p,
|
||||
const int obtype,
|
||||
const int M,
|
||||
const DofMapType dmtype)
|
||||
: VectorFiniteElement(dims, GetTensorProductGeometry(dims), d,
|
||||
p, M, FunctionSpace::Pk),
|
||||
TensorBasisElement(dims, p, obtype, dmtype),
|
||||
cbasis1d(poly1d.GetBasis(p, VerifyOpen(obtype))),
|
||||
obasis1d(poly1d.GetBasis(p, VerifyOpen(obtype)))
|
||||
{
|
||||
MFEM_VERIFY(dims == 1, "Constructor for VectorTensorFiniteElement without "
|
||||
"closed basis is only valid for 1D elements.");
|
||||
}
|
||||
|
||||
H1_SegmentElement::H1_SegmentElement(const int p, const int btype)
|
||||
: NodalTensorFiniteElement(1, p, VerifyClosed(btype), H1_DOF_MAP)
|
||||
@@ -13055,9 +13075,8 @@ void ND_TriangleElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
const double ND_SegmentElement::tk[1] = { 1. };
|
||||
|
||||
ND_SegmentElement::ND_SegmentElement(const int p, const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, p, p - 1,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type))),
|
||||
: VectorTensorFiniteElement(1, p, p - 1, ob_type, H_CURL,
|
||||
DofMapType::L2_DOF_MAP),
|
||||
dof2tk(dof)
|
||||
{
|
||||
if (obasis1d.IsIntegratedType()) { is_nodal = false; }
|
||||
|
||||
+6
-3
@@ -2239,6 +2239,11 @@ public:
|
||||
const int cbtype, const int obtype,
|
||||
const int M, const DofMapType dmtype);
|
||||
|
||||
// For 1D elements: there is only an "open basis", no "closed basis"
|
||||
VectorTensorFiniteElement(const int dims, const int d, const int p,
|
||||
const int obtype, const int M,
|
||||
const DofMapType dmtype);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const;
|
||||
|
||||
@@ -3311,11 +3316,9 @@ public:
|
||||
|
||||
|
||||
/// Arbitrary order Nedelec elements in 1D on a segment
|
||||
class ND_SegmentElement : public VectorFiniteElement
|
||||
class ND_SegmentElement : public VectorTensorFiniteElement
|
||||
{
|
||||
static const double tk[1];
|
||||
|
||||
Poly_1D::Basis &obasis1d;
|
||||
Array<int> dof2tk;
|
||||
|
||||
public:
|
||||
|
||||
+2
-2
@@ -1225,7 +1225,7 @@ const Operator *FiniteElementSpace::GetElementRestriction(
|
||||
return L2E_nat.Ptr();
|
||||
}
|
||||
|
||||
const Operator *FiniteElementSpace::GetFaceRestriction(
|
||||
const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
|
||||
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
|
||||
{
|
||||
const bool is_dg_space = IsDGSpace();
|
||||
@@ -1239,7 +1239,7 @@ const Operator *FiniteElementSpace::GetFaceRestriction(
|
||||
}
|
||||
else
|
||||
{
|
||||
Operator* res;
|
||||
FaceRestriction *res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
res = new L2FaceRestriction(*this, e_ordering, type, m);
|
||||
|
||||
+2
-2
@@ -164,7 +164,7 @@ protected:
|
||||
+ 8 * (int)std::get<3>(k);
|
||||
}
|
||||
};
|
||||
using map_L2F = std::unordered_map<const key_face,Operator*,key_hash>;
|
||||
using map_L2F = std::unordered_map<const key_face,FaceRestriction*,key_hash>;
|
||||
mutable map_L2F L2F;
|
||||
|
||||
mutable Array<QuadratureInterpolator*> E2Q_array;
|
||||
@@ -488,7 +488,7 @@ public:
|
||||
const Operator *GetElementRestriction(ElementDofOrdering e_ordering) const;
|
||||
|
||||
/// Return an Operator that converts L-vectors to E-vectors on each face.
|
||||
virtual const Operator *GetFaceRestriction(
|
||||
virtual const FaceRestriction *GetFaceRestriction(
|
||||
ElementDofOrdering e_ordering, FaceType,
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const;
|
||||
|
||||
|
||||
+9
-4
@@ -34,6 +34,7 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry)
|
||||
nx = irx.GetNPoints();
|
||||
ny = iry.GetNPoints();
|
||||
SetSize(nx * ny);
|
||||
SetPointIndices();
|
||||
|
||||
for (j = 0; j < ny; j++)
|
||||
{
|
||||
@@ -48,8 +49,6 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry)
|
||||
ip.weight = ipx.weight * ipy.weight;
|
||||
}
|
||||
}
|
||||
|
||||
SetPointIndices();
|
||||
}
|
||||
|
||||
IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
|
||||
@@ -59,6 +58,7 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
|
||||
const int ny = iry.GetNPoints();
|
||||
const int nz = irz.GetNPoints();
|
||||
SetSize(nx*ny*nz);
|
||||
SetPointIndices();
|
||||
|
||||
for (int iz = 0; iz < nz; ++iz)
|
||||
{
|
||||
@@ -78,8 +78,6 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetPointIndices();
|
||||
}
|
||||
|
||||
const Array<double> &IntegrationRule::GetWeights() const
|
||||
@@ -125,6 +123,7 @@ void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
|
||||
}
|
||||
np /= f;
|
||||
SetSize(np);
|
||||
SetPointIndices();
|
||||
|
||||
int pt = 0;
|
||||
for (int i = 0; i <= s; i++)
|
||||
@@ -375,6 +374,7 @@ public:
|
||||
void QuadratureFunctions1D::GaussLegendre(const int np, IntegrationRule* ir)
|
||||
{
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
|
||||
switch (np)
|
||||
{
|
||||
@@ -477,6 +477,7 @@ void QuadratureFunctions1D::GaussLobatto(const int np, IntegrationRule* ir)
|
||||
*/
|
||||
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
if ( np == 1 )
|
||||
{
|
||||
ir->IntPoint(0).Set1w(0.5, 1.0);
|
||||
@@ -576,6 +577,7 @@ void QuadratureFunctions1D::GaussLobatto(const int np, IntegrationRule* ir)
|
||||
void QuadratureFunctions1D::OpenUniform(const int np, IntegrationRule* ir)
|
||||
{
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
|
||||
// The Newton-Cotes quadrature is based on weights that integrate exactly the
|
||||
// interpolatory polynomial through the equally spaced quadrature points.
|
||||
@@ -591,6 +593,7 @@ void QuadratureFunctions1D::ClosedUniform(const int np,
|
||||
IntegrationRule* ir)
|
||||
{
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
if ( np == 1 ) // allow this case as "closed"
|
||||
{
|
||||
ir->IntPoint(0).Set1w(0.5, 1.0);
|
||||
@@ -608,6 +611,7 @@ void QuadratureFunctions1D::ClosedUniform(const int np,
|
||||
void QuadratureFunctions1D::OpenHalfUniform(const int np, IntegrationRule* ir)
|
||||
{
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
|
||||
// Open half points: the centers of np uniform intervals
|
||||
for (int i = 0; i < np ; ++i)
|
||||
@@ -621,6 +625,7 @@ void QuadratureFunctions1D::OpenHalfUniform(const int np, IntegrationRule* ir)
|
||||
void QuadratureFunctions1D::ClosedGL(const int np, IntegrationRule* ir)
|
||||
{
|
||||
ir->SetSize(np);
|
||||
ir->SetPointIndices();
|
||||
ir->IntPoint(0).x = 0.0;
|
||||
ir->IntPoint(np-1).x = 1.0;
|
||||
|
||||
|
||||
+5
-3
@@ -96,9 +96,6 @@ private:
|
||||
by request with the method GetWeights(). */
|
||||
mutable Array<double> weights;
|
||||
|
||||
/// Sets the indices of each quadrature point on initialization.
|
||||
void SetPointIndices();
|
||||
|
||||
/// Define n-simplex rule (triangle/tetrahedron for n=2/3) of order (2s+1)
|
||||
void GrundmannMollerSimplexRule(int s, int n = 3);
|
||||
|
||||
@@ -227,6 +224,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the indices of each quadrature point on initialization.
|
||||
/** Note that most calls to IntegrationRule::SetSize should be paired with a
|
||||
call to SetPointIndices in order for the indices to be set correctly. */
|
||||
void SetPointIndices();
|
||||
|
||||
/// Tensor product of two 1D integration rules
|
||||
IntegrationRule(IntegrationRule &irx, IntegrationRule &iry);
|
||||
|
||||
|
||||
+74
-64
@@ -26,14 +26,14 @@ LinearForm::LinearForm(FiniteElementSpace *f, LinearForm *lf)
|
||||
extern_lfs = 1;
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
dlfi = lf->dlfi;
|
||||
domain_integs = lf->domain_integs;
|
||||
|
||||
dlfi_delta = lf->dlfi_delta;
|
||||
domain_delta_integs = lf->domain_delta_integs;
|
||||
|
||||
blfi = lf->blfi;
|
||||
boundary_integs = lf->boundary_integs;
|
||||
|
||||
flfi = lf->flfi;
|
||||
flfi_marker = lf->flfi_marker;
|
||||
boundary_face_integs = lf->boundary_face_integs;
|
||||
boundary_face_integs_marker = lf->boundary_face_integs_marker;
|
||||
}
|
||||
|
||||
void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi)
|
||||
@@ -42,13 +42,13 @@ void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi)
|
||||
dynamic_cast<DeltaLFIntegrator *>(lfi);
|
||||
if (!maybe_delta || !maybe_delta->IsDelta())
|
||||
{
|
||||
dlfi.Append(lfi);
|
||||
domain_integs.Append(lfi);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlfi_delta.Append(maybe_delta);
|
||||
domain_delta_integs.Append(maybe_delta);
|
||||
}
|
||||
dlfi_marker.Append(NULL);
|
||||
domain_integs_marker.Append(NULL);
|
||||
}
|
||||
|
||||
void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi,
|
||||
@@ -58,44 +58,45 @@ void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi,
|
||||
dynamic_cast<DeltaLFIntegrator *>(lfi);
|
||||
if (!maybe_delta || !maybe_delta->IsDelta())
|
||||
{
|
||||
dlfi.Append(lfi);
|
||||
domain_integs.Append(lfi);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlfi_delta.Append(maybe_delta);
|
||||
domain_delta_integs.Append(maybe_delta);
|
||||
}
|
||||
dlfi_marker.Append(&elem_marker);
|
||||
domain_integs_marker.Append(&elem_marker);
|
||||
}
|
||||
|
||||
void LinearForm::AddBoundaryIntegrator (LinearFormIntegrator * lfi)
|
||||
{
|
||||
blfi.Append (lfi);
|
||||
blfi_marker.Append(NULL); // NULL -> all attributes are active
|
||||
boundary_integs.Append (lfi);
|
||||
boundary_integs_marker.Append(NULL); // NULL -> all attributes are active
|
||||
}
|
||||
|
||||
void LinearForm::AddBoundaryIntegrator (LinearFormIntegrator * lfi,
|
||||
Array<int> &bdr_attr_marker)
|
||||
{
|
||||
blfi.Append (lfi);
|
||||
blfi_marker.Append(&bdr_attr_marker);
|
||||
boundary_integs.Append (lfi);
|
||||
boundary_integs_marker.Append(&bdr_attr_marker);
|
||||
}
|
||||
|
||||
void LinearForm::AddBdrFaceIntegrator (LinearFormIntegrator * lfi)
|
||||
{
|
||||
flfi.Append(lfi);
|
||||
flfi_marker.Append(NULL); // NULL -> all attributes are active
|
||||
boundary_face_integs.Append(lfi);
|
||||
// NULL -> all attributes are active
|
||||
boundary_face_integs_marker.Append(NULL);
|
||||
}
|
||||
|
||||
void LinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi,
|
||||
Array<int> &bdr_attr_marker)
|
||||
{
|
||||
flfi.Append(lfi);
|
||||
flfi_marker.Append(&bdr_attr_marker);
|
||||
boundary_face_integs.Append(lfi);
|
||||
boundary_face_integs_marker.Append(&bdr_attr_marker);
|
||||
}
|
||||
|
||||
void LinearForm::AddInteriorFaceIntegrator(LinearFormIntegrator *lfi)
|
||||
{
|
||||
iflfi.Append(lfi);
|
||||
interior_face_integs.Append(lfi);
|
||||
}
|
||||
|
||||
void LinearForm::Assemble()
|
||||
@@ -112,14 +113,14 @@ void LinearForm::Assemble()
|
||||
// The first use of AddElementVector() below will move it back to host
|
||||
// because both 'vdofs' and 'elemvect' are on host.
|
||||
|
||||
if (dlfi.Size())
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
for (int k = 0; k < dlfi.Size(); k++)
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if (dlfi_marker[k] != NULL)
|
||||
if (domain_integs_marker[k] != NULL)
|
||||
{
|
||||
MFEM_VERIFY(fes->GetMesh()->attributes.Size() ==
|
||||
dlfi_marker[k]->Size(),
|
||||
domain_integs_marker[k]->Size(),
|
||||
"invalid element marker for domain linear form "
|
||||
"integrator #" << k << ", counting from zero");
|
||||
}
|
||||
@@ -128,14 +129,15 @@ void LinearForm::Assemble()
|
||||
for (i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
for (int k = 0; k < dlfi.Size(); k++)
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if ( dlfi_marker[k] == NULL ||
|
||||
(*(dlfi_marker[k]))[elem_attr-1] == 1 )
|
||||
if ( domain_integs_marker[k] == NULL ||
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1 )
|
||||
{
|
||||
fes -> GetElementVDofs (i, vdofs);
|
||||
eltrans = fes -> GetElementTransformation (i);
|
||||
dlfi[k]->AssembleRHSElementVect(*fes->GetFE(i), *eltrans, elemvect);
|
||||
domain_integs[k]->AssembleRHSElementVect(*fes->GetFE(i),
|
||||
*eltrans, elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
@@ -143,7 +145,7 @@ void LinearForm::Assemble()
|
||||
}
|
||||
AssembleDelta();
|
||||
|
||||
if (blfi.Size())
|
||||
if (boundary_integs.Size())
|
||||
{
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
|
||||
@@ -151,14 +153,14 @@ void LinearForm::Assemble()
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < blfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (blfi_marker[k] == NULL)
|
||||
if (boundary_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *blfi_marker[k];
|
||||
Array<int> &bdr_marker = *boundary_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary integrator #"
|
||||
<< k << ", counting from zero");
|
||||
@@ -174,18 +176,19 @@ void LinearForm::Assemble()
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
fes -> GetBdrElementVDofs (i, vdofs);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
for (int k=0; k < blfi.Size(); k++)
|
||||
for (int k=0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (blfi_marker[k] &&
|
||||
(*blfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_integs_marker[k] &&
|
||||
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
blfi[k]->AssembleRHSElementVect(*fes->GetBE(i), *eltrans, elemvect);
|
||||
boundary_integs[k]->AssembleRHSElementVect(*fes->GetBE(i),
|
||||
*eltrans, elemvect);
|
||||
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flfi.Size())
|
||||
if (boundary_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *tr;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
@@ -194,14 +197,14 @@ void LinearForm::Assemble()
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
bdr_attr_marker = 0;
|
||||
for (int k = 0; k < flfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (flfi_marker[k] == NULL)
|
||||
if (boundary_face_integs_marker[k] == NULL)
|
||||
{
|
||||
bdr_attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &bdr_marker = *flfi_marker[k];
|
||||
Array<int> &bdr_marker = *boundary_face_integs_marker[k];
|
||||
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
|
||||
"invalid boundary marker for boundary face integrator #"
|
||||
<< k << ", counting from zero");
|
||||
@@ -220,24 +223,26 @@ void LinearForm::Assemble()
|
||||
if (tr != NULL)
|
||||
{
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
for (int k = 0; k < flfi.Size(); k++)
|
||||
for (int k = 0; k < boundary_face_integs.Size(); k++)
|
||||
{
|
||||
if (flfi_marker[k] &&
|
||||
(*flfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
if (boundary_face_integs_marker[k] &&
|
||||
(*boundary_face_integs_marker[k])[bdr_attr-1] == 0)
|
||||
{ continue; }
|
||||
|
||||
flfi[k] -> AssembleRHSElementVect (*fes->GetFE(tr -> Elem1No),
|
||||
*tr, elemvect);
|
||||
boundary_face_integs[k]->
|
||||
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
|
||||
*tr, elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iflfi.Size())
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
|
||||
for (int k = 0; k < iflfi.Size(); k++)
|
||||
for (int k = 0; k < interior_face_integs.Size(); k++)
|
||||
{
|
||||
for (i = 0; i < mesh->GetNumFaces(); i++)
|
||||
{
|
||||
@@ -249,9 +254,10 @@ void LinearForm::Assemble()
|
||||
Array<int> vdofs2;
|
||||
fes -> GetElementVDofs (tr -> Elem2No, vdofs2);
|
||||
vdofs.Append(vdofs2);
|
||||
iflfi[k] -> AssembleRHSElementVect (*fes->GetFE(tr -> Elem1No),
|
||||
*fes->GetFE(tr -> Elem2No),
|
||||
*tr, elemvect);
|
||||
interior_face_integs[k]->
|
||||
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
|
||||
*fes->GetFE(tr->Elem2No),
|
||||
*tr, elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
@@ -277,40 +283,41 @@ void LinearForm::MakeRef(FiniteElementSpace *f, Vector &v, int v_offset)
|
||||
|
||||
void LinearForm::AssembleDelta()
|
||||
{
|
||||
if (dlfi_delta.Size() == 0) { return; }
|
||||
if (domain_delta_integs.Size() == 0) { return; }
|
||||
|
||||
if (!HaveDeltaLocations())
|
||||
{
|
||||
int sdim = fes->GetMesh()->SpaceDimension();
|
||||
Vector center;
|
||||
DenseMatrix centers(sdim, dlfi_delta.Size());
|
||||
DenseMatrix centers(sdim, domain_delta_integs.Size());
|
||||
for (int i = 0; i < centers.Width(); i++)
|
||||
{
|
||||
centers.GetColumnReference(i, center);
|
||||
dlfi_delta[i]->GetDeltaCenter(center);
|
||||
domain_delta_integs[i]->GetDeltaCenter(center);
|
||||
MFEM_VERIFY(center.Size() == sdim,
|
||||
"Point dim " << center.Size() <<
|
||||
" does not match space dim " << sdim);
|
||||
}
|
||||
fes->GetMesh()->FindPoints(centers, dlfi_delta_elem_id, dlfi_delta_ip);
|
||||
fes->GetMesh()->FindPoints(centers, domain_delta_integs_elem_id,
|
||||
domain_delta_integs_ip);
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
Vector elemvect;
|
||||
for (int i = 0; i < dlfi_delta.Size(); i++)
|
||||
for (int i = 0; i < domain_delta_integs.Size(); i++)
|
||||
{
|
||||
int elem_id = dlfi_delta_elem_id[i];
|
||||
int elem_id = domain_delta_integs_elem_id[i];
|
||||
// The delta center may be outside of this sub-domain, or
|
||||
// (Par)Mesh::FindPoints() failed to find this point:
|
||||
if (elem_id < 0) { continue; }
|
||||
|
||||
const IntegrationPoint &ip = dlfi_delta_ip[i];
|
||||
const IntegrationPoint &ip = domain_delta_integs_ip[i];
|
||||
ElementTransformation &Trans = *fes->GetElementTransformation(elem_id);
|
||||
Trans.SetIntPoint(&ip);
|
||||
|
||||
fes->GetElementVDofs(elem_id, vdofs);
|
||||
dlfi_delta[i]->AssembleDeltaElementVect(*fes->GetFE(elem_id), Trans,
|
||||
elemvect);
|
||||
domain_delta_integs[i]->AssembleDeltaElementVect(*fes->GetFE(elem_id),
|
||||
Trans, elemvect);
|
||||
AddElementVector(vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
@@ -333,11 +340,14 @@ LinearForm::~LinearForm()
|
||||
if (!extern_lfs)
|
||||
{
|
||||
int k;
|
||||
for (k=0; k < dlfi_delta.Size(); k++) { delete dlfi_delta[k]; }
|
||||
for (k=0; k < dlfi.Size(); k++) { delete dlfi[k]; }
|
||||
for (k=0; k < blfi.Size(); k++) { delete blfi[k]; }
|
||||
for (k=0; k < flfi.Size(); k++) { delete flfi[k]; }
|
||||
for (k=0; k < iflfi.Size(); k++) { delete iflfi[k]; }
|
||||
for (k=0; k < domain_delta_integs.Size(); k++)
|
||||
{ delete domain_delta_integs[k]; }
|
||||
for (k=0; k < domain_integs.Size(); k++) { delete domain_integs[k]; }
|
||||
for (k=0; k < boundary_integs.Size(); k++) { delete boundary_integs[k]; }
|
||||
for (k=0; k < boundary_face_integs.Size(); k++)
|
||||
{ delete boundary_face_integs[k]; }
|
||||
for (k=0; k < interior_face_integs.Size(); k++)
|
||||
{ delete interior_face_integs[k]; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-19
@@ -26,43 +26,46 @@ protected:
|
||||
/// FE space on which the LinearForm lives. Not owned.
|
||||
FiniteElementSpace *fes;
|
||||
|
||||
/** @brief Indicates the LinearFormIntegrator%s stored in #dlfi, #dlfi_delta,
|
||||
#blfi, and #flfi are owned by another LinearForm. */
|
||||
/** @brief Indicates the LinearFormIntegrator%s stored in #domain_integs,
|
||||
#domain_delta_integs, #boundary_integs, and #boundary_face_integs are
|
||||
owned by another LinearForm. */
|
||||
int extern_lfs;
|
||||
|
||||
/// Set of Domain Integrators to be applied.
|
||||
Array<LinearFormIntegrator*> dlfi;
|
||||
Array<LinearFormIntegrator*> domain_integs;
|
||||
/// Element attribute marker (should be of length mesh->attributes)
|
||||
/// Includes all by default.
|
||||
/// 0 - ignore attribute
|
||||
/// 1 - include attribute
|
||||
Array<Array<int>*> dlfi_marker;
|
||||
Array<Array<int>*> domain_integs_marker;
|
||||
|
||||
/// Separate array for integrators with delta function coefficients.
|
||||
Array<DeltaLFIntegrator*> dlfi_delta;
|
||||
Array<DeltaLFIntegrator*> domain_delta_integs;
|
||||
|
||||
/// Set of Boundary Integrators to be applied.
|
||||
Array<LinearFormIntegrator*> blfi;
|
||||
Array<Array<int>*> blfi_marker; ///< Entries are not owned.
|
||||
Array<LinearFormIntegrator*> boundary_integs;
|
||||
/// Entries are not owned.
|
||||
Array<Array<int>*> boundary_integs_marker;
|
||||
|
||||
/// Set of Boundary Face Integrators to be applied.
|
||||
Array<LinearFormIntegrator*> flfi;
|
||||
Array<Array<int>*> flfi_marker; ///< Entries are not owned.
|
||||
Array<LinearFormIntegrator*> boundary_face_integs;
|
||||
Array<Array<int>*> boundary_face_integs_marker; ///< Entries not owned.
|
||||
|
||||
/// Set of Internal Face Integrators to be applied.
|
||||
Array<LinearFormIntegrator*> iflfi;
|
||||
Array<LinearFormIntegrator*> interior_face_integs;
|
||||
|
||||
/// The element ids where the centers of the delta functions lie
|
||||
Array<int> dlfi_delta_elem_id;
|
||||
Array<int> domain_delta_integs_elem_id;
|
||||
|
||||
/// The reference coordinates where the centers of the delta functions lie
|
||||
Array<IntegrationPoint> dlfi_delta_ip;
|
||||
Array<IntegrationPoint> domain_delta_integs_ip;
|
||||
|
||||
/// If true, the delta locations are not (re)computed during assembly.
|
||||
bool HaveDeltaLocations() { return (dlfi_delta_elem_id.Size() != 0); }
|
||||
bool HaveDeltaLocations()
|
||||
{ return (domain_delta_integs_elem_id.Size() != 0); }
|
||||
|
||||
/// Force (re)computation of delta locations.
|
||||
void ResetDeltaLocations() { dlfi_delta_elem_id.SetSize(0); }
|
||||
void ResetDeltaLocations() { domain_delta_integs_elem_id.SetSize(0); }
|
||||
|
||||
private:
|
||||
/// Copy construction is not supported; body is undefined.
|
||||
@@ -150,22 +153,25 @@ public:
|
||||
/** @brief Access all integrators added with AddDomainIntegrator() which are
|
||||
not DeltaLFIntegrator%s or they are DeltaLFIntegrator%s with non-delta
|
||||
coefficients. */
|
||||
Array<LinearFormIntegrator*> *GetDLFI() { return &dlfi; }
|
||||
Array<LinearFormIntegrator*> *GetDLFI() { return &domain_integs; }
|
||||
|
||||
/** @brief Access all integrators added with AddDomainIntegrator() which are
|
||||
DeltaLFIntegrator%s with delta coefficients. */
|
||||
Array<DeltaLFIntegrator*> *GetDLFI_Delta() { return &dlfi_delta; }
|
||||
Array<DeltaLFIntegrator*> *GetDLFI_Delta() { return &domain_delta_integs; }
|
||||
|
||||
/// Access all integrators added with AddBoundaryIntegrator().
|
||||
Array<LinearFormIntegrator*> *GetBLFI() { return &blfi; }
|
||||
Array<LinearFormIntegrator*> *GetBLFI() { return &boundary_integs; }
|
||||
|
||||
/// Access all integrators added with AddBdrFaceIntegrator().
|
||||
Array<LinearFormIntegrator*> *GetFLFI() { return &flfi; }
|
||||
Array<LinearFormIntegrator*> *GetFLFI() { return &boundary_face_integs; }
|
||||
|
||||
/// Access all integrators added with AddInteriorFaceIntegrator().
|
||||
Array<LinearFormIntegrator*> *GetIFLFI() { return &interior_face_integs; }
|
||||
|
||||
/** @brief Access all boundary markers added with AddBdrFaceIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetFLFI_Marker() { return &flfi_marker; }
|
||||
Array<Array<int>*> *GetFLFI_Marker() { return &boundary_face_integs_marker; }
|
||||
|
||||
/// Assembles the linear form i.e. sums over all domain/bdr integrators.
|
||||
void Assemble();
|
||||
|
||||
@@ -979,6 +979,8 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
|
||||
for (int k = 0; k < bfnfi.Size(); ++k)
|
||||
{
|
||||
if (bfnfi_marker[k] &&
|
||||
(*bfnfi_marker[k])[bdr_attr-1] == 0) { continue; }
|
||||
bfnfi[k]->AssembleFaceGrad(fe, fe2, *tr, el_x_const, elmats);
|
||||
for (int l=0; l<fes.Size(); ++l)
|
||||
{
|
||||
|
||||
+2
-2
@@ -40,10 +40,10 @@ protected:
|
||||
public:
|
||||
/** @brief Prescribe a fixed IntegrationRule to use (when @a ir != NULL) or
|
||||
let the integrator choose (when @a ir == NULL). */
|
||||
void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
|
||||
virtual void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
|
||||
|
||||
/// Prescribe a fixed IntegrationRule to use.
|
||||
void SetIntegrationRule(const IntegrationRule &irule) { IntRule = &irule; }
|
||||
void SetIntegrationRule(const IntegrationRule &ir) { SetIntRule(&ir); }
|
||||
|
||||
/// Set the memory type used for GeometricFactors and other large allocations
|
||||
/// in PA extensions.
|
||||
|
||||
+10
-8
@@ -130,7 +130,7 @@ void ParBilinearForm::ParallelAssemble(OperatorHandle &A, SparseMatrix *A_local)
|
||||
|
||||
OperatorHandle dA(A.Type()), Ph(A.Type()), hdA;
|
||||
|
||||
if (fbfi.Size() == 0)
|
||||
if (interior_face_integs.Size() == 0)
|
||||
{
|
||||
// construct a parallel block-diagonal matrix 'A' based on 'a'
|
||||
dA.MakeSquareBlockDiag(pfes->GetComm(), pfes->GlobalVSize(),
|
||||
@@ -214,11 +214,12 @@ void ParBilinearForm::AssembleSharedFaces(int skip_zeros)
|
||||
}
|
||||
}
|
||||
vdofs_all.Append(vdofs2);
|
||||
for (int k = 0; k < fbfi.Size(); k++)
|
||||
for (int k = 0; k < interior_face_integs.Size(); k++)
|
||||
{
|
||||
fbfi[k]->AssembleFaceMatrix(*pfes->GetFE(T->Elem1No),
|
||||
*pfes->GetFaceNbrFE(Elem2NbrNo),
|
||||
*T, elemmat);
|
||||
interior_face_integs[k]->
|
||||
AssembleFaceMatrix(*pfes->GetFE(T->Elem1No),
|
||||
*pfes->GetFaceNbrFE(Elem2NbrNo),
|
||||
*T, elemmat);
|
||||
if (keep_nbr_block)
|
||||
{
|
||||
mat->AddSubMatrix(vdofs_all, vdofs_all, elemmat, skip_zeros);
|
||||
@@ -233,7 +234,7 @@ void ParBilinearForm::AssembleSharedFaces(int skip_zeros)
|
||||
|
||||
void ParBilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
if (fbfi.Size())
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
pfes->ExchangeFaceNbrData();
|
||||
if (!ext && mat == NULL)
|
||||
@@ -244,7 +245,7 @@ void ParBilinearForm::Assemble(int skip_zeros)
|
||||
|
||||
BilinearForm::Assemble(skip_zeros);
|
||||
|
||||
if (!ext && fbfi.Size() > 0)
|
||||
if (!ext && interior_face_integs.Size() > 0)
|
||||
{
|
||||
AssembleSharedFaces(skip_zeros);
|
||||
}
|
||||
@@ -316,7 +317,8 @@ ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
void ParBilinearForm::TrueAddMult(const Vector &x, Vector &y, const double a)
|
||||
const
|
||||
{
|
||||
MFEM_VERIFY(fbfi.Size() == 0, "the case of interior face integrators is not"
|
||||
MFEM_VERIFY(interior_face_integs.Size() == 0,
|
||||
"the case of interior face integrators is not"
|
||||
" implemented");
|
||||
|
||||
if (X.ParFESpace() != pfes)
|
||||
|
||||
+2
-2
@@ -515,7 +515,7 @@ const FiniteElement *ParFiniteElementSpace::GetFE(int i) const
|
||||
else { return FiniteElementSpace::GetFE(i); }
|
||||
}
|
||||
|
||||
const Operator *ParFiniteElementSpace::GetFaceRestriction(
|
||||
const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
|
||||
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
|
||||
{
|
||||
const bool is_dg_space = IsDGSpace();
|
||||
@@ -529,7 +529,7 @@ const Operator *ParFiniteElementSpace::GetFaceRestriction(
|
||||
}
|
||||
else
|
||||
{
|
||||
Operator* res;
|
||||
FaceRestriction *res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
res = new ParL2FaceRestriction(*this, e_ordering, type, m);
|
||||
|
||||
+1
-1
@@ -299,7 +299,7 @@ public:
|
||||
presence of shared faces. Shared faces are treated as interior faces,
|
||||
the returned operator handles the communication needed to get the
|
||||
shared face values from other MPI ranks */
|
||||
virtual const Operator *GetFaceRestriction(
|
||||
virtual const FaceRestriction *GetFaceRestriction(
|
||||
ElementDofOrdering e_ordering, FaceType type,
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const;
|
||||
|
||||
|
||||
+7
-6
@@ -47,7 +47,7 @@ void ParLinearForm::Assemble()
|
||||
{
|
||||
LinearForm::Assemble();
|
||||
|
||||
if (iflfi.Size())
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
pfes->ExchangeFaceNbrData();
|
||||
AssembleSharedFaces();
|
||||
@@ -59,10 +59,10 @@ void ParLinearForm::AssembleSharedFaces()
|
||||
Array<int> vdofs;
|
||||
Vector elemvect;
|
||||
|
||||
if (iflfi.Size())
|
||||
if (interior_face_integs.Size())
|
||||
{
|
||||
ParMesh *pmesh = pfes->GetParMesh();
|
||||
for (int k = 0; k < iflfi.Size(); k++)
|
||||
for (int k = 0; k < interior_face_integs.Size(); k++)
|
||||
{
|
||||
for (int i = 0; i < pmesh->GetNSharedFaces(); i++)
|
||||
{
|
||||
@@ -73,9 +73,10 @@ void ParLinearForm::AssembleSharedFaces()
|
||||
{
|
||||
int Elem2Nbr = tr->Elem2No - pmesh->GetNE();
|
||||
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
|
||||
iflfi[0] -> AssembleRHSElementVect (*fes->GetFE(tr -> Elem1No),
|
||||
*pfes->GetFaceNbrFE(Elem2Nbr),
|
||||
*tr, elemvect);
|
||||
interior_face_integs[k]->
|
||||
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
|
||||
*pfes->GetFaceNbrFE(Elem2Nbr),
|
||||
*tr, elemvect);
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -847,7 +847,7 @@ void H1FaceRestriction::Mult(const Vector& x, Vector& y) const
|
||||
});
|
||||
}
|
||||
|
||||
void H1FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
void H1FaceRestriction::AddMultTranspose(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
@@ -856,7 +856,7 @@ void H1FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
auto d_offsets = offsets.Read();
|
||||
auto d_indices = gather_indices.Read();
|
||||
auto d_x = Reshape(x.Read(), nd, vd, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
@@ -1267,7 +1267,7 @@ void L2FaceRestriction::Mult(const Vector& x, Vector& y) const
|
||||
}
|
||||
}
|
||||
|
||||
void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
void L2FaceRestriction::AddMultTranspose(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
@@ -1280,7 +1280,7 @@ void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
if (m == L2FaceValues::DoubleValued)
|
||||
{
|
||||
auto d_x = Reshape(x.Read(), nd, vd, 2, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
@@ -1304,7 +1304,7 @@ void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
else
|
||||
{
|
||||
auto d_x = Reshape(x.Read(), nd, vd, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
|
||||
+121
-15
@@ -21,10 +21,6 @@ namespace mfem
|
||||
class FiniteElementSpace;
|
||||
enum class ElementDofOrdering;
|
||||
|
||||
/** An enum type to specify if only e1 value is requested (SingleValued) or both
|
||||
e1 and e2 (DoubleValued). */
|
||||
enum class L2FaceValues : bool {SingleValued, DoubleValued};
|
||||
|
||||
/// Operator that converts FiniteElementSpace L-vectors to E-vectors.
|
||||
/** Objects of this type are typically created and owned by FiniteElementSpace
|
||||
objects, see FiniteElementSpace::GetElementRestriction(). */
|
||||
@@ -104,10 +100,75 @@ public:
|
||||
void FillJAndData(const Vector &ea_data, SparseMatrix &mat) const;
|
||||
};
|
||||
|
||||
/// Operator that extracts Face degrees of freedom.
|
||||
/** An enum type to specify if only e1 value is requested (SingleValued) or both
|
||||
e1 and e2 (DoubleValued). */
|
||||
enum class L2FaceValues : bool {SingleValued, DoubleValued};
|
||||
|
||||
/** @brief Base class for operators that extracts Face degrees of freedom.
|
||||
|
||||
In order to compute quantities on the faces of a mesh, it is often useful to
|
||||
extract the degrees of freedom on the faces of the elements. This class
|
||||
provides an interface for such operations.
|
||||
|
||||
If the FiniteElementSpace is ordered by Ordering::byVDIM, then the expected
|
||||
format for the L-vector is (vdim x ndofs), otherwise if Ordering::byNODES
|
||||
the expected format is (ndofs x vdim), where ndofs is the total number of
|
||||
degrees of freedom.
|
||||
Since FiniteElementSpace can either be continuous or discontinuous, the
|
||||
degrees of freedom on a face can either be single valued or double valued,
|
||||
this is what we refer to as the multiplicity and is represented by the
|
||||
L2FaceValues enum type.
|
||||
The format of the output face E-vector of degrees of freedom is
|
||||
(face_dofs x vdim x multiplicity x nfaces), where face_dofs is the number of
|
||||
degrees of freedom on each face, and nfaces the number of faces of the
|
||||
requested FaceType (see FiniteElementSpace::GetNFbyType).
|
||||
|
||||
@note Objects of this type are typically created and owned by
|
||||
FiniteElementSpace objects, see FiniteElementSpace::GetFaceRestriction(). */
|
||||
class FaceRestriction : public Operator
|
||||
{
|
||||
public:
|
||||
FaceRestriction(): Operator() { }
|
||||
|
||||
FaceRestriction(int h, int w): Operator(h, w) { }
|
||||
|
||||
virtual ~FaceRestriction() { }
|
||||
|
||||
/** @brief Extract the face degrees of freedom from @a x into @a y.
|
||||
|
||||
@param[in] x The L-vector of degrees of freedom.
|
||||
@param[out] y The degrees of freedom on the face, corresponding to a face
|
||||
E-vector.
|
||||
*/
|
||||
void Mult(const Vector &x, Vector &y) const override = 0;
|
||||
|
||||
/** @brief Add the face degrees of freedom @a x to the element degrees of
|
||||
freedom @a y.
|
||||
|
||||
@param[in] x The face degrees of freedom on the face.
|
||||
@param[in,out] y The L-vector of degrees of freedom to which we add the
|
||||
face degrees of freedom.
|
||||
*/
|
||||
virtual void AddMultTranspose(const Vector &x, Vector &y) const = 0;
|
||||
|
||||
/** @brief Set the face degrees of freedom in the element degrees of freedom
|
||||
@a y to the values given in @a x.
|
||||
|
||||
@param[in] x The face degrees of freedom on the face.
|
||||
@param[in,out] y The L-vector of degrees of freedom to which we add the
|
||||
face degrees of freedom.
|
||||
*/
|
||||
void MultTranspose(const Vector &x, Vector &y) const override
|
||||
{
|
||||
y = 0.0;
|
||||
AddMultTranspose(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
/// Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
|
||||
/** Objects of this type are typically created and owned by FiniteElementSpace
|
||||
objects, see FiniteElementSpace::GetFaceRestriction(). */
|
||||
class H1FaceRestriction : public Operator
|
||||
class H1FaceRestriction : public FaceRestriction
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace &fes;
|
||||
@@ -122,16 +183,42 @@ protected:
|
||||
Array<int> gather_indices;
|
||||
|
||||
public:
|
||||
H1FaceRestriction(const FiniteElementSpace&, const ElementDofOrdering,
|
||||
const FaceType);
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
/** @brief Constructor for a H1FaceRestriction.
|
||||
|
||||
@param[in] fes The FiniteElementSpace on which this H1FaceRestriction
|
||||
operates.
|
||||
@param[in] ordering The requested output ordering of the
|
||||
H1FaceRestriction, either Native or Lexicographic.
|
||||
@param[in] type The requested type of faces on which this operator
|
||||
extracts the degrees of freedom, either Interior or
|
||||
Boundary.
|
||||
*/
|
||||
H1FaceRestriction(const FiniteElementSpace& fes,
|
||||
const ElementDofOrdering ordering,
|
||||
const FaceType type);
|
||||
|
||||
/** @brief Extract the face degrees of freedom from @a x into @a y.
|
||||
|
||||
@param[in] x The L-vector of degrees of freedom.
|
||||
@param[out] y The degrees of freedom on the face, corresponding to a face
|
||||
E-vector.
|
||||
*/
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/** @brief Add the face degrees of freedom @a x to the element degrees of
|
||||
freedom @a y.
|
||||
|
||||
@param[in] x The face degrees of freedom on the face.
|
||||
@param[in,out] y The L-vector of degrees of freedom to which we add the
|
||||
face degrees of freedom.
|
||||
*/
|
||||
void AddMultTranspose(const Vector &x, Vector &y) const override;
|
||||
};
|
||||
|
||||
/// Operator that extracts Face degrees of freedom.
|
||||
/// Operator that extracts Face degrees of freedom on L2 FiniteElementSpaces.
|
||||
/** Objects of this type are typically created and owned by FiniteElementSpace
|
||||
objects, see FiniteElementSpace::GetFaceRestriction(). */
|
||||
class L2FaceRestriction : public Operator
|
||||
class L2FaceRestriction : public FaceRestriction
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace &fes;
|
||||
@@ -154,19 +241,38 @@ protected:
|
||||
const L2FaceValues m = L2FaceValues::DoubleValued);
|
||||
|
||||
public:
|
||||
L2FaceRestriction(const FiniteElementSpace&, const ElementDofOrdering,
|
||||
L2FaceRestriction(const FiniteElementSpace&,
|
||||
const ElementDofOrdering,
|
||||
const FaceType,
|
||||
const L2FaceValues m = L2FaceValues::DoubleValued);
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
|
||||
/** @brief Extract the face degrees of freedom from @a x into @a y.
|
||||
|
||||
@param[in] x The L-vector of degrees of freedom.
|
||||
@param[out] y The degrees of freedom on the face, corresponding to a face
|
||||
E-vector.
|
||||
*/
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/** @brief Add the face degrees of freedom @a x to the element degrees of
|
||||
freedom @a y.
|
||||
|
||||
@param[in] x The face degrees of freedom on the face.
|
||||
@param[in,out] y The L-vector of degrees of freedom to which we add the
|
||||
face degrees of freedom.
|
||||
*/
|
||||
void AddMultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
/** Fill the I array of SparseMatrix corresponding to the sparsity pattern
|
||||
given by this L2FaceRestriction. */
|
||||
virtual void FillI(SparseMatrix &mat, const bool keep_nbr_block = false) const;
|
||||
|
||||
/** Fill the J and Data arrays of SparseMatrix corresponding to the sparsity
|
||||
pattern given by this L2FaceRestriction, and the values of ea_data. */
|
||||
virtual void FillJAndData(const Vector &ea_data,
|
||||
SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const;
|
||||
|
||||
/// This methods adds the DG face matrices to the element matrices.
|
||||
void AddFaceMatricesToElementMatrices(Vector &fea_data,
|
||||
Vector &ea_data) const;
|
||||
|
||||
@@ -34,6 +34,7 @@ void TMOP_Combo_QualityMetric::EvalP(const DenseMatrix &Jpt,
|
||||
DenseMatrix &P) const
|
||||
{
|
||||
DenseMatrix Pt(P.Size());
|
||||
P = 0.0;
|
||||
for (int i = 0; i < tmop_q_arr.Size(); i++)
|
||||
{
|
||||
tmop_q_arr[i]->EvalP(Jpt, Pt);
|
||||
@@ -50,6 +51,7 @@ void TMOP_Combo_QualityMetric::AssembleH(const DenseMatrix &Jpt,
|
||||
DenseMatrix At(A.Size());
|
||||
for (int i = 0; i < tmop_q_arr.Size(); i++)
|
||||
{
|
||||
At = 0.0;
|
||||
tmop_q_arr[i]->AssembleH(Jpt, DS, weight, At);
|
||||
At *= wt_arr[i];
|
||||
A += At;
|
||||
|
||||
@@ -371,6 +371,8 @@ public:
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
virtual int Id() const { return 80; }
|
||||
double GetGamma() const { return gamma; }
|
||||
|
||||
virtual ~TMOP_Metric_080() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
@@ -590,6 +592,52 @@ public:
|
||||
virtual int Id() const { return 321; }
|
||||
};
|
||||
|
||||
/// 3D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_Metric_332 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
public:
|
||||
TMOP_Metric_332(double gamma_) : gamma(gamma_),
|
||||
sh_metric(new TMOP_Metric_302),
|
||||
sz_metric(new TMOP_Metric_315)
|
||||
{
|
||||
// (1-gamma) mu_302 + gamma mu_315
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual int Id() const { return 332; }
|
||||
double GetGamma() const { return gamma; }
|
||||
|
||||
virtual ~TMOP_Metric_332() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// 3D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_Metric_333 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
public:
|
||||
TMOP_Metric_333(double gamma_) : gamma(gamma_),
|
||||
sh_metric(new TMOP_Metric_302),
|
||||
sz_metric(new TMOP_Metric_316)
|
||||
{
|
||||
// (1-gamma) mu_302 + gamma mu_316
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual int Id() const { return 333; }
|
||||
double GetGamma() const { return gamma; }
|
||||
|
||||
virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// Shifted barrier form of 3D metric 16 (volume, ideal barrier metric), 3D
|
||||
class TMOP_Metric_352 : public TMOP_QualityMetric
|
||||
{
|
||||
|
||||
@@ -150,9 +150,49 @@ void EvalH_077(const int e, const int qx, const int qy,
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline
|
||||
void EvalH_080(const int e, const int qx, const int qy,
|
||||
const double weight, const double gamma, const double *Jpt,
|
||||
DeviceTensor<7,double> H)
|
||||
{
|
||||
// h_80 = (1-gamma) h_2 + gamma h_77.
|
||||
|
||||
constexpr int DIM = 2;
|
||||
double ddI1[4], ddI1b[4], dI2[4], dI2b[4], ddI2[4];
|
||||
kernels::InvariantsEvaluator2D ie(Args()
|
||||
.J(Jpt)
|
||||
.dI2(dI2)
|
||||
.ddI1(ddI1)
|
||||
.ddI1b(ddI1b)
|
||||
.dI2b(dI2b)
|
||||
.ddI2(ddI2));
|
||||
|
||||
const double I2 = ie.Get_I2(), I2inv_sq = 1.0 / (I2 * I2);
|
||||
ConstDeviceMatrix di2(ie.Get_dI2(),DIM,DIM);
|
||||
for (int i = 0; i < DIM; i++)
|
||||
{
|
||||
for (int j = 0; j < DIM; j++)
|
||||
{
|
||||
ConstDeviceMatrix ddi1b(ie.Get_ddI1b(i,j),DIM,DIM);
|
||||
ConstDeviceMatrix ddi2(ie.Get_ddI2(i,j),DIM,DIM);
|
||||
for (int r = 0; r < DIM; r++)
|
||||
{
|
||||
for (int c = 0; c < DIM; c++)
|
||||
{
|
||||
H(r,c,i,j,qx,qy,e) =
|
||||
(1.0 - gamma) * 0.5 * weight * ddi1b(r,c) +
|
||||
gamma * ( weight * 0.5 * (1.0 - I2inv_sq) * ddi2(r,c) +
|
||||
weight * (I2inv_sq / I2) * di2(r,c) * di2(i,j) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_2D,
|
||||
const Vector &x_,
|
||||
const double metric_normal,
|
||||
const double metric_param,
|
||||
const int mid,
|
||||
const int NE,
|
||||
const Array<double> &w_,
|
||||
@@ -163,7 +203,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_2D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77,
|
||||
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77 || mid == 80,
|
||||
"Metric not yet implemented!");
|
||||
|
||||
constexpr int DIM = 2;
|
||||
@@ -222,6 +262,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_2D,
|
||||
if (mid == 2) { EvalH_002(e,qx,qy,weight,Jpt,H); }
|
||||
if (mid == 7) { EvalH_007(e,qx,qy,weight,Jpt,H); }
|
||||
if (mid == 77) { EvalH_077(e,qx,qy,weight,Jpt,H); }
|
||||
if (mid == 80) { EvalH_080(e,qx,qy,weight,metric_param,Jpt,H); }
|
||||
} // qx
|
||||
} // qy
|
||||
});
|
||||
@@ -241,7 +282,10 @@ void TMOP_Integrator::AssembleGradPA_2D(const Vector &X) const
|
||||
const Array<double> &G = PA.maps->G;
|
||||
Vector &H = PA.H;
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_2D,id,X,mn,M,N,W,B,G,J,H);
|
||||
double mp = 0.0;
|
||||
if (auto m = dynamic_cast<TMOP_Metric_080 *>(metric)) { mp = m->GetGamma(); }
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_2D,id,X,mn,mp,M,N,W,B,G,J,H);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -181,8 +181,58 @@ void EvalH_321(const int e, const int qx, const int qy, const int qz,
|
||||
}
|
||||
}
|
||||
|
||||
// H_332 = (1-gamma) H_302 + gamma H_315
|
||||
static MFEM_HOST_DEVICE inline
|
||||
void EvalH_332(const int e, const int qx, const int qy, const int qz,
|
||||
const double weight, const double gamma,
|
||||
const double *J, DeviceTensor<8,double> dP)
|
||||
{
|
||||
double B[9];
|
||||
double dI1b[9], ddI1b[9];
|
||||
double dI2[9], dI2b[9], ddI2[9], ddI2b[9];
|
||||
double dI3b[9], ddI3b[9];
|
||||
constexpr int DIM = 3;
|
||||
kernels::InvariantsEvaluator3D ie(Args()
|
||||
.J(J).B(B)
|
||||
.dI1b(dI1b).ddI1b(ddI1b)
|
||||
.dI2(dI2).dI2b(dI2b).ddI2(ddI2).ddI2b(ddI2b)
|
||||
.dI3b(dI3b).ddI3b(ddI3b));
|
||||
double sign_detJ;
|
||||
const double c1 = weight/9.;
|
||||
const double I1b = ie.Get_I1b();
|
||||
const double I2b = ie.Get_I2b();
|
||||
const double I3b = ie.Get_I3b(sign_detJ);
|
||||
ConstDeviceMatrix di1b(ie.Get_dI1b(),DIM,DIM);
|
||||
ConstDeviceMatrix di2b(ie.Get_dI2b(),DIM,DIM);
|
||||
ConstDeviceMatrix di3b(ie.Get_dI3b(sign_detJ),DIM,DIM);
|
||||
for (int i = 0; i < DIM; i++)
|
||||
{
|
||||
for (int j = 0; j < DIM; j++)
|
||||
{
|
||||
ConstDeviceMatrix ddi1b(ie.Get_ddI1b(i,j),DIM,DIM);
|
||||
ConstDeviceMatrix ddi2b(ie.Get_ddI2b(i,j),DIM,DIM);
|
||||
ConstDeviceMatrix ddi3b(ie.Get_ddI3b(i,j),DIM,DIM);
|
||||
for (int r = 0; r < DIM; r++)
|
||||
{
|
||||
for (int c = 0; c < DIM; c++)
|
||||
{
|
||||
const double dp_302 =
|
||||
(di2b(r,c)*di1b(i,j) + di1b(r,c)*di2b(i,j))
|
||||
+ ddi2b(r,c)*I1b
|
||||
+ ddi1b(r,c)*I2b;
|
||||
const double dp_315 = 2.0 * weight * (I3b - 1.0) * ddi3b(r,c) +
|
||||
2.0 * weight * di3b(r,c) * di3b(i,j);
|
||||
dP(r,c,i,j,qx,qy,qz,e) = (1.0 - gamma) * c1 * dp_302 +
|
||||
gamma * dp_315;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_3D,
|
||||
const double metric_normal,
|
||||
const double metric_param,
|
||||
const int mid,
|
||||
const Vector &x_,
|
||||
const int NE,
|
||||
@@ -194,8 +244,8 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_3D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 || mid == 321 ,
|
||||
"3D metric not yet implemented!");
|
||||
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 ||
|
||||
mid == 321 || mid == 332, "3D metric not yet implemented!");
|
||||
|
||||
constexpr int DIM = 3;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
@@ -255,6 +305,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_3D,
|
||||
if (mid == 303) { EvalH_303(e,qx,qy,qz,weight,Jpt,H); }
|
||||
if (mid == 315) { EvalH_315(e,qx,qy,qz,weight,Jpt,H); }
|
||||
if (mid == 321) { EvalH_321(e,qx,qy,qz,weight,Jpt,H); }
|
||||
if (mid == 332) { EvalH_332(e,qx,qy,qz,weight,metric_param,Jpt,H); }
|
||||
} // qx
|
||||
} // qy
|
||||
} // qz
|
||||
@@ -275,7 +326,10 @@ void TMOP_Integrator::AssembleGradPA_3D(const Vector &X) const
|
||||
const Array<double> &G = PA.maps->G;
|
||||
Vector &H = PA.H;
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_3D,id,mn,M,X,N,W,B,G,J,H);
|
||||
double mp = 0.0;
|
||||
if (auto m = dynamic_cast<TMOP_Metric_332 *>(metric)) { mp = m->GetGamma(); }
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_3D,id,mn,mp,M,X,N,W,B,G,J,H);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+22
-2
@@ -58,8 +58,24 @@ void EvalP_077(const double *Jpt, double *P)
|
||||
kernels::Set(2,2, 0.5 * (1.0 - 1.0 / (I2 * I2)), ie.Get_dI2(), P);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline
|
||||
void EvalP_080(const double *Jpt, double gamma, double *P)
|
||||
{
|
||||
// p_80 = (1-gamma) p_2 + gamma p_77.
|
||||
|
||||
double dI1b[4], dI2[4], dI2b[4];
|
||||
kernels::InvariantsEvaluator2D ie(Args().J(Jpt).
|
||||
dI1b(dI1b).dI2(dI2).dI2b(dI2b));
|
||||
|
||||
kernels::Set(2,2, (1.0 - gamma) * 1./2., ie.Get_dI1b(), P);
|
||||
|
||||
const double I2 = ie.Get_I2();
|
||||
kernels::Add(2,2, gamma * 0.5 * (1.0 - 1.0 / (I2 * I2)), ie.Get_dI2(), P);
|
||||
}
|
||||
|
||||
MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_2D,
|
||||
const double metric_normal,
|
||||
const double metric_param,
|
||||
const int mid,
|
||||
const int NE,
|
||||
const DenseTensor &j_,
|
||||
@@ -71,7 +87,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_2D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77,
|
||||
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77 || mid == 80,
|
||||
"Metric not yet implemented!");
|
||||
|
||||
constexpr int DIM = 2;
|
||||
@@ -132,6 +148,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_2D,
|
||||
if (mid == 2) { EvalP_002(Jpt, P); }
|
||||
if (mid == 7) { EvalP_007(Jpt, P); }
|
||||
if (mid == 77) { EvalP_077(Jpt, P); }
|
||||
if (mid == 80) { EvalP_080(Jpt, metric_param, P); }
|
||||
for (int i = 0; i < 4; i++) { P[i] *= weight; }
|
||||
|
||||
// PMatO += DS . P^t += DSh . (Jrt . P^t)
|
||||
@@ -160,7 +177,10 @@ void TMOP_Integrator::AddMultPA_2D(const Vector &X, Vector &Y) const
|
||||
const Array<double> &G = PA.maps->G;
|
||||
const double mn = metric_normal;
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_2D,id,mn,M,N,J,W,B,G,X,Y);
|
||||
double mp = 0.0;
|
||||
if (auto m = dynamic_cast<TMOP_Metric_080 *>(metric)) { mp = m->GetGamma(); }
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_2D,id,mn,mp,M,N,J,W,B,G,X,Y);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+32
-7
@@ -75,8 +75,29 @@ void EvalP_321(const double *J, double *P)
|
||||
kernels::Add(3,3, ie.Get_dI1(), P);
|
||||
}
|
||||
|
||||
// P_332 = (1-gamma) P_302 + gamma P_315.
|
||||
static MFEM_HOST_DEVICE inline
|
||||
void EvalP_332(const double *J, double gamma, double *P)
|
||||
{
|
||||
double B[9];
|
||||
double dI1b[9], dI2[9], dI2b[9], dI3b[9];
|
||||
kernels::InvariantsEvaluator3D ie(Args()
|
||||
.J(J).B(B)
|
||||
.dI1b(dI1b)
|
||||
.dI2(dI2).dI2b(dI2b)
|
||||
.dI3b(dI3b));
|
||||
const double alpha = (1.0 - gamma) * ie.Get_I1b()/9.;
|
||||
const double beta = (1.0 - gamma) * ie.Get_I2b()/9.;
|
||||
kernels::Add(3,3, alpha, ie.Get_dI2b(), beta, ie.Get_dI1b(), P);
|
||||
|
||||
double sign_detJ;
|
||||
const double I3b = ie.Get_I3b(sign_detJ);
|
||||
kernels::Add(3,3, gamma * 2.0 * (I3b - 1.0), ie.Get_dI3b(sign_detJ), P);
|
||||
}
|
||||
|
||||
MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_3D,
|
||||
const double metric_normal,
|
||||
double metric_param,
|
||||
const int mid,
|
||||
const int NE,
|
||||
const DenseTensor &j_,
|
||||
@@ -88,8 +109,8 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_3D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 || mid == 321 ,
|
||||
"3D metric not yet implemented!");
|
||||
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 ||
|
||||
mid == 321 || mid == 332, "3D metric not yet implemented!");
|
||||
|
||||
constexpr int DIM = 3;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
@@ -146,10 +167,11 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_3D,
|
||||
|
||||
// metric->EvalP(Jpt, P);
|
||||
double P[9];
|
||||
if (mid == 302) { EvalP_302(Jpt,P); }
|
||||
if (mid == 303) { EvalP_303(Jpt,P); }
|
||||
if (mid == 315) { EvalP_315(Jpt,P); }
|
||||
if (mid == 321) { EvalP_321(Jpt,P); }
|
||||
if (mid == 302) { EvalP_302(Jpt, P); }
|
||||
if (mid == 303) { EvalP_303(Jpt, P); }
|
||||
if (mid == 315) { EvalP_315(Jpt, P); }
|
||||
if (mid == 321) { EvalP_321(Jpt, P); }
|
||||
if (mid == 332) { EvalP_332(Jpt, metric_param, P); }
|
||||
for (int i = 0; i < 9; i++) { P[i] *= weight; }
|
||||
|
||||
// Y += DS . P^t += DSh . (Jrt . P^t)
|
||||
@@ -180,7 +202,10 @@ void TMOP_Integrator::AddMultPA_3D(const Vector &X, Vector &Y) const
|
||||
const Array<double> &G = PA.maps->G;
|
||||
const double mn = metric_normal;
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_3D,id,mn,M,N,J,W,B,G,X,Y);
|
||||
double mp = 0.0;
|
||||
if (auto m = dynamic_cast<TMOP_Metric_332 *>(metric)) { mp = m->GetGamma(); }
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_3D,id,mn,mp,M,N,J,W,B,G,X,Y);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+15
-4
@@ -50,8 +50,15 @@ double EvalW_077(const double *Jpt)
|
||||
return 0.5*(I2b*I2b + 1./(I2b*I2b) - 2.);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline
|
||||
double EvalW_080(const double *Jpt, double gamma)
|
||||
{
|
||||
return (1.0 - gamma) * EvalW_002(Jpt) + gamma * EvalW_077(Jpt);
|
||||
}
|
||||
|
||||
MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_2D,
|
||||
const double metric_normal,
|
||||
const double metric_param,
|
||||
const int mid,
|
||||
const int NE,
|
||||
const DenseTensor &j_,
|
||||
@@ -64,7 +71,7 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_2D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77,
|
||||
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77 || mid == 80,
|
||||
"2D metric not yet implemented!");
|
||||
|
||||
constexpr int DIM = 2;
|
||||
@@ -125,7 +132,8 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_2D,
|
||||
mid == 1 ? EvalW_001(Jpt) :
|
||||
mid == 2 ? EvalW_002(Jpt) :
|
||||
mid == 7 ? EvalW_007(Jpt) :
|
||||
mid == 77 ? EvalW_077(Jpt) : 0.0;
|
||||
mid == 77 ? EvalW_077(Jpt) :
|
||||
mid == 80 ? EvalW_080(Jpt, metric_param) : 0.0;
|
||||
|
||||
E(qx,qy,e) = weight * EvalW;
|
||||
}
|
||||
@@ -141,7 +149,7 @@ double TMOP_Integrator::GetLocalStateEnergyPA_2D(const Vector &X) const
|
||||
const int D1D = PA.maps->ndof;
|
||||
const int Q1D = PA.maps->nqpt;
|
||||
const int id = (D1D << 4 ) | Q1D;
|
||||
const double m = metric_normal;
|
||||
const double mn = metric_normal;
|
||||
const DenseTensor &J = PA.Jtr;
|
||||
const Array<double> &W = PA.ir->GetWeights();
|
||||
const Array<double> &B = PA.maps->B;
|
||||
@@ -149,7 +157,10 @@ double TMOP_Integrator::GetLocalStateEnergyPA_2D(const Vector &X) const
|
||||
const Vector &O = PA.O;
|
||||
Vector &E = PA.E;
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_2D,id,m,M,N,J,W,B,G,X,O,E);
|
||||
double mp = 0.0;
|
||||
if (auto m = dynamic_cast<TMOP_Metric_080 *>(metric)) { mp = m->GetGamma(); }
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_2D,id,mn,mp,M,N,J,W,B,G,X,O,E);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+15
-4
@@ -58,8 +58,15 @@ double EvalW_321(const double *J)
|
||||
return ie.Get_I1() + ie.Get_I2()/ie.Get_I3() - 6.0;
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline
|
||||
double EvalW_332(const double *J, double gamma)
|
||||
{
|
||||
return (1.0 - gamma) * EvalW_302(J) + gamma * EvalW_315(J);
|
||||
}
|
||||
|
||||
MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_3D,
|
||||
const double metric_normal,
|
||||
const double metric_param,
|
||||
const int mid,
|
||||
const int NE,
|
||||
const DenseTensor &j_,
|
||||
@@ -72,8 +79,8 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_3D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 || mid == 321 ,
|
||||
"3D metric not yet implemented!");
|
||||
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 ||
|
||||
mid == 321 || mid == 332, "3D metric not yet implemented!");
|
||||
|
||||
constexpr int DIM = 3;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
@@ -134,7 +141,8 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_3D,
|
||||
mid == 302 ? EvalW_302(Jpt) :
|
||||
mid == 303 ? EvalW_303(Jpt) :
|
||||
mid == 315 ? EvalW_315(Jpt) :
|
||||
mid == 321 ? EvalW_321(Jpt) : 0.0;
|
||||
mid == 321 ? EvalW_321(Jpt) :
|
||||
mid == 332 ? EvalW_332(Jpt, metric_param) : 0.0;
|
||||
|
||||
E(qx,qy,qz,e) = weight * EvalW;
|
||||
}
|
||||
@@ -159,7 +167,10 @@ double TMOP_Integrator::GetLocalStateEnergyPA_3D(const Vector &X) const
|
||||
const Vector &O = PA.O;
|
||||
Vector &E = PA.E;
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_3D,id,mn,M,N,J,W,B,G,O,X,E);
|
||||
double mp = 0.0;
|
||||
if (auto m = dynamic_cast<TMOP_Metric_332 *>(metric)) { mp = m->GetGamma(); }
|
||||
|
||||
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_3D,id,mn,mp,M,N,J,W,B,G,O,X,E);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -71,7 +71,7 @@ static const unsigned char b64table[] =
|
||||
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
|
||||
};
|
||||
|
||||
void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf)
|
||||
void DecodeBase64(const char *src, size_t len, std::vector<char> &buf)
|
||||
{
|
||||
const unsigned char *in = (const unsigned char *)src;
|
||||
buf.clear();
|
||||
@@ -79,7 +79,7 @@ void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf)
|
||||
for (size_t i=0; i<len; ++i) { if (b64table[in[i]] != 255) { ++count; } }
|
||||
if (count % 4 != 0) { return; }
|
||||
buf.resize(3*len/4);
|
||||
unsigned char *out = buf.data();
|
||||
unsigned char *out = (unsigned char *)buf.data();
|
||||
count = 0;
|
||||
int pad = 0;
|
||||
unsigned char c[4];
|
||||
@@ -97,8 +97,10 @@ void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf)
|
||||
count = pad = 0;
|
||||
}
|
||||
}
|
||||
buf.resize(out - buf.data());
|
||||
buf.resize(out - (unsigned char *)buf.data());
|
||||
}
|
||||
|
||||
size_t NumBase64Chars(size_t nbytes) { return ((4*nbytes/3) + 3) & ~3; }
|
||||
|
||||
} // namespace mfem::bin_io
|
||||
} // namespace mfem
|
||||
|
||||
+11
-2
@@ -50,6 +50,7 @@ inline T read(const char *buf)
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Append the binary representation of @a val to the byte buffer @a vec.
|
||||
template <typename T>
|
||||
void AppendBytes(std::vector<char> &vec, const T &val)
|
||||
{
|
||||
@@ -57,9 +58,17 @@ void AppendBytes(std::vector<char> &vec, const T &val)
|
||||
vec.insert(vec.end(), ptr, ptr + sizeof(T));
|
||||
}
|
||||
|
||||
void WriteBase64(std::ostream &out, const void *bytes, size_t length);
|
||||
/// Given a buffer @a buf of length @a nbytes, encode the data in base-64
|
||||
/// format, and write the encoded data to the output stream @a out.
|
||||
void WriteBase64(std::ostream &out, const void *bytes, size_t nbytes);
|
||||
|
||||
void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf);
|
||||
/// Decode @a len base-64 encoded characters in the buffer @a src, and store the
|
||||
/// resulting decoded data in @a buf. @a buf will be resized as needed.
|
||||
void DecodeBase64(const char *src, size_t len, std::vector<char> &buf);
|
||||
|
||||
/// Return the number of characters needed to encode @a nbytes in base-64. This
|
||||
/// is equal to 4*nbytes/3, rounded up to the nearest multiple of 4.
|
||||
size_t NumBase64Chars(size_t nbytes);
|
||||
|
||||
} // namespace mfem::bin_io
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ list(APPEND SRCS
|
||||
complex_operator.cpp
|
||||
constraints.cpp
|
||||
densemat.cpp
|
||||
fdsolver.cpp
|
||||
symmat.cpp
|
||||
handle.cpp
|
||||
matrix.cpp
|
||||
@@ -39,6 +40,7 @@ list(APPEND HDRS
|
||||
dinvariants.hpp
|
||||
symmat.hpp
|
||||
dtensor.hpp
|
||||
fdsolver.hpp
|
||||
handle.hpp
|
||||
invariants.hpp
|
||||
kernels.hpp
|
||||
|
||||
+10
-8
@@ -208,11 +208,14 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
|
||||
" \"config_version\": 2, \n"
|
||||
" \"solver\": { \n"
|
||||
" \"solver\": \"AMG\", \n"
|
||||
" \"scope\": \"main\", \n"
|
||||
" \"smoother\": \"JACOBI_L1\", \n"
|
||||
" \"presweeps\": 1, \n"
|
||||
" \"postsweeps\": 1, \n"
|
||||
" \"interpolator\": \"D2\", \n"
|
||||
" \"max_iters\": 2, \n"
|
||||
" \"convergence\": \"ABSOLUTE\", \n"
|
||||
" \"max_row_sum\" : 0.9, \n"
|
||||
" \"strength_threshold\" : 0.25, \n"
|
||||
" \"postsweeps\": 1, \n"
|
||||
" \"max_iters\": 1, \n"
|
||||
" \"cycle\": \"V\"";
|
||||
if (verbose)
|
||||
{
|
||||
@@ -239,22 +242,21 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
|
||||
" \"solver\": \"AMG\", \n"
|
||||
" \"smoother\": { \n"
|
||||
" \"scope\": \"jacobi\", \n"
|
||||
" \"solver\": \"BLOCK_JACOBI\", \n"
|
||||
" \"relaxation_factor\": 0.7 \n"
|
||||
" \"solver\": \"JACOBI_L1\" \n"
|
||||
" }, \n"
|
||||
" \"presweeps\": 1, \n"
|
||||
" \"interpolator\": \"D2\", \n"
|
||||
" \"max_row_sum\" : 0.9, \n"
|
||||
" \"strength_threshold\" : 0.25, \n"
|
||||
" \"max_iters\": 2, \n"
|
||||
" \"max_iters\": 1, \n"
|
||||
" \"scope\": \"amg\", \n"
|
||||
" \"max_levels\": 100, \n"
|
||||
" \"cycle\": \"V\", \n"
|
||||
" \"postsweeps\": 1 \n"
|
||||
" }, \n"
|
||||
" \"solver\": \"PCG\", \n"
|
||||
" \"max_iters\": 100, \n"
|
||||
" \"convergence\": \"RELATIVE_MAX\", \n"
|
||||
" \"max_iters\": 150, \n"
|
||||
" \"convergence\": \"RELATIVE_INI_CORE\", \n"
|
||||
" \"scope\": \"main\", \n"
|
||||
" \"tolerance\": 1e-12, \n"
|
||||
" \"monitor_residual\": 1, \n"
|
||||
|
||||
+203
-10
@@ -50,6 +50,10 @@ dsyevr_(char *JOBZ, char *RANGE, char *UPLO, int *N, double *A, int *LDA,
|
||||
double *W, double *Z, int *LDZ, int *ISUPPZ, double *WORK, int *LWORK,
|
||||
int *IWORK, int *LIWORK, int *INFO);
|
||||
extern "C" void
|
||||
dgeev_(const char * jobvl, const char * jobvr, int *n, double * A, int * lda,
|
||||
double * wr, double * wl, double * vl, int * ldvl, double * vr, int * ldvr,
|
||||
double * work, int * lwork, int * info);
|
||||
extern "C" void
|
||||
dsyev_(char *JOBZ, char *UPLO, int *N, double *A, int *LDA, double *W,
|
||||
double *WORK, int *LWORK, int *INFO);
|
||||
extern "C" void
|
||||
@@ -2859,6 +2863,101 @@ void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt)
|
||||
}
|
||||
}
|
||||
|
||||
void KronProd(const DenseMatrix & A, const DenseMatrix & B, DenseMatrix & C)
|
||||
{
|
||||
const int ah = A.Height();
|
||||
const int aw = A.Width();
|
||||
const int bh = B.Height();
|
||||
const int bw = B.Width();
|
||||
|
||||
C.SetSize(ah*bh,aw*bw);
|
||||
const double * ad = A.Data();
|
||||
const double * bd = B.Data();
|
||||
double * cd = C.Data();
|
||||
|
||||
for (int ja = 0; ja<aw; ++ja)
|
||||
for (int jb = 0; jb<bw; ++jb)
|
||||
for (int ia = 0; ia<ah; ++ia)
|
||||
for (int ib = 0; ib<bh; ++ib)
|
||||
cd[bh*ia + ib + ah*bh*(bw*ja + jb)]
|
||||
= ad[ia + ja * ah] * bd[ib + jb*bh];
|
||||
}
|
||||
|
||||
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const Vector &r,
|
||||
Vector & z)
|
||||
{
|
||||
const int nA = A.Height();
|
||||
const int mA = A.Width();
|
||||
const int nB = B.Height();
|
||||
const int mB = B.Width();
|
||||
const int nr = r.Size();
|
||||
MFEM_VERIFY(nr == mA*mB, "Wrong size of Vector r");
|
||||
z.SetSize(nA*nB);
|
||||
DenseMatrix R(r.GetData(),mB,mA);
|
||||
DenseMatrix X(nB,mA);
|
||||
DenseMatrix Y(z.GetData(),nB,nA);
|
||||
Mult(B,R,X);
|
||||
MultABt(X,A,Y);
|
||||
}
|
||||
|
||||
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &R,
|
||||
DenseMatrix & Z)
|
||||
{
|
||||
const int nA = A.Height();
|
||||
const int nB = B.Height();
|
||||
const int nR = R.Height();
|
||||
const int mR = R.Width();
|
||||
Z.SetSize(nA*nB,mR);
|
||||
|
||||
Vector r,z;
|
||||
double * dataR = R.Data();
|
||||
for (int i = 0; i<mR; i++)
|
||||
{
|
||||
r.SetDataAndSize(&dataR[i*nR],nR);
|
||||
KronMult(A,B,r,z);
|
||||
Z.SetCol(i,z);
|
||||
}
|
||||
}
|
||||
|
||||
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &C,
|
||||
const Vector &r, Vector &z)
|
||||
{
|
||||
const int nA = A.Height();
|
||||
const int mA = A.Width();
|
||||
const int nB = B.Height();
|
||||
const int mB = B.Width();
|
||||
const int nC = C.Height();
|
||||
const int mC = C.Width();
|
||||
const int nr = r.Size();
|
||||
MFEM_VERIFY(nr == mA*mB*mC, "Wrong size of Vector r");
|
||||
z.SetSize(nA*nB*nC);
|
||||
|
||||
double * dataR = r.GetData();
|
||||
DenseMatrix R(dataR,mC,mA*mB);
|
||||
DenseMatrix X(nC,mA*mB);
|
||||
Mult(C,R,X);
|
||||
X.Transpose();
|
||||
DenseMatrix Z(z.GetData(),mA*mB,nC);
|
||||
KronMult(A,B,X,Z);
|
||||
Z.Transpose();
|
||||
}
|
||||
|
||||
void KronMult(const Array<DenseMatrix *> & A, const Vector & r, Vector & z)
|
||||
{
|
||||
int dim = A.Size();
|
||||
if (dim == 2)
|
||||
{
|
||||
KronMult(*A[0],*A[1],r,z);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
KronMult(*A[0],*A[1],*A[2], r,z);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("KronMult::Wrong dimension");
|
||||
}
|
||||
}
|
||||
|
||||
bool LUFactors::Factor(int m, double TOL)
|
||||
{
|
||||
@@ -3310,23 +3409,105 @@ DenseMatrixInverse::~DenseMatrixInverse()
|
||||
delete [] lu.ipiv;
|
||||
}
|
||||
|
||||
void KronMult(const DenseMatrixInverse &A, const DenseMatrixInverse &B,
|
||||
const Vector &r, Vector & z)
|
||||
{
|
||||
// A and B are square matrices
|
||||
z.SetSize(r.Size());
|
||||
int nA = A.Height();
|
||||
int nB = B.Height();
|
||||
DenseMatrix R(r.GetData(),nB,nA);
|
||||
DenseMatrix X(nB,nA);
|
||||
B.Mult(R,X);
|
||||
X.Transpose();
|
||||
DenseMatrix Y(z.GetData(),nA,nB);
|
||||
A.Mult(X,Y);
|
||||
Y.Transpose();
|
||||
}
|
||||
|
||||
DenseMatrixEigensystem::DenseMatrixEigensystem(DenseMatrix &m)
|
||||
void KronMult(const DenseMatrixInverse &A, const DenseMatrixInverse &B,
|
||||
const DenseMatrix &R, DenseMatrix & Z)
|
||||
{
|
||||
// A and B are square matrices
|
||||
int nR = R.Height();
|
||||
int mR = R.Width();
|
||||
Z.SetSize(nR,mR);
|
||||
Vector r(nR);
|
||||
Vector z(nR);
|
||||
double * dataR = R.GetData();
|
||||
double * dataZ = Z.GetData();
|
||||
for (int i = 0; i<mR; i++)
|
||||
{
|
||||
r.SetData(&dataR[i*nR]);
|
||||
z.SetData(&dataZ[i*nR]);
|
||||
KronMult(A,B,r,z);
|
||||
}
|
||||
}
|
||||
|
||||
void KronMult(const DenseMatrixInverse &A, const DenseMatrixInverse &B,
|
||||
const DenseMatrixInverse &C, const Vector &r, Vector & z)
|
||||
{
|
||||
// A, B and C are square matrices
|
||||
int n = r.Size();
|
||||
z.SetSize(n);
|
||||
int nA = A.Height();
|
||||
int nB = B.Height();
|
||||
int nC = C.Height();
|
||||
double * dataR = r.GetData();
|
||||
DenseMatrix R(dataR,nC,nA*nB);
|
||||
DenseMatrix X(nC,nA*nB);
|
||||
C.Mult(R,X);
|
||||
X.Transpose();
|
||||
DenseMatrix Z(z.GetData(),nC,nA*nB);
|
||||
KronMult(A,B,X,Z);
|
||||
Z.Transpose();
|
||||
}
|
||||
|
||||
void KronMult(const Array<DenseMatrixInverse *> & A, const Vector & r,
|
||||
Vector & z)
|
||||
{
|
||||
int dim = A.Size();
|
||||
if (dim == 2)
|
||||
{
|
||||
KronMult(*A[0],*A[1],r,z);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
KronMult(*A[0],*A[1],*A[2], r,z);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("KronMult::Wrong dimension");
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrixEigensystem::DenseMatrixEigensystem(DenseMatrix &m, bool sym_)
|
||||
: mat(m)
|
||||
{
|
||||
n = mat.Width();
|
||||
EVal.SetSize(n);
|
||||
EVali.SetSize(n);
|
||||
EVect.SetSize(n);
|
||||
ev.SetDataAndSize(NULL, n);
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
sym = sym_;
|
||||
jobz = 'V';
|
||||
uplo = 'U';
|
||||
lwork = -1;
|
||||
double qwork;
|
||||
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
|
||||
&qwork, &lwork, &info);
|
||||
|
||||
if (sym)
|
||||
{
|
||||
uplo = 'U';
|
||||
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
|
||||
&qwork, &lwork, &info);
|
||||
}
|
||||
else
|
||||
{
|
||||
char jobvl = 'N';
|
||||
int ldvl = 1;
|
||||
dgeev_(&jobvl,&jobz,&n, mat.GetData(), &n, EVal.GetData(), EVali.GetData(),
|
||||
nullptr, &ldvl, EVect.GetData(), &n, &qwork, &lwork, &info);
|
||||
}
|
||||
lwork = (int) qwork;
|
||||
work = new double[lwork];
|
||||
#endif
|
||||
@@ -3338,6 +3519,7 @@ DenseMatrixEigensystem::DenseMatrixEigensystem(
|
||||
n(other.n)
|
||||
{
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
sym = other.sym;
|
||||
jobz = other.jobz;
|
||||
uplo = other.uplo;
|
||||
lwork = other.lwork;
|
||||
@@ -3356,13 +3538,24 @@ void DenseMatrixEigensystem::Eval()
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
EVect = mat;
|
||||
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
|
||||
work, &lwork, &info);
|
||||
|
||||
if (sym)
|
||||
{
|
||||
EVect = mat;
|
||||
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
|
||||
work, &lwork, &info);
|
||||
}
|
||||
else
|
||||
{
|
||||
char jobvl = 'N';
|
||||
int ldvl = 1;
|
||||
DenseMatrix T = mat; // mat is overwritten by dgeev
|
||||
dgeev_(&jobvl,&jobz,&n, T.GetData(), &n, EVal.GetData(), EVali.GetData(),
|
||||
nullptr, &ldvl, EVect.GetData(), &n, work, &lwork, &info);
|
||||
}
|
||||
if (info != 0)
|
||||
{
|
||||
mfem::err << "DenseMatrixEigensystem::Eval(): DSYEV error code: "
|
||||
string lpck = (sym) ? "DSYEV" : "DGEEV";
|
||||
mfem::err << "DenseMatrixEigensystem::Eval(): " << lpck << "error code: "
|
||||
<< info << endl;
|
||||
mfem_error();
|
||||
}
|
||||
|
||||
+35
-2
@@ -523,6 +523,22 @@ void AddMult_a_VWt(const double a, const Vector &v, const Vector &w,
|
||||
/// VVt += a * v v^t
|
||||
void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt);
|
||||
|
||||
/// C = A ⊗ B
|
||||
void KronProd(const DenseMatrix & A, const DenseMatrix & B, DenseMatrix & C);
|
||||
|
||||
/// z = (A ⊗ B) r = vec(B R A^T), where R := vec^-1 (r)
|
||||
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const Vector &r,
|
||||
Vector & z);
|
||||
|
||||
/// z = (A ⊗ B) R
|
||||
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &R,
|
||||
DenseMatrix & Z);
|
||||
|
||||
/// z = ( A ⊗ B ⊗ C ) r
|
||||
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &C,
|
||||
const Vector &r, Vector & z);
|
||||
|
||||
void KronMult(const Array<DenseMatrix *> & A, const Vector & r, Vector & z);
|
||||
|
||||
/** Class that can compute LU factorization of external data and perform various
|
||||
operations with the factored data. */
|
||||
@@ -685,16 +701,33 @@ public:
|
||||
virtual ~DenseMatrixInverse();
|
||||
};
|
||||
|
||||
/// z = (A^-1 ⊗ B^-1) r = vec(B^-1 R A^-T), where R := vec^-1 (r)
|
||||
void KronMult(const DenseMatrixInverse &Ainv, const DenseMatrixInverse &Binv,
|
||||
const Vector &r, Vector & z);
|
||||
|
||||
/// z = (A^-1 ⊗ B^-1) R
|
||||
void KronMult(const DenseMatrixInverse &Ainv, const DenseMatrixInverse &Binv,
|
||||
const DenseMatrix &R, DenseMatrix & Z);
|
||||
|
||||
/// z = ( A^-1 ⊗ B^-1 ⊗ C^-1 ) r
|
||||
void KronMult(const DenseMatrixInverse &Ainv, const DenseMatrixInverse &Binv,
|
||||
const DenseMatrixInverse &Cinv, const Vector &r, Vector & z);
|
||||
|
||||
void KronMult(const Array<DenseMatrixInverse *> & A, const Vector & r,
|
||||
Vector & z);
|
||||
|
||||
class DenseMatrixEigensystem
|
||||
{
|
||||
DenseMatrix &mat;
|
||||
Vector EVal;
|
||||
// Possible non zero imaginary part of Eigenvalues
|
||||
Vector EVali;
|
||||
DenseMatrix EVect;
|
||||
Vector ev;
|
||||
int n;
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
bool sym;
|
||||
double *work;
|
||||
char jobz, uplo;
|
||||
int lwork, info;
|
||||
@@ -702,10 +735,10 @@ class DenseMatrixEigensystem
|
||||
|
||||
public:
|
||||
|
||||
DenseMatrixEigensystem(DenseMatrix &m);
|
||||
DenseMatrixEigensystem(DenseMatrix &m, bool sym_ = false);
|
||||
DenseMatrixEigensystem(const DenseMatrixEigensystem &other);
|
||||
void Eval();
|
||||
Vector &Eigenvalues() { return EVal; }
|
||||
Vector &Eigenvalues(bool imag = false) { return imag ? EVali : EVal; }
|
||||
DenseMatrix &Eigenvectors() { return EVect; }
|
||||
double Eigenvalue(int i) { return EVal(i); }
|
||||
const Vector &Eigenvector(int i)
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2010-2021, 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 "linalg.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void KronProdInvDiag(const Vector & a, const Vector & b, Vector & dinv)
|
||||
{
|
||||
int n = a.Size(), m = b.Size();
|
||||
dinv.SetSize(n*m);
|
||||
|
||||
for (int j = 0; j<m; j++)
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
dinv(i*m+j) = 1./(a(i) + b(j));
|
||||
}
|
||||
}
|
||||
|
||||
void KronProdInvDiag(const Vector & a, const Vector & b,
|
||||
const Vector & c, Vector & dinv)
|
||||
{
|
||||
int n = a.Size(), m = b.Size(), l = c.Size();
|
||||
dinv.SetSize(n*m*l);
|
||||
|
||||
for (int k = 0; k<l; k++)
|
||||
for (int j = 0; j<m; j++)
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
dinv(i*m*l+j*l+k) = 1./(a(i) + b(j) + c(k));
|
||||
}
|
||||
}
|
||||
|
||||
void KronProdInvDiag(const Array<Vector *> & X, Vector & dinv)
|
||||
{
|
||||
int dim = X.Size();
|
||||
if (dim == 1)
|
||||
{
|
||||
int n = X[0]->Size();
|
||||
dinv.SetSize(n);
|
||||
for (int i = 0; i<n; i++) { dinv(i) = 1./(*X[0])(i); }
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
KronProdInvDiag(*X[0], *X[1], dinv);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
KronProdInvDiag(*X[0], *X[1], *X[2], dinv);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("KronProdInvDiag::Wrong dimension");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
FDSolver::FDSolver(const Array<DenseMatrix *> & A,
|
||||
const Array<DenseMatrix *> & B)
|
||||
{
|
||||
MFEM_ASSERT(A.Size() == B.Size(), "DenseFDSolver: Incompatible Dimensions");
|
||||
dim = A.Size();
|
||||
|
||||
int solver_size = 1;
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
MFEM_ASSERT(A[i]->Height() == A[i]->Width(),
|
||||
"DenseFDSolver: Matrix is not square");
|
||||
MFEM_ASSERT(B[i]->Height() == B[i]->Width(),
|
||||
"DenseFDSolver: Matrix is not square");
|
||||
MFEM_ASSERT(A[i]->Height() == B[i]->Height(),
|
||||
"DenseFDSolver: Matrices A and B have incompatible size");
|
||||
solver_size *= A[i]->Height();
|
||||
}
|
||||
this->height = solver_size;
|
||||
this->width = solver_size;
|
||||
if (solver_size) { Setup(A,B); }
|
||||
}
|
||||
|
||||
void FDSolver::Setup(const Array<DenseMatrix *> & A,
|
||||
const Array<DenseMatrix *> & B)
|
||||
{
|
||||
EigSystem.SetSize(dim);
|
||||
eigv.SetSize(dim);
|
||||
Array<Vector *> evalues(dim);
|
||||
SQ.SetSize(dim);
|
||||
DenseMatrix D;
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
DenseMatrixInverse Minv(*B[i]);
|
||||
Minv.Mult(*A[i],D);
|
||||
EigSystem[i] = new DenseMatrixEigensystem(D);
|
||||
EigSystem[i]->Eval();
|
||||
evalues[i] = &EigSystem[i]->Eigenvalues();
|
||||
eigv[i] = &EigSystem[i]->Eigenvectors();
|
||||
DenseMatrixInverse Qinv(*eigv[i]);
|
||||
DenseMatrix Sdinv;
|
||||
Minv.GetInverseMatrix(Sdinv);
|
||||
SQ[i] = new DenseMatrix;
|
||||
Qinv.Mult(Sdinv,*SQ[i]);
|
||||
}
|
||||
KronProdInvDiag(evalues,dinv);
|
||||
}
|
||||
|
||||
|
||||
void FDSolver::Mult(const Vector & r,Vector & z) const
|
||||
{
|
||||
MFEM_ASSERT(height == r.Size(),
|
||||
"DenseFDSolver::Mult: Inconsistent vector size");
|
||||
if (r.Size() == 0) { return; }
|
||||
Vector rtemp;
|
||||
KronMult(SQ,r,rtemp);
|
||||
// 2. Diagonal solve;
|
||||
rtemp *= dinv;
|
||||
// 3. Modify RHS; z <-- (Q1 x Q2) rtemp
|
||||
KronMult(eigv,rtemp,z);
|
||||
}
|
||||
|
||||
FDSolver::~FDSolver()
|
||||
{
|
||||
if (height)
|
||||
{
|
||||
for (int i=0; i<dim; i++)
|
||||
{
|
||||
delete SQ[i];
|
||||
delete EigSystem[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_LAPACK
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_FDSOLVER
|
||||
#define MFEM_FDSOLVER
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "densemat.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Computes the inverse diagonal dinv = (a⊗I + I⊗b)^-1
|
||||
/// where a, b are diagonal matrices and I is the identity of the
|
||||
/// appropriate size
|
||||
void KronProdInvDiag(const Vector & a, const Vector & b, Vector & dinv);
|
||||
|
||||
/// Computes the inverse diagonal dinv = (a⊗I⊗I + I⊗b⊗I + I⊗I⊗c)^-1
|
||||
/// where a, b, c are diagonal matrices and I is the identity of the
|
||||
/// appropriate size
|
||||
void KronProdInvDiag(const Vector & a, const Vector & b,
|
||||
const Vector & c, Vector & dinv);
|
||||
|
||||
void KronProdInvDiag(const Array<Vector *> & X, Vector & dinv);
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
/// In 2D it solves the system (A_0 ⊗ B_1 + B_0 ⊗ A_1) z = r
|
||||
/// In 3D it solves the system
|
||||
/// (A_0 ⊗ B_1 ⊗ B_2 + B_0 ⊗ A_1 ⊗ B_2 + B_0 ⊗ B_1 ⊗ A_2) z = r
|
||||
class FDSolver: public Solver
|
||||
{
|
||||
private:
|
||||
int dim = 2;
|
||||
Array<DenseMatrixEigensystem *> EigSystem;
|
||||
Array<DenseMatrix *> eigv; // eigenvectors
|
||||
Array<DenseMatrix *> SQ;
|
||||
mutable Vector dinv;
|
||||
void Setup(const Array<DenseMatrix *> & A, const Array<DenseMatrix *> & B);
|
||||
public:
|
||||
FDSolver(const Array<DenseMatrix *> & A, const Array<DenseMatrix *> & B);
|
||||
virtual void SetOperator(const Operator &op) {}
|
||||
virtual void Mult(const Vector &r, Vector &z) const;
|
||||
virtual ~FDSolver();
|
||||
};
|
||||
|
||||
#endif // MFEM_USE_LAPACK
|
||||
|
||||
} // mfem name space
|
||||
|
||||
|
||||
#endif // MFEM_FDSOLVER
|
||||
+8
-1
@@ -516,7 +516,14 @@ public:
|
||||
|
||||
/// Initialize all entries with value.
|
||||
HypreParMatrix &operator=(double value)
|
||||
{ internal::hypre_ParCSRMatrixSetConstantValues(A, value); return *this; }
|
||||
{
|
||||
#if MFEM_HYPRE_VERSION < 22200
|
||||
internal::hypre_ParCSRMatrixSetConstantValues(A, value);
|
||||
#else
|
||||
hypre_ParCSRMatrixSetConstantValues(A, value);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Perform the operation `*this += B`, assuming that both matrices use the
|
||||
same row and column partitions and the same col_map_offd arrays, or B has
|
||||
|
||||
@@ -1942,8 +1942,8 @@ HYPRE_Int
|
||||
hypre_ParCSRMatrixSetConstantValues(hypre_ParCSRMatrix *A,
|
||||
HYPRE_Complex value)
|
||||
{
|
||||
hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixDiag(A), value);
|
||||
hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixOffd(A), value);
|
||||
internal::hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixDiag(A), value);
|
||||
internal::hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixOffd(A), value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -198,6 +198,16 @@ hypre_CSRMatrixSum(hypre_CSRMatrix *A,
|
||||
HYPRE_Complex beta,
|
||||
hypre_CSRMatrix *B);
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 22200
|
||||
/** Provide an overloaded function for code consistency between HYPRE API
|
||||
versions. */
|
||||
inline hypre_CSRMatrix *hypre_CSRMatrixAdd(hypre_CSRMatrix *A,
|
||||
hypre_CSRMatrix *B)
|
||||
{
|
||||
return ::hypre_CSRMatrixAdd(1.0, A, 1.0, B);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Return a new matrix containing the sum of A and B, assuming that both
|
||||
matrices use the same row and column partitions. The col_map_offd do not
|
||||
need to be the same, but a more efficient algorithm is used if that's the
|
||||
|
||||
+14
-1
@@ -276,6 +276,20 @@ void Add(const int height, const int width, const TA *Adata, TB *Bdata)
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Compute B +=alpha*A, where the matrices A and B are of size
|
||||
@a height x @a width with data @a Adata and @a Bdata. */
|
||||
template<typename TA, typename TB>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void Add(const int height, const int width,
|
||||
const double alpha, const TA *Adata, TB *Bdata)
|
||||
{
|
||||
const int m = height * width;
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
Bdata[i] += alpha * Adata[i];
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Compute B = alpha*A, where the matrices A and B are of size
|
||||
@a height x @a width with data @a Adata and @a Bdata. */
|
||||
template<typename TA, typename TB>
|
||||
@@ -290,7 +304,6 @@ void Set(const int height, const int width,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @brief Matrix-matrix multiplication: A = B * C, where the matrices A, B and
|
||||
C are of sizes @a Aheight x @a Awidth, @a Aheight x @a Bwidth and @a Bwidth
|
||||
x @a Awidth, respectively. */
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "invariants.hpp"
|
||||
#include "constraints.hpp"
|
||||
#include "auxiliary.hpp"
|
||||
#include "fdsolver.hpp"
|
||||
|
||||
#ifdef MFEM_USE_AMGX
|
||||
#include "amgxsolver.hpp"
|
||||
|
||||
+90
-21
@@ -232,7 +232,7 @@ void OperatorJacobiSmoother::Mult(const Vector &x, Vector &y) const
|
||||
MFEM_FORALL(i, height, Y[i] += DI[i] * R[i]; );
|
||||
}
|
||||
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
|
||||
const Vector &d,
|
||||
const Array<int>& ess_tdofs,
|
||||
int order_, double max_eig_estimate_)
|
||||
@@ -242,19 +242,19 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
|
||||
max_eig_estimate(max_eig_estimate_),
|
||||
N(d.Size()),
|
||||
dinv(N),
|
||||
diag(d),
|
||||
diag(&d),
|
||||
coeffs(order),
|
||||
ess_tdof_list(ess_tdofs),
|
||||
ess_tdof_list(&ess_tdofs),
|
||||
residual(N),
|
||||
oper(oper_) { Setup(); }
|
||||
oper(&oper_) { Setup(); }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
|
||||
const Vector &d,
|
||||
const Array<int>& ess_tdofs,
|
||||
int order_, MPI_Comm comm, int power_iterations, double power_tolerance)
|
||||
#else
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
|
||||
const Vector &d,
|
||||
const Array<int>& ess_tdofs,
|
||||
int order_, int power_iterations, double power_tolerance)
|
||||
@@ -263,13 +263,13 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
|
||||
order(order_),
|
||||
N(d.Size()),
|
||||
dinv(N),
|
||||
diag(d),
|
||||
diag(&d),
|
||||
coeffs(order),
|
||||
ess_tdof_list(ess_tdofs),
|
||||
ess_tdof_list(&ess_tdofs),
|
||||
residual(N),
|
||||
oper(oper_)
|
||||
oper(&oper_)
|
||||
{
|
||||
OperatorJacobiSmoother invDiagOperator(diag, ess_tdofs, 1.0);
|
||||
OperatorJacobiSmoother invDiagOperator(*diag, ess_tdofs, 1.0);
|
||||
ProductOperator diagPrecond(&invDiagOperator, oper, false, false);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
@@ -284,16 +284,74 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
|
||||
Setup();
|
||||
}
|
||||
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
|
||||
const Vector &d,
|
||||
const Array<int>& ess_tdofs,
|
||||
int order_, double max_eig_estimate_)
|
||||
: OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, max_eig_estimate_) { }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
|
||||
const Vector &d,
|
||||
const Array<int>& ess_tdofs,
|
||||
int order_, MPI_Comm comm, int power_iterations, double power_tolerance)
|
||||
: OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, comm,
|
||||
power_iterations, power_tolerance) { }
|
||||
#else
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
|
||||
const Vector &d,
|
||||
const Array<int>& ess_tdofs,
|
||||
int order_, int power_iterations, double power_tolerance)
|
||||
: OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, power_iterations,
|
||||
power_tolerance) { }
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
|
||||
const Solver &prec_,
|
||||
int order_, MPI_Comm comm,
|
||||
int power_iterations, double power_tolerance)
|
||||
#else
|
||||
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
|
||||
const Solver &prec_,
|
||||
int order_, int power_iterations, double power_tolerance)
|
||||
#endif
|
||||
: Solver(oper_.Height()),
|
||||
order(order_),
|
||||
diag(nullptr),
|
||||
N(oper_.Height()),
|
||||
coeffs(order),
|
||||
ess_tdof_list(nullptr),
|
||||
residual(N),
|
||||
oper(&oper_),
|
||||
prec(&prec_)
|
||||
{
|
||||
ProductOperator Precond(prec, oper, false, false);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
PowerMethod powerMethod(comm);
|
||||
#else
|
||||
PowerMethod powerMethod;
|
||||
#endif
|
||||
Vector ev(oper->Width());
|
||||
max_eig_estimate = powerMethod.EstimateLargestEigenvalue(Precond, ev,
|
||||
power_iterations, power_tolerance);
|
||||
Setup();
|
||||
}
|
||||
|
||||
|
||||
void OperatorChebyshevSmoother::Setup()
|
||||
{
|
||||
// Invert diagonal
|
||||
residual.UseDevice(true);
|
||||
auto D = diag.Read();
|
||||
auto X = dinv.Write();
|
||||
MFEM_FORALL(i, N, X[i] = 1.0 / D[i]; );
|
||||
auto I = ess_tdof_list.Read();
|
||||
MFEM_FORALL(i, ess_tdof_list.Size(), X[I[i]] = 1.0; );
|
||||
|
||||
// Invert diagonal
|
||||
if (diag)
|
||||
{
|
||||
auto D = diag->Read();
|
||||
auto X = dinv.Write();
|
||||
auto I = ess_tdof_list->Read();
|
||||
MFEM_FORALL(i, N, X[i] = 1.0 / D[i]; );
|
||||
MFEM_FORALL(i, ess_tdof_list->Size(), X[I[i]] = 1.0; );
|
||||
}
|
||||
// Set up Chebyshev coefficients
|
||||
// For reference, see e.g., Parallel multigrid smoothing: polynomial versus
|
||||
// Gauss-Seidel by Adams et al.
|
||||
@@ -388,11 +446,22 @@ void OperatorChebyshevSmoother::Mult(const Vector& x, Vector &y) const
|
||||
residual = helperVector;
|
||||
}
|
||||
|
||||
// Scale residual by inverse diagonal
|
||||
// Scale residual by inverse diagonal or apply the given preconditioner
|
||||
const int n = N;
|
||||
auto Dinv = dinv.Read();
|
||||
|
||||
auto R = residual.ReadWrite();
|
||||
MFEM_FORALL(i, n, R[i] *= Dinv[i]; );
|
||||
if (prec)
|
||||
{
|
||||
// No device yet
|
||||
Vector z(residual.Size()); z = 0.0;
|
||||
prec->Mult(residual,z);
|
||||
residual = z;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto Dinv = dinv.Read();
|
||||
MFEM_FORALL(i, n, R[i] *= Dinv[i]; );
|
||||
}
|
||||
|
||||
// Add weighted contribution to y
|
||||
auto Y = y.ReadWrite();
|
||||
@@ -2533,7 +2602,7 @@ BlockILU::BlockILU(int block_size_,
|
||||
reordering(reordering_)
|
||||
{ }
|
||||
|
||||
BlockILU::BlockILU(Operator &op,
|
||||
BlockILU::BlockILU(const Operator &op,
|
||||
int block_size_,
|
||||
Reordering reordering_,
|
||||
int k_fill_)
|
||||
|
||||
+41
-6
@@ -208,7 +208,13 @@ public:
|
||||
the matrix-free setting. The estimated largest eigenvalue of the
|
||||
diagonally preconditoned operator must be provided via
|
||||
max_eig_estimate. */
|
||||
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
|
||||
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d,
|
||||
const Array<int>& ess_tdof_list,
|
||||
int order, double max_eig_estimate);
|
||||
|
||||
/// Deprecated: see pass-by-reference version above
|
||||
MFEM_DEPRECATED
|
||||
OperatorChebyshevSmoother(const Operator* oper_, const Vector &d,
|
||||
const Array<int>& ess_tdof_list,
|
||||
int order, double max_eig_estimate);
|
||||
|
||||
@@ -220,13 +226,41 @@ public:
|
||||
accuracy of the estimated eigenvalue may be controlled via
|
||||
power_iterations and power_tolerance. */
|
||||
#ifdef MFEM_USE_MPI
|
||||
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
|
||||
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d,
|
||||
const Array<int>& ess_tdof_list,
|
||||
int order, MPI_Comm comm = MPI_COMM_NULL,
|
||||
int power_iterations = 10,
|
||||
double power_tolerance = 1e-8);
|
||||
|
||||
/// Deprecated: see pass-by-reference version above
|
||||
MFEM_DEPRECATED
|
||||
OperatorChebyshevSmoother(const Operator* oper_, const Vector &d,
|
||||
const Array<int>& ess_tdof_list,
|
||||
int order, MPI_Comm comm = MPI_COMM_NULL,
|
||||
int power_iterations = 10,
|
||||
double power_tolerance = 1e-8);
|
||||
|
||||
/** Chebyshev accelaration for the given preconditioner @a prec.
|
||||
The largest eigenvalue of the preconditoned operator
|
||||
is estimated internally via a power method. The
|
||||
accuracy of the estimated eigenvalue may be controlled via
|
||||
power_iterations and power_tolerance. */
|
||||
OperatorChebyshevSmoother(const Operator &oper_, const Solver &prec,
|
||||
int order, MPI_Comm comm = MPI_COMM_NULL,
|
||||
int power_iterations = 10,
|
||||
double power_tolerance = 1e-8);
|
||||
#else
|
||||
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
|
||||
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d,
|
||||
const Array<int>& ess_tdof_list,
|
||||
int order, int power_iterations = 10,
|
||||
double power_tolerance = 1e-8);
|
||||
|
||||
OperatorChebyshevSmoother(const Operator &oper_, const Solver &prec,
|
||||
int order, int power_iterations = 10,
|
||||
double power_tolerance = 1e-8);
|
||||
/// Deprecated: see pass-by-reference version above
|
||||
MFEM_DEPRECATED
|
||||
OperatorChebyshevSmoother(const Operator* oper_, const Vector &d,
|
||||
const Array<int>& ess_tdof_list,
|
||||
int order, int power_iterations = 10,
|
||||
double power_tolerance = 1e-8);
|
||||
@@ -250,12 +284,13 @@ private:
|
||||
double max_eig_estimate;
|
||||
const int N;
|
||||
Vector dinv;
|
||||
const Vector &diag;
|
||||
const Vector * diag = nullptr;
|
||||
Array<double> coeffs;
|
||||
const Array<int>& ess_tdof_list;
|
||||
const Array<int> * ess_tdof_list = nullptr;
|
||||
mutable Vector residual;
|
||||
mutable Vector helperVector;
|
||||
const Operator* oper;
|
||||
const Solver* prec=nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -731,7 +766,7 @@ public:
|
||||
* case that @a op is a HypreParMatrix, the ILU factorization is performed
|
||||
* on the diagonal blocks of the parallel decomposition.
|
||||
*/
|
||||
BlockILU(Operator &op, int block_size_ = 1,
|
||||
BlockILU(const Operator &op, int block_size_ = 1,
|
||||
Reordering reordering_ = Reordering::MINIMUM_DISCARDED_FILL,
|
||||
int k_fill_ = 0);
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ MFEM makefile targets:
|
||||
make distclean
|
||||
make style
|
||||
make tags
|
||||
make hooks
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -96,6 +97,8 @@ make style
|
||||
make tags
|
||||
Generate a vi or Emacs compatible TAGS file in ${MFEM_DIR}/TAGS. Requires
|
||||
functional "etags" and "egrep" in the user ${PATH}.
|
||||
make hooks
|
||||
Creates symlinks to the hooks in the `.git/hooks` directory.
|
||||
endef
|
||||
|
||||
# Save the MAKEOVERRIDES for cases where we explicitly want to pass the command
|
||||
@@ -759,6 +762,15 @@ endif
|
||||
@cd $(MFEM_REAL_DIR) && $(ETAGS_BIN) --class-qualify \
|
||||
--declarations -o $(MFEM_REAL_DIR)/TAGS $(MFEM_TRACKED_SOURCE)
|
||||
|
||||
# Creates symlinks to the hooks in the `.git/hooks` directory. Individual
|
||||
# hooks can be enabled by manually creating symlinks. Hooks can be customized
|
||||
# using hard copies (trading off with automated updates).
|
||||
.PHONY: hooks
|
||||
hooks:
|
||||
@cd $(MFEM_DIR)/.git/hooks && \
|
||||
ln -s ../../config/githooks/pre-commit pre-commit; \
|
||||
ln -s ../../config/githooks/pre-push pre-push;
|
||||
|
||||
# Print the contents of a makefile variable, e.g.: 'make print-MFEM_LIBS'.
|
||||
print-%:
|
||||
$(info [ variable name]: $*)
|
||||
|
||||
+4
-4
@@ -11478,10 +11478,10 @@ FaceGeometricFactors::FaceGeometricFactors(const Mesh *mesh,
|
||||
const int NF = fespace->GetNFbyType(type);
|
||||
const int NQ = ir.GetNPoints();
|
||||
|
||||
const Operator *face_restr = fespace->GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
type,
|
||||
L2FaceValues::SingleValued );
|
||||
const FaceRestriction *face_restr = fespace->GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
type,
|
||||
L2FaceValues::SingleValued );
|
||||
Vector Fnodes(face_restr->Height());
|
||||
face_restr->Mult(*nodes, Fnodes);
|
||||
|
||||
|
||||
+64
-35
@@ -691,16 +691,31 @@ struct BufferReader : BufferReaderBase
|
||||
BufferReader(bool compressed_, HeaderType header_type_)
|
||||
: compressed(compressed_), header_type(header_type_) { }
|
||||
|
||||
/// Return the number of bytes in the header. The header consists of one
|
||||
/// integer if the data is uncompressed, and four integers if the data is
|
||||
/// compressed. The integers are either 32 or 64 bytes depending on the value
|
||||
/// of @a header_type.
|
||||
int NumHeaderBytes() const
|
||||
/// Return the number of bytes of each header entry.
|
||||
size_t HeaderEntrySize() const
|
||||
{
|
||||
int num_entries = compressed ? 4 : 1;
|
||||
int entry_size = (header_type == UINT64_HEADER)
|
||||
? sizeof(uint64_t) : sizeof(uint32_t);
|
||||
return num_entries*entry_size;
|
||||
return header_type == UINT64_HEADER ? sizeof(uint64_t) : sizeof(uint32_t);
|
||||
}
|
||||
|
||||
/// Return the value of the header entry pointer to by @a header_buf. The
|
||||
/// value is stored as either uint32_t or uint64_t, according to the @a
|
||||
/// header_type, and is returned as uint64_t.
|
||||
uint64_t ReadHeaderEntry(const char *header_buf) const
|
||||
{
|
||||
return (header_type == UINT64_HEADER) ? bin_io::read<uint64_t>(header_buf)
|
||||
: bin_io::read<uint32_t>(header_buf);
|
||||
}
|
||||
|
||||
/// Return the number of bytes in the header. The header consists of one
|
||||
/// integer if the data is uncompressed, and @a N + 3 integers if the data is
|
||||
/// compressed, where @a N is the number of blocks. The integers are either
|
||||
/// 32 or 64 bytes depending on the value of @a header_type. The number of
|
||||
/// blocks is determined by reading the first integer (of type @a
|
||||
/// header_type) pointed to by @a header_buf.
|
||||
int NumHeaderBytes(const char *header_buf) const
|
||||
{
|
||||
if (!compressed) { return HeaderEntrySize(); }
|
||||
return (3 + ReadHeaderEntry(header_buf))*HeaderEntrySize();
|
||||
}
|
||||
|
||||
/// Read @a n elements of type @a F from the source buffer @a buf into the
|
||||
@@ -711,32 +726,42 @@ struct BufferReader : BufferReaderBase
|
||||
void ReadBinaryWithHeader(const char *header_buf, const char *buf,
|
||||
void *dest_void, int n) const
|
||||
{
|
||||
std::vector<unsigned char> uncompressed_data;
|
||||
std::vector<char> uncompressed_data;
|
||||
T *dest = static_cast<T*>(dest_void);
|
||||
|
||||
if (compressed)
|
||||
{
|
||||
#ifdef MFEM_USE_ZLIB
|
||||
uint64_t header[4];
|
||||
if (header_type == UINT32_HEADER)
|
||||
// The header has format (where header_t is uint32_t or uint64_t):
|
||||
// header_t number_of_blocks;
|
||||
// header_t uncompressed_block_size;
|
||||
// header_t uncompressed_last_block_size;
|
||||
// header_t compressed_size[number_of_blocks];
|
||||
int header_entry_size = HeaderEntrySize();
|
||||
int nblocks = ReadHeaderEntry(header_buf);
|
||||
header_buf += header_entry_size;
|
||||
std::vector<int> header(nblocks + 2);
|
||||
for (int i=0; i<nblocks+2; ++i)
|
||||
{
|
||||
uint32_t *header_32 = (uint32_t *)header_buf;
|
||||
for (int i=0; i<4; ++i) { header[i] = header_32[i]; }
|
||||
header[i] = ReadHeaderEntry(header_buf);
|
||||
header_buf += header_entry_size;
|
||||
}
|
||||
else
|
||||
uncompressed_data.resize((nblocks-1)*header[0] + header[1]);
|
||||
Bytef *dest_ptr = (Bytef *)uncompressed_data.data();
|
||||
Bytef *dest_start = dest_ptr;
|
||||
const Bytef *source_ptr = (const Bytef *)buf;
|
||||
for (int i=0; i<nblocks; ++i)
|
||||
{
|
||||
uint64_t *header_64 = (uint64_t *)header_buf;
|
||||
for (int i=0; i<4; ++i) { header[i] = header_64[i]; }
|
||||
uLongf source_len = header[i+2];
|
||||
uLong dest_len = (i == nblocks-1) ? header[1] : header[0];
|
||||
int res = uncompress(dest_ptr, &dest_len, source_ptr, source_len);
|
||||
MFEM_VERIFY(res == Z_OK, "Error uncompressing");
|
||||
dest_ptr += dest_len;
|
||||
source_ptr += source_len;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(header[0] == 1, "Multiple compressed blocks not supported");
|
||||
uLongf dest_len = header[1];
|
||||
uncompressed_data.resize(dest_len);
|
||||
int res = uncompress(uncompressed_data.data(), &dest_len,
|
||||
(const Bytef *)buf, header[3]);
|
||||
MFEM_VERIFY(res == Z_OK, "Error uncompressing");
|
||||
MFEM_VERIFY(sizeof(F)*n == dest_len, "AppendedData: wrong data size");
|
||||
buf = (const char *)uncompressed_data.data();
|
||||
MFEM_VERIFY(int(sizeof(F)*n) == (dest_ptr - dest_start),
|
||||
"AppendedData: wrong data size");
|
||||
buf = uncompressed_data.data();
|
||||
#else
|
||||
MFEM_ABORT("MFEM must be compiled with zlib enabled to uncompress.")
|
||||
#endif
|
||||
@@ -779,7 +804,7 @@ struct BufferReader : BufferReaderBase
|
||||
/// buffer @a dest. The input buffer contains both the header and the data.
|
||||
void ReadBinary(const char *buf, void *dest, int n) const override
|
||||
{
|
||||
ReadBinaryWithHeader(buf, buf + NumHeaderBytes(), dest, n);
|
||||
ReadBinaryWithHeader(buf, buf + NumHeaderBytes(buf), dest, n);
|
||||
}
|
||||
|
||||
/// Read @a n elements of type @a F from base-64 encoded source buffer into
|
||||
@@ -796,21 +821,25 @@ struct BufferReader : BufferReaderBase
|
||||
}
|
||||
if (compressed)
|
||||
{
|
||||
std::vector<unsigned char> data, header;
|
||||
// Decode the first entry of the header, which we need to determine
|
||||
// how long the rest of the header is.
|
||||
std::vector<char> nblocks_buf;
|
||||
int nblocks_b64 = bin_io::NumBase64Chars(HeaderEntrySize());
|
||||
bin_io::DecodeBase64(txt, nblocks_b64, nblocks_buf);
|
||||
std::vector<char> data, header;
|
||||
// Compute number of characters needed to encode header in base 64,
|
||||
// then round to nearest multiple of 4 to take padding into account.
|
||||
int b64_header = ((4*NumHeaderBytes()/3) + 3) & ~3;
|
||||
int header_b64 = bin_io::NumBase64Chars(NumHeaderBytes(nblocks_buf.data()));
|
||||
// If data is compressed, header is encoded separately
|
||||
bin_io::DecodeBase64(txt, b64_header, header);
|
||||
bin_io::DecodeBase64(txt + b64_header, strlen(txt)-b64_header, data);
|
||||
ReadBinaryWithHeader((const char *)header.data(),
|
||||
(const char *)data.data(), dest, n);
|
||||
bin_io::DecodeBase64(txt, header_b64, header);
|
||||
bin_io::DecodeBase64(txt + header_b64, strlen(txt)-header_b64, data);
|
||||
ReadBinaryWithHeader(header.data(), data.data(), dest, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
std::vector<char> data;
|
||||
bin_io::DecodeBase64(txt, strlen(txt), data);
|
||||
ReadBinary((const char *)data.data(), dest, n);
|
||||
ReadBinary(data.data(), dest, n);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+20
-2
@@ -4321,7 +4321,7 @@ const CoarseFineTransformations& NCMesh::GetRefinementTransforms()
|
||||
if (!transforms.embeddings.Size())
|
||||
{
|
||||
transforms.Clear();
|
||||
transforms.embeddings.SetSize(leaf_elements.Size());
|
||||
transforms.embeddings.SetSize(NElements);
|
||||
|
||||
std::string ref_path;
|
||||
ref_path.reserve(100);
|
||||
@@ -4455,7 +4455,8 @@ struct RefType
|
||||
void CoarseFineTransformations::GetCoarseToFineMap(
|
||||
const mfem::Mesh &fine_mesh, Table &coarse_to_fine,
|
||||
Array<int> &coarse_to_ref_type, Table &ref_type_to_matrix,
|
||||
Array<mfem::Geometry::Type> &ref_type_to_geom) const
|
||||
Array<mfem::Geometry::Type> &ref_type_to_geom,
|
||||
bool get_coarse_to_fine_only) const
|
||||
{
|
||||
const int fine_ne = embeddings.Size();
|
||||
int coarse_ne = -1;
|
||||
@@ -4495,6 +4496,11 @@ void CoarseFineTransformations::GetCoarseToFineMap(
|
||||
coarse_to_fine.GetJ()[i] = cf_j[i].two;
|
||||
}
|
||||
|
||||
if (get_coarse_to_fine_only) { return; }
|
||||
MFEM_VERIFY(fine_mesh.GetLastOperation() != Mesh::Operation::DEREFINE,
|
||||
"GetCoarseToFineMap is not fully supported for derefined meshes."
|
||||
" Set 'get_coarse_to_fine_only=true'.")
|
||||
|
||||
using internal::RefType;
|
||||
using std::map;
|
||||
using std::pair;
|
||||
@@ -4536,6 +4542,18 @@ void CoarseFineTransformations::GetCoarseToFineMap(
|
||||
ref_type_to_matrix.ShiftUpI();
|
||||
}
|
||||
|
||||
void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &fine_mesh,
|
||||
Table &coarse_to_fine) const
|
||||
{
|
||||
Array<int> coarse_to_ref_type;
|
||||
Table ref_type_to_matrix;
|
||||
Array<mfem::Geometry::Type> ref_type_to_geom;
|
||||
bool get_coarse_to_fine_only = true;
|
||||
GetCoarseToFineMap(fine_mesh, coarse_to_fine, coarse_to_ref_type,
|
||||
ref_type_to_matrix, ref_type_to_geom,
|
||||
get_coarse_to_fine_only);
|
||||
}
|
||||
|
||||
void NCMesh::ClearTransforms()
|
||||
{
|
||||
coarse_elements.DeleteAll();
|
||||
|
||||
+5
-1
@@ -68,7 +68,11 @@ struct CoarseFineTransformations
|
||||
Table &coarse_to_fine,
|
||||
Array<int> &coarse_to_ref_type,
|
||||
Table &ref_type_to_matrix,
|
||||
Array<Geometry::Type> &ref_type_to_geom) const;
|
||||
Array<Geometry::Type> &ref_type_to_geom,
|
||||
bool get_coarse_to_fine_only = false) const;
|
||||
|
||||
void GetCoarseToFineMap(const Mesh &fine_mesh,
|
||||
Table &coarse_to_fine) const;
|
||||
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
@@ -297,9 +297,6 @@ protected: // implementation
|
||||
|
||||
virtual void Update();
|
||||
|
||||
virtual int GetNumGhostElements() const { return NGhostElements; }
|
||||
virtual int GetNumGhostVertices() const { return NGhostVertices; }
|
||||
|
||||
/// Return the processor number for a global element number.
|
||||
int Partition(long index, long total_elements) const
|
||||
{ return index * NRanks / total_elements; }
|
||||
|
||||
@@ -109,15 +109,19 @@ int main(int argc, char *argv[])
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
if (myid == 0) { args.PrintOptions(cout); }
|
||||
|
||||
// Enable hardware devices such as GPUs, and programming models such as CUDA,
|
||||
// OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device("cpu");
|
||||
device.Print();
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// Refine the mesh.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
|
||||
@@ -96,7 +96,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
|
||||
const SparseMatrix& agg_elem,
|
||||
const SparseMatrix& elem_dof,
|
||||
const HypreParMatrix& dof_truedof,
|
||||
Array<int>& agg_starts)
|
||||
Array<HYPRE_Int>& agg_starts)
|
||||
{
|
||||
OperatorPtr agg_dof(Mult(agg_elem, elem_dof));
|
||||
SparseMatrix& agg_dof_ref = *agg_dof.As<SparseMatrix>();
|
||||
@@ -131,7 +131,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
|
||||
|
||||
void DFSSpaces::MakeDofRelationTables(int level)
|
||||
{
|
||||
Array<int> agg_starts(Array<int>(l2_0_fes_->GetDofOffsets(), 2));
|
||||
Array<HYPRE_Int> agg_starts(Array<HYPRE_Int>(l2_0_fes_->GetDofOffsets(), 2));
|
||||
auto& elem_agg = (const SparseMatrix&)*l2_0_fes_->GetUpdateOperator();
|
||||
OperatorPtr agg_elem(Transpose(elem_agg));
|
||||
SparseMatrix& agg_el = *agg_elem.As<SparseMatrix>();
|
||||
|
||||
@@ -110,6 +110,7 @@ then
|
||||
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
|
||||
|
||||
cp ${hostconfig_path} ${project_dir}/config/
|
||||
ln -sf ${build_root}/data ${project_dir}/../
|
||||
|
||||
make all -j ${threads}
|
||||
fi
|
||||
|
||||
@@ -28,6 +28,7 @@ set(UNIT_TESTS_SRCS
|
||||
linalg/test_complex_operator.cpp
|
||||
linalg/test_constrainedsolver.cpp
|
||||
linalg/test_direct_solvers.cpp
|
||||
linalg/test_fdsolver.cpp
|
||||
linalg/test_hypre_ilu.cpp
|
||||
linalg/test_ilu.cpp
|
||||
linalg/test_matrix_block.cpp
|
||||
|
||||
@@ -11,48 +11,14 @@
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
#include "run_unit_tests.hpp"
|
||||
|
||||
bool launch_all_non_regression_tests = false;
|
||||
std::string mfem_data_dir;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mfem::Device device("cuda");
|
||||
|
||||
// There must be exactly one instance.
|
||||
Catch::Session session;
|
||||
|
||||
// Build a new command line parser on top of Catch's
|
||||
using namespace Catch::clara;
|
||||
auto cli = session.cli() |
|
||||
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
|
||||
session.cli(cli);
|
||||
|
||||
// For floating point comparisons, print 8 digits for single precision
|
||||
// values, and 16 digits for double precision values.
|
||||
Catch::StringMaker<float>::precision = 8;
|
||||
Catch::StringMaker<double>::precision = 16;
|
||||
|
||||
// Apply provided command line arguments.
|
||||
int r = session.applyCommandLine(argc, argv);
|
||||
if (r != 0) { return r; }
|
||||
|
||||
auto cfg = session.configData();
|
||||
|
||||
cfg.testsOrTags.push_back("[CUDA]");
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Exclude tests marked as Parallel in a serial run, even when compiled with
|
||||
// MPI. This is done because there is no MPI session initialized.
|
||||
cfg.testsOrTags.push_back("~[Parallel]");
|
||||
#endif
|
||||
|
||||
std::cout << "INFO: Test filter: [CUDA] ~[Parallel]" << std::endl;
|
||||
device.Print();
|
||||
|
||||
session.useConfigData(cfg);
|
||||
|
||||
int result = session.run();
|
||||
|
||||
return result;
|
||||
// Include only tests labeled with CUDA. Exclude parallel tests.
|
||||
return RunCatchSession(argc, argv, {"[CUDA]", "~[Parallel]"});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
#include "general/tinyxml2.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -235,3 +236,86 @@ TEST_CASE("Save and load from collections", "[DataCollection]")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SaveDataCollection(DataCollection &dc, int cycle, double t)
|
||||
{
|
||||
dc.SetCycle(cycle);
|
||||
dc.SetTime(t);
|
||||
dc.Save();
|
||||
}
|
||||
|
||||
TEST_CASE("ParaView restart mode", "[ParaView]")
|
||||
{
|
||||
Mesh mesh = Mesh::MakeCartesian2D(2, 3, Element::QUADRILATERAL);
|
||||
H1_FECollection fec(1, mesh.Dimension());
|
||||
FiniteElementSpace fes(&mesh, &fec);
|
||||
GridFunction u(&fes);
|
||||
u = 0.0;
|
||||
|
||||
// Write initial dataset with three timesteps: 0, 1, 2.
|
||||
{
|
||||
ParaViewDataCollection dc("ParaView", &mesh);
|
||||
dc.RegisterField("u", &u);
|
||||
SaveDataCollection(dc, 0, 0);
|
||||
SaveDataCollection(dc, 1, 1);
|
||||
SaveDataCollection(dc, 2, 2);
|
||||
}
|
||||
|
||||
// Using restart mode, append to the existing dataset, overwriting timesteps
|
||||
// 1 and 2 with 1 and 1.5.
|
||||
{
|
||||
ParaViewDataCollection dc("ParaView", &mesh);
|
||||
dc.UseRestartMode(true);
|
||||
dc.RegisterField("u", &u);
|
||||
SaveDataCollection(dc, 1, 1.0);
|
||||
SaveDataCollection(dc, 2, 1.5);
|
||||
}
|
||||
|
||||
// Parse the resulting PVD file, and verify that the structure is correct,
|
||||
// and that it contains three timesteps: 0, 1, and 1.5.
|
||||
using namespace tinyxml2;
|
||||
auto StringCompare = [](const char *s1, const char *s2)
|
||||
{
|
||||
if (s1 == NULL || s2 == NULL) { return false; }
|
||||
return strcmp(s1, s2) == 0;
|
||||
};
|
||||
auto VerifyDataset = [StringCompare](const XMLElement *ds, double t_ref)
|
||||
{
|
||||
REQUIRE(ds);
|
||||
REQUIRE(StringCompare(ds->Name(), "DataSet"));
|
||||
const char *timestep = ds->Attribute("timestep");
|
||||
REQUIRE(timestep);
|
||||
double t = std::stod(timestep);
|
||||
REQUIRE(t == MFEM_Approx(t_ref));
|
||||
};
|
||||
|
||||
XMLDocument xml;
|
||||
xml.LoadFile("ParaView/ParaView.pvd");
|
||||
REQUIRE(xml.ErrorID() == XML_SUCCESS);
|
||||
|
||||
const XMLElement *vtkfile = xml.FirstChildElement();
|
||||
REQUIRE(vtkfile);
|
||||
REQUIRE(StringCompare(vtkfile->Name(), "VTKFile"));
|
||||
const XMLElement *collection = vtkfile->FirstChildElement();
|
||||
REQUIRE(collection);
|
||||
REQUIRE(StringCompare(collection->Name(), "Collection"));
|
||||
|
||||
const XMLElement *dataset = collection->FirstChildElement();
|
||||
VerifyDataset(dataset, 0.0);
|
||||
dataset = dataset->NextSiblingElement();
|
||||
VerifyDataset(dataset, 1.0);
|
||||
dataset = dataset->NextSiblingElement();
|
||||
VerifyDataset(dataset, 1.5);
|
||||
REQUIRE(dataset->NextSiblingElement() == NULL);
|
||||
|
||||
// Clean up
|
||||
for (int c=0; c<=2; ++c)
|
||||
{
|
||||
std::string prefix = "ParaView/Cycle00000" + std::to_string(c);
|
||||
REQUIRE(remove((prefix + "/data.pvtu").c_str()) == 0);
|
||||
REQUIRE(remove((prefix + "/proc000000.vtu").c_str()) == 0);
|
||||
REQUIRE(rmdir(prefix.c_str()) == 0);
|
||||
}
|
||||
REQUIRE(remove("ParaView/ParaView.pvd") == 0);
|
||||
REQUIRE(rmdir("ParaView") == 0);
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ TEST_CASE("DG SumIntegrator", "[SumIntegrator][PartialAssembly]")
|
||||
integ2.AssemblePAInteriorFaces(fes);
|
||||
integ_sum.AssemblePAInteriorFaces(fes);
|
||||
|
||||
const Operator *R_int = fes.GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Interior);
|
||||
const FaceRestriction *R_int = fes.GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Interior);
|
||||
|
||||
int n_int = R_int->Height();
|
||||
Vector x(n_int), y1(n_int), y2(n_int);
|
||||
@@ -198,10 +198,10 @@ TEST_CASE("DG SumIntegrator", "[SumIntegrator][PartialAssembly]")
|
||||
integ2.AssemblePABoundaryFaces(fes);
|
||||
integ_sum.AssemblePABoundaryFaces(fes);
|
||||
|
||||
const Operator *R_bdr = fes.GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Boundary,
|
||||
L2FaceValues::DoubleValued);
|
||||
const FaceRestriction *R_bdr = fes.GetFaceRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Boundary,
|
||||
L2FaceValues::DoubleValued);
|
||||
|
||||
int n_bdr = R_bdr->Height();
|
||||
x.SetSize(n_bdr);
|
||||
|
||||
@@ -38,7 +38,7 @@ TEST_CASE("OperatorChebyshevSmoother", "[Chebyshev symmetry]")
|
||||
Vector diag(fespace.GetTrueVSize());
|
||||
aform.AssembleDiagonal(diag);
|
||||
|
||||
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag, ess_tdof_list,
|
||||
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag, ess_tdof_list,
|
||||
cheb_order);
|
||||
|
||||
int n = smoother->Width();
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
TEST_CASE("FDSolver",
|
||||
"[FDSolver]")
|
||||
{
|
||||
double tol = 1e-10;
|
||||
|
||||
// SPD matrices
|
||||
DenseMatrix A0(
|
||||
{
|
||||
{
|
||||
1.29919, 0.61256, 0.82545
|
||||
},
|
||||
{0.61256, 0.57891, 0.39662},
|
||||
{0.82545, 0.39662, 0.57541}
|
||||
});
|
||||
|
||||
DenseMatrix A1(
|
||||
{
|
||||
{0.748236, 0.701663, 0.607517, 0.236740},
|
||||
{0.701663, 0.809316, 0.713186, 0.256070},
|
||||
{0.607517, 0.713186, 0.794221, 0.233943},
|
||||
{0.236740, 0.256070, 0.233943, 0.083129}
|
||||
});
|
||||
|
||||
DenseMatrix B0(
|
||||
{
|
||||
{0.13483, 0.51389, 0.43052},
|
||||
{0.51389, 2.26750, 1.86331},
|
||||
{0.43052, 1.86331, 1.59869}
|
||||
});
|
||||
|
||||
DenseMatrix B1(
|
||||
{
|
||||
{0.94177, 1.02400, 1.14743, 0.35723},
|
||||
{1.02400, 1.79087, 1.78708, 0.78304},
|
||||
{1.14743, 1.78708, 2.06259, 0.80837},
|
||||
{0.35723, 0.78304, 0.80837, 1.01798}
|
||||
});
|
||||
|
||||
|
||||
SECTION("2D")
|
||||
{
|
||||
Array<DenseMatrix *> A(2), B(2);
|
||||
A[0] = &A0; A[1] = &A1;
|
||||
B[0] = &B0; B[1] = &B1;
|
||||
|
||||
Vector y(12); y.Randomize(1);
|
||||
Vector x(12), diff(12);
|
||||
|
||||
FDSolver S(A,B);
|
||||
S.Mult(y,x);
|
||||
|
||||
DenseMatrix C1, C;
|
||||
KronProd(A0, B1, C1);
|
||||
KronProd(B0, A1, C);
|
||||
|
||||
C.Add(1., C1);
|
||||
|
||||
DenseMatrixInverse Cinv(C);
|
||||
Cinv.Mult(y,diff);
|
||||
|
||||
diff-=x;
|
||||
REQUIRE(diff.Norml2() < tol);
|
||||
}
|
||||
|
||||
|
||||
SECTION("3D")
|
||||
{
|
||||
DenseMatrix A2(
|
||||
{
|
||||
{1.14593, 0.76119},
|
||||
{0.76119, 0.78993}
|
||||
});
|
||||
DenseMatrix B2(
|
||||
{
|
||||
{0.88088, 0.37899},
|
||||
{0.37899, 0.45096}
|
||||
});
|
||||
Array<DenseMatrix *> A(3), B(3);
|
||||
A[0] = &A0; A[1] = &A1; A[2] = &A2;
|
||||
B[0] = &B0; B[1] = &B1; B[2] = &B2;
|
||||
|
||||
Vector y(24); y.Randomize(1);
|
||||
Vector x(24), diff(24);
|
||||
|
||||
FDSolver S(A,B);
|
||||
S.Mult(y,x);
|
||||
|
||||
DenseMatrix Temp, C0, C1, C;
|
||||
|
||||
KronProd(A0, B1, Temp);
|
||||
KronProd(Temp, B2, C0);
|
||||
KronProd(B0, A1, Temp);
|
||||
KronProd(Temp, B2, C1);
|
||||
KronProd(B0, B1, Temp);
|
||||
KronProd(Temp, A2, C);
|
||||
|
||||
C.Add(1.,C0);
|
||||
C.Add(1.,C1);
|
||||
|
||||
DenseMatrixInverse Cinv(C);
|
||||
Cinv.Mult(y,diff);
|
||||
|
||||
diff-=x;
|
||||
REQUIRE(diff.Norml2() < tol);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // if MFEM_USE_LAPACK
|
||||
@@ -239,6 +239,188 @@ TEST_CASE("DenseMatrix A*B^T methods",
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("KronMult methods",
|
||||
"[DenseMatrix]")
|
||||
{
|
||||
double tol = 1e-12;
|
||||
int nA = 3, mA = 4;
|
||||
int nB = 5, mB = 6;
|
||||
DenseMatrix A(nA,mA);
|
||||
DenseMatrix B(nB,mB);
|
||||
|
||||
for (int i = 0; i<nA; i++)
|
||||
for (int j = 0; j<mA; j++)
|
||||
{
|
||||
A(i,j) = ((double)rand()/(double)RAND_MAX);
|
||||
}
|
||||
|
||||
for (int i = 0; i<nB; i++)
|
||||
for (int j = 0; j<mB; j++)
|
||||
{
|
||||
B(i,j) = ((double)rand()/(double)RAND_MAX);
|
||||
}
|
||||
|
||||
DenseMatrix AB;
|
||||
KronProd(A,B,AB);
|
||||
|
||||
// (A ⊗ B) r
|
||||
SECTION("KronMultABr")
|
||||
{
|
||||
Vector r(mA*mB); r.Randomize();
|
||||
MFEM_VERIFY(r.Size() == AB.Width(), "Check r size");
|
||||
Vector z0(AB.Height());
|
||||
AB.Mult(r,z0);
|
||||
|
||||
Vector z1;
|
||||
KronMult(A,B,r,z1);
|
||||
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
|
||||
z0-=z1;
|
||||
REQUIRE(z0.Norml2() < tol);
|
||||
}
|
||||
// (A ⊗ B) R
|
||||
SECTION("KronMultABR")
|
||||
{
|
||||
int nR = mA*mB;
|
||||
int mR = 7;
|
||||
DenseMatrix R(nR, mR);
|
||||
for (int i = 0; i<nR; i++)
|
||||
for (int j = 0; j<mR; j++)
|
||||
{
|
||||
R(i,j) = ((double)rand()/(double)RAND_MAX);
|
||||
}
|
||||
|
||||
DenseMatrix Z0(nA*nB,mR);
|
||||
Mult(AB,R,Z0);
|
||||
|
||||
DenseMatrix Z1;
|
||||
KronMult(A,B,R,Z1);
|
||||
MFEM_VERIFY(Z0.Height() == Z1.Height() &&
|
||||
Z0.Width() == Z1.Width(), "Check z1 size");
|
||||
Z0-=Z1;
|
||||
|
||||
REQUIRE(Z0.MaxMaxNorm() < tol);
|
||||
|
||||
}
|
||||
|
||||
// (A ⊗ B ⊗ C) r
|
||||
SECTION("KronMultABCr")
|
||||
{
|
||||
int nC = 7, mC = 2;
|
||||
DenseMatrix C(nC, mC);
|
||||
for (int i = 0; i<nC; i++)
|
||||
for (int j = 0; j<mC; j++)
|
||||
{
|
||||
C(i,j) = ((double)rand()/(double)RAND_MAX);
|
||||
}
|
||||
|
||||
DenseMatrix ABC;
|
||||
KronProd(AB,C,ABC);
|
||||
Vector r(mA*mB*mC); r.Randomize();
|
||||
MFEM_VERIFY(r.Size() == ABC.Width(), "Check r size");
|
||||
Vector z0(nA*nB*nC);
|
||||
ABC.Mult(r,z0);
|
||||
|
||||
Vector z1;
|
||||
KronMult(A,B,C,r,z1);
|
||||
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
|
||||
z0-=z1;
|
||||
REQUIRE(z0.Norml2() < tol);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("KronMultInv methods",
|
||||
"[DenseMatrixInverse]")
|
||||
{
|
||||
double tol = 1e-12;
|
||||
int nA = 3;
|
||||
int nB = 2;
|
||||
DenseMatrix A(
|
||||
{
|
||||
{ 1.0, 0.2, 3.4},
|
||||
{-2.0, -1.0, 3.1},
|
||||
{ 0.7, 1.4,-0.9}
|
||||
});
|
||||
DenseMatrix B(
|
||||
{
|
||||
{-10.1, 5.7},
|
||||
{-3.0, 4.2}
|
||||
});
|
||||
|
||||
DenseMatrixInverse Ainv(A);
|
||||
DenseMatrixInverse Binv(B);
|
||||
|
||||
DenseMatrix AB;
|
||||
KronProd(A,B,AB);
|
||||
|
||||
// (A^-1 ⊗ B^-1) r
|
||||
SECTION("KronMultInvABr")
|
||||
{
|
||||
|
||||
Vector r(nA*nB); r.Randomize();
|
||||
MFEM_VERIFY(r.Size() == AB.Width(), "Check r size");
|
||||
Vector z0(AB.Height());
|
||||
DenseMatrixInverse ABinv(AB);
|
||||
ABinv.Mult(r,z0);
|
||||
|
||||
Vector z1;
|
||||
KronMult(Ainv,Binv,r,z1);
|
||||
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
|
||||
z0-=z1;
|
||||
REQUIRE(z0.Norml2() < tol);
|
||||
}
|
||||
|
||||
// (A^-1 ⊗ B^-1) R
|
||||
SECTION("KronMultInvABR")
|
||||
{
|
||||
int nR = nA*nB;
|
||||
int mR = 7;
|
||||
DenseMatrix R(nR, mR);
|
||||
for (int i = 0; i<nR; i++)
|
||||
for (int j = 0; j<mR; j++)
|
||||
{
|
||||
R(i,j) = ((double)rand()/(double)RAND_MAX);
|
||||
}
|
||||
|
||||
DenseMatrixInverse ABinv(AB);
|
||||
DenseMatrix Z0(nA*nB,mR);
|
||||
ABinv.Mult(R,Z0);
|
||||
|
||||
DenseMatrix Z1;
|
||||
KronMult(Ainv,Binv,R,Z1);
|
||||
MFEM_VERIFY(Z0.Height() == Z1.Height() &&
|
||||
Z0.Width() == Z1.Width(), "Check z1 size");
|
||||
Z0-=Z1;
|
||||
|
||||
REQUIRE(Z0.MaxMaxNorm() < tol);
|
||||
}
|
||||
|
||||
// (A^-1 ⊗ B^-1 ⊗ C^-1) r
|
||||
SECTION("KronMultInvABCr")
|
||||
{
|
||||
int nC = 4;
|
||||
DenseMatrix C(
|
||||
{
|
||||
{-2.1, 1.6, -3.4, 17.5},
|
||||
{-7.1, 1.3, -7.5, -12.5},
|
||||
{ 0.5, 5.7, -6.0, -0.5},
|
||||
{ 9.2, 0.3, -1.4, -14.9}
|
||||
});
|
||||
DenseMatrix ABC;
|
||||
KronProd(AB,C,ABC);
|
||||
DenseMatrixInverse ABCInv(ABC);
|
||||
Vector r(nA*nB*nC); r.Randomize();
|
||||
MFEM_VERIFY(r.Size() == ABC.Width(), "Check r size");
|
||||
Vector z0(nA*nB*nC);
|
||||
ABCInv.Mult(r,z0);
|
||||
|
||||
DenseMatrixInverse Cinv(C);
|
||||
Vector z1;
|
||||
KronMult(Ainv,Binv,Cinv,r,z1);
|
||||
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
|
||||
z0-=z1;
|
||||
REQUIRE(z0.Norml2() < tol);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("LUFactors RightSolve", "[DenseMatrix]")
|
||||
{
|
||||
@@ -314,6 +496,88 @@ TEST_CASE("DenseTensor LinearSolve methods",
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
TEST_CASE("EigenSystem methods",
|
||||
"[DenseMatrix]")
|
||||
{
|
||||
double tol = 1e-12;
|
||||
SECTION("SPD Matrix")
|
||||
{
|
||||
DenseMatrix A({{0.56806, 0.29211, 0.48315, 0.70024},
|
||||
{0.29211, 0.85147, 0.68123, 0.70689},
|
||||
{0.48315, 0.68123, 1.07229, 1.02681},
|
||||
{0.70024, 0.70689, 1.02681, 1.15468}
|
||||
});
|
||||
DenseMatrix V, AV(4);
|
||||
Vector Lambda;
|
||||
for (bool sym: { false, true })
|
||||
{
|
||||
DenseMatrixEigensystem EigA(A,sym);
|
||||
EigA.Eval();
|
||||
V = EigA.Eigenvectors();
|
||||
Lambda = EigA.Eigenvalues();
|
||||
Mult(A,V,AV);
|
||||
V.RightScaling(Lambda);
|
||||
AV -= V;
|
||||
REQUIRE(AV.MaxMaxNorm() < tol);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Indefinite Matrix")
|
||||
{
|
||||
DenseMatrix A({{0.486278, 0.041135, 0.480727, 0.616026},
|
||||
{0.523599, 0.119827, 0.087808, 0.415241},
|
||||
{0.214454, 0.661631, 0.909626, 0.744259},
|
||||
{0.107007, 0.630604, 0.077862, 0.221006}
|
||||
});
|
||||
DenseMatrixEigensystem EigA(A);
|
||||
EigA.Eval();
|
||||
|
||||
Vector Lambda_r, Lambda_i;
|
||||
// Real part of eigenvalues
|
||||
Lambda_r = EigA.Eigenvalues();
|
||||
// Imag part of eigenvalues
|
||||
Lambda_i = EigA.Eigenvalues(true);
|
||||
|
||||
DenseMatrix V;
|
||||
V = EigA.Eigenvectors();
|
||||
// Real part of eigenvectors
|
||||
DenseMatrix Vr(4), Vi(4);
|
||||
Vr.SetCol(0,V.GetColumn(0));
|
||||
Vr.SetCol(1,V.GetColumn(1));
|
||||
Vr.SetCol(2,V.GetColumn(1));
|
||||
Vr.SetCol(3,V.GetColumn(3));
|
||||
|
||||
// Imag part of eigenvectors
|
||||
Vector vi(4); V.GetColumn(2,vi);
|
||||
Vi.SetCol(0,0.);
|
||||
Vi.SetCol(1,vi); vi *= -1.;
|
||||
Vi.SetCol(2,vi);
|
||||
Vi.SetCol(3,0.);
|
||||
|
||||
// Check that A*V = V * Lambda
|
||||
// or A * (V_r + i V_i ) = (V_r + i V_i)*(Lamda_r + i Lambda_i)
|
||||
// or A * V_r = V_r * Lambda_r - V_i * Lambda_i
|
||||
// and A * V_i = V_r ( Lambda_i + V_i * Lambda_r
|
||||
DenseMatrix AVr(4), AVi(4);
|
||||
Mult(A,Vr, AVr);
|
||||
Mult(A,Vi, AVi);
|
||||
|
||||
DenseMatrix Vrlr = Vr; Vrlr.RightScaling(Lambda_r);
|
||||
DenseMatrix Vrli = Vr; Vrli.RightScaling(Lambda_i);
|
||||
DenseMatrix Vilr = Vi; Vilr.RightScaling(Lambda_r);
|
||||
DenseMatrix Vili = Vi; Vili.RightScaling(Lambda_i);
|
||||
|
||||
AVr -= Vrlr; AVr+= Vili;
|
||||
AVi -= Vrli; AVi-= Vilr;
|
||||
|
||||
REQUIRE(AVr.MaxMaxNorm() < tol);
|
||||
REQUIRE(AVi.MaxMaxNorm() < tol);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // if MFEM_USE_LAPACK
|
||||
TEST_CASE("DenseTensor copy", "[DenseMatrix][DenseTensor]")
|
||||
{
|
||||
DenseTensor t1(2,3,4);
|
||||
|
||||
+13
-8
@@ -214,21 +214,27 @@ $(DATA_DIR):
|
||||
MFEM_TESTS = UNIT_TESTS
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
ifeq (,$(wildcard $(MFEM_DIR)/../data))
|
||||
MFEM_DATA_FLAG =
|
||||
else
|
||||
MFEM_DATA_FLAG = --data $(MFEM_DIR)/../data
|
||||
endif
|
||||
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Unit tests,,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, Unit tests,$(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
|
||||
ceed_tests-test-seq: ceed_tests
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cpu),--device ceed-cpu,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cpu),--device ceed-cpu $(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
ifeq ($(MFEM_USE_CUDA),YES)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cuda-ref),--device ceed-cuda:/gpu/cuda/ref,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cuda-shared),--device ceed-cuda:/gpu/cuda/shared,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cuda-gen),--device ceed-cuda:/gpu/cuda/gen,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cuda-ref),--device ceed-cuda:/gpu/cuda/ref $(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cuda-shared),--device ceed-cuda:/gpu/cuda/shared $(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<,, CEED Unit tests (cuda-gen),--device ceed-cuda:/gpu/cuda/gen $(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
endif
|
||||
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP)
|
||||
%-test-par: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI) 1, Parallel unit tests,,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<, $(RUN_MPI) $(MFEM_MPI_NP), Parallel unit tests,,SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<, $(RUN_MPI) 1, Parallel unit tests,$(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
@$(call mfem-test,$<, $(RUN_MPI) $(MFEM_MPI_NP), Parallel unit tests,$(MFEM_DATA_FLAG),SKIP-NO-VIS)
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
@@ -237,4 +243,3 @@ $(MFEM_LIB_FILE):
|
||||
clean:
|
||||
rm -f $(SEQ_UNIT_TESTS) $(PAR_UNIT_TESTS) *.o */*.o */*~ *~
|
||||
rm -rf *.dSYM output_meshes
|
||||
|
||||
|
||||
@@ -41,3 +41,21 @@ TEST_CASE("VTU XML Reader", "[Mesh][VTU][XML]")
|
||||
REQUIRE(mesh.GetNumGeometries(2) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("VTU XML Compressed Blocks", "[VTU][XML][MFEMData]")
|
||||
{
|
||||
auto filename = GENERATE(
|
||||
"bracket_appended_compressed.vtu",
|
||||
"bracket_appended_encoded_compressed.vtu",
|
||||
"bracket_inline_compressed.vtu"
|
||||
);
|
||||
|
||||
std::string mesh_path = mfem_data_dir + "/vtk/" + filename;
|
||||
Mesh mesh = Mesh::LoadFromFile(mesh_path.c_str());
|
||||
|
||||
REQUIRE(mesh.Dimension() == 3);
|
||||
REQUIRE(mesh.GetNE() == 206208);
|
||||
REQUIRE(mesh.GetNV() == 50000);
|
||||
REQUIRE(mesh.HasGeometry(Geometry::TETRAHEDRON));
|
||||
REQUIRE(mesh.GetNumGeometries(3) == 1);
|
||||
}
|
||||
|
||||
@@ -186,10 +186,12 @@ int tmop(int id, Req &res, int argc, char *argv[])
|
||||
case 2: metric = new TMOP_Metric_002; break;
|
||||
case 7: metric = new TMOP_Metric_007; break;
|
||||
case 77: metric = new TMOP_Metric_077; break;
|
||||
case 80: metric = new TMOP_Metric_080(0.5); break;
|
||||
case 302: metric = new TMOP_Metric_302; break;
|
||||
case 303: metric = new TMOP_Metric_303; break;
|
||||
case 315: metric = new TMOP_Metric_315; break;
|
||||
case 321: metric = new TMOP_Metric_321; break;
|
||||
case 332: metric = new TMOP_Metric_332(0.5); break;
|
||||
default:
|
||||
{
|
||||
if (id == 0) { cout << "Unknown metric_id: " << metric_id << endl; }
|
||||
@@ -740,6 +742,13 @@ static void tmop_tests(int id = 0, bool all = false)
|
||||
POR({1,2}).QOR({2,4}).
|
||||
TID({4}).MID({1,2})).Run(id,all);
|
||||
|
||||
Launch(Launch::Args("Square01 + Adapted discrete size").
|
||||
MESH("../../miniapps/meshing/square01.mesh").REFINE(1).
|
||||
NORMALIZATION(true).
|
||||
POR({1,2}).QOR({4,6}).
|
||||
LINEAR_ITERATIONS(150).
|
||||
TID({5}).MID({80}).LS({3})).Run(id,all);
|
||||
|
||||
Launch(Launch::Args("Blade").
|
||||
MESH("../../miniapps/meshing/blade.mesh").
|
||||
POR({1,2}).QOR({2,4}).
|
||||
@@ -768,6 +777,12 @@ static void tmop_tests(int id = 0, bool all = false)
|
||||
POR({1,2}).QOR({4,2}).
|
||||
TID({7}).MID({302,321})).Run(id,all);
|
||||
|
||||
Launch(Launch::Args("Cube + Discrete size + normalization").
|
||||
MESH("../../miniapps/meshing/cube.mesh").
|
||||
NORMALIZATION(true).
|
||||
POR({1,2}).QOR({4,2}).
|
||||
TID({5}).MID({332})).Run(id,all);
|
||||
|
||||
// Note: order 1 has no interior nodes, so all residuals are zero and the
|
||||
// Newton iteration exits immediately.
|
||||
Launch(Launch::Args("Toroid-Hex").
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
#include "run_unit_tests.hpp"
|
||||
|
||||
bool launch_all_non_regression_tests = false;
|
||||
std::string mfem_data_dir;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
mfem::MPI_Session *GlobalMPISession;
|
||||
@@ -24,44 +25,14 @@ mfem::MPI_Session *GlobalMPISession;
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mfem::Device device("cuda");
|
||||
|
||||
// There must be exactly one instance.
|
||||
Catch::Session session;
|
||||
|
||||
// Build a new command line parser on top of Catch's
|
||||
using namespace Catch::clara;
|
||||
auto cli = session.cli() |
|
||||
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
|
||||
session.cli(cli);
|
||||
|
||||
// For floating point comparisons, print 8 digits for single precision
|
||||
// values, and 16 digits for double precision values.
|
||||
Catch::StringMaker<float>::precision = 8;
|
||||
Catch::StringMaker<double>::precision = 16;
|
||||
|
||||
// Apply provided command line arguments.
|
||||
int r = session.applyCommandLine(argc, argv);
|
||||
if (r != 0) { return r; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
mfem::MPI_Session mpi;
|
||||
GlobalMPISession = &mpi;
|
||||
|
||||
// Exclude all tests that are not labeled with Parallel and CUDA.
|
||||
auto cfg = session.configData();
|
||||
cfg.testsOrTags.push_back("[Parallel]");
|
||||
cfg.testsOrTags.push_back("[CUDA]");
|
||||
|
||||
session.useConfigData(cfg);
|
||||
|
||||
if (mpi.Root())
|
||||
{
|
||||
std::cout << "INFO: Test filter: [Parallel] [CUDA]" << std::endl;
|
||||
device.Print();
|
||||
}
|
||||
bool root = mpi.Root();
|
||||
#else
|
||||
bool root = true;
|
||||
#endif
|
||||
|
||||
int result = session.run();
|
||||
|
||||
return result;
|
||||
// Include only tests that are labeled with both CUDA and Parallel.
|
||||
return RunCatchSession(argc, argv, {"[CUDA]","[Parallel]"}, root);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
#include "run_unit_tests.hpp"
|
||||
|
||||
bool launch_all_non_regression_tests = false;
|
||||
std::string mfem_data_dir;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
mfem::MPI_Session *GlobalMPISession;
|
||||
@@ -23,43 +24,14 @@ mfem::MPI_Session *GlobalMPISession;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// There must be exactly one instance.
|
||||
Catch::Session session;
|
||||
|
||||
// Build a new command line parser on top of Catch's
|
||||
using namespace Catch::clara;
|
||||
auto cli = session.cli() |
|
||||
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
|
||||
session.cli(cli);
|
||||
|
||||
// For floating point comparisons, print 8 digits for single precision
|
||||
// values, and 16 digits for double precision values.
|
||||
Catch::StringMaker<float>::precision = 8;
|
||||
Catch::StringMaker<double>::precision = 16;
|
||||
|
||||
// Apply provided command line arguments.
|
||||
int r = session.applyCommandLine(argc, argv);
|
||||
if (r != 0) { return r; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
mfem::MPI_Session mpi;
|
||||
GlobalMPISession = &mpi;
|
||||
|
||||
// Exclude all tests that are not labeled with Parallel.
|
||||
auto cfg = session.configData();
|
||||
cfg.testsOrTags.push_back("[Parallel]");
|
||||
session.useConfigData(cfg);
|
||||
|
||||
// NOTE: tests marked with "[CUDA]" (in addition to "[Parallel]") are still
|
||||
// run with the default device.
|
||||
|
||||
if (mpi.Root())
|
||||
{
|
||||
std::cout << "INFO: Test filter: [Parallel]" << std::endl;
|
||||
}
|
||||
bool root = mpi.Root();
|
||||
#else
|
||||
bool root = true;
|
||||
#endif
|
||||
|
||||
int result = session.run();
|
||||
|
||||
return result;
|
||||
// Only run tests that are labeled with Parallel.
|
||||
return RunCatchSession(argc, argv, {"[Parallel]"}, root);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_RUN_UNIT_TEST
|
||||
#define MFEM_RUN_UNIT_TEST
|
||||
|
||||
#include "unit_tests.hpp"
|
||||
|
||||
static int RunCatchSession(int argc, char *argv[],
|
||||
const std::vector<std::string> &testsOrTags,
|
||||
bool root=true)
|
||||
{
|
||||
// There must be exactly one instance.
|
||||
Catch::Session session;
|
||||
|
||||
// Build a new command line parser on top of Catch's
|
||||
using namespace Catch::clara;
|
||||
auto cli = session.cli()
|
||||
| Opt(launch_all_non_regression_tests) ["--all"] ("all tests")
|
||||
| Opt(mfem_data_dir, "") ["--data"] ("mfem/data repository");
|
||||
session.cli(cli);
|
||||
|
||||
// For floating point comparisons, print 8 digits for single precision
|
||||
// values, and 16 digits for double precision values.
|
||||
Catch::StringMaker<float>::precision = 8;
|
||||
Catch::StringMaker<double>::precision = 16;
|
||||
|
||||
// Apply provided command line arguments.
|
||||
int r = session.applyCommandLine(argc, argv);
|
||||
if (r != 0) { return r; }
|
||||
|
||||
auto cfg = session.configData();
|
||||
cfg.testsOrTags.insert(cfg.testsOrTags.end(), testsOrTags.begin(), testsOrTags.end());
|
||||
if (mfem_data_dir == "") { cfg.testsOrTags.push_back("~[MFEMData]"); }
|
||||
session.useConfigData(cfg);
|
||||
|
||||
if (root)
|
||||
{
|
||||
std::cout << "INFO: Test filter: ";
|
||||
for (std::string &filter : cfg.testsOrTags)
|
||||
{
|
||||
std::cout << filter << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
int result = session.run();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -11,43 +11,13 @@
|
||||
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
#include "run_unit_tests.hpp"
|
||||
|
||||
bool launch_all_non_regression_tests = false;
|
||||
std::string mfem_data_dir;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// There must be exactly one instance.
|
||||
Catch::Session session;
|
||||
|
||||
// Build a new command line parser on top of Catch's
|
||||
using namespace Catch::clara;
|
||||
auto cli = session.cli() |
|
||||
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
|
||||
session.cli(cli);
|
||||
|
||||
// For floating point comparisons, print 8 digits for single precision
|
||||
// values, and 16 digits for double precision values.
|
||||
Catch::StringMaker<float>::precision = 8;
|
||||
Catch::StringMaker<double>::precision = 16;
|
||||
|
||||
// Apply provided command line arguments.
|
||||
int r = session.applyCommandLine(argc, argv);
|
||||
if (r != 0) { return r; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Exclude tests marked as Parallel in a serial run, even when compiled with
|
||||
// MPI. This is done because there is no MPI session initialized.
|
||||
auto cfg = session.configData();
|
||||
cfg.testsOrTags.push_back("~[Parallel]");
|
||||
session.useConfigData(cfg);
|
||||
#endif
|
||||
|
||||
// NOTE: tests marked with "[CUDA]" are still run using the default device.
|
||||
|
||||
std::cout << "INFO: Test filter: ~[Parallel]" << std::endl;
|
||||
|
||||
int result = session.run();
|
||||
|
||||
return result;
|
||||
// Exclude parallel tests.
|
||||
return RunCatchSession(argc, argv, {"~[Parallel]"});
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
/// Command line '--all' option to launch all non-regression tests.
|
||||
extern bool launch_all_non_regression_tests;
|
||||
|
||||
/// Command line '--data' argument for path to mfem/data repo.
|
||||
/** If no --data path is provided, then mfem_data_dir will be the empty string,
|
||||
and tests tagged with [MFEMData] will be skipped. */
|
||||
extern std::string mfem_data_dir;
|
||||
|
||||
/** @brief MFEM_Approx can be used to compare floating point values within an
|
||||
absolute tolerance of @a abs_tol (default value 1e-12) and relative
|
||||
tolerance of @a rel_tol (default value 1e-12). */
|
||||
|
||||
Reference in New Issue
Block a user