Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39150a110c | ||
|
|
1bd031a948 |
@@ -1,12 +0,0 @@
|
||||
# extends https://github.com/jupyterhub/repo2docker/blob/main/repo2docker/buildpacks/conda/environment.yml
|
||||
# see https://mybinder.readthedocs.io/en/latest/using/config_files.html#environment-yml-install-a-conda-environment
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- xeus-cling=0.13.0
|
||||
- xwidgets=0.26.0
|
||||
# NOTE: it's possible these aren't needed for the lab frontend
|
||||
- widgetsnbextension=3.5.1
|
||||
- pip
|
||||
- pip:
|
||||
- glvis==0.3.2
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# cling is installed here (in bin) and will look in {dir}/include and {dir}/lib
|
||||
# without extra intervention (jk it doesn't look in {dir}/lib unless something
|
||||
# has been #included from {dir}/include first...)
|
||||
install_dir=/srv/conda/envs/notebook
|
||||
mkdir -p $install_dir
|
||||
|
||||
# build and install mfem, which is the directory we start in
|
||||
make serial SHARED=YES -j8
|
||||
make install PREFIX=$install_dir
|
||||
|
||||
# install xeus-glvis
|
||||
git clone https://github.com/GLVis/xeus-glvis.git
|
||||
pushd xeus-glvis
|
||||
make install prefix=$install_dir
|
||||
popd
|
||||
|
||||
# install jupyter-lab extension
|
||||
jupyter labextension install @jupyter-widgets/jupyterlab-manager --no-build
|
||||
jupyter labextension install glvis-jupyter
|
||||
|
||||
# fixup kernelspec, we could probably do this from sh but ¯\_(ツ)_/¯
|
||||
python .binder/update_kernel_env.py
|
||||
@@ -1,14 +0,0 @@
|
||||
# Update the LD_LIBRARY_PATH of the C++14 kernel so it can find mfem without
|
||||
# extra pragma cling statements
|
||||
|
||||
import json
|
||||
|
||||
kernelspec = "/srv/conda/envs/notebook/share/jupyter/kernels/xcpp14/kernel.json"
|
||||
|
||||
with open(kernelspec, "r") as f:
|
||||
obj = json.load(f)
|
||||
|
||||
obj["env"] = {"LD_LIBRARY_PATH": "/srv/conda/envs/notebook/lib"}
|
||||
|
||||
with open(kernelspec, "w") as f:
|
||||
json.dump(obj, f)
|
||||
@@ -47,36 +47,26 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-10.15]
|
||||
target: [dbg, opt]
|
||||
mpi: [seq, par]
|
||||
target: [debug, optim]
|
||||
mpi: [sequential, parallel]
|
||||
build-system: [make]
|
||||
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'
|
||||
# 'include' allows us to
|
||||
# - add a variable without creating a new matrix dimension.
|
||||
# - add a new combination ('build-system: cmake' case here)
|
||||
#
|
||||
# note: we will gather coverage info for any non-debug run except the
|
||||
# CMake build.
|
||||
include:
|
||||
- target: dbg
|
||||
- target: debug
|
||||
codecov: NO
|
||||
- target: opt
|
||||
- target: optim
|
||||
codecov: YES
|
||||
- os: ubuntu-18.04
|
||||
target: opt
|
||||
target: optim
|
||||
codecov: NO
|
||||
mpi: par
|
||||
mpi: parallel
|
||||
build-system: cmake
|
||||
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 }}
|
||||
name: ${{ matrix.os }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.build-system }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -102,7 +92,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 == 'par' && matrix.os == 'ubuntu-18.04'
|
||||
if: matrix.mpi == 'parallel' && matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
sudo apt-get install mpich libmpich-dev
|
||||
export MAKE_CXX_FLAG="MPICXX=mpic++"
|
||||
@@ -113,11 +103,11 @@ jobs:
|
||||
sudo apt-get install lcov
|
||||
|
||||
- name: Set up Homebrew
|
||||
if: ( matrix.mpi == 'par' || matrix.codecov == 'YES' ) && matrix.os == 'macos-10.15'
|
||||
if: ( matrix.mpi == 'parallel' || matrix.codecov == 'YES' ) && matrix.os == 'macos-10.15'
|
||||
uses: Homebrew/actions/setup-homebrew@c4aafe8c4620bf08883dd4679c374f11e73329d3
|
||||
|
||||
- name: get MPI (MacOS)
|
||||
if: matrix.mpi == 'par' && matrix.os == 'macos-10.15'
|
||||
if: matrix.mpi == 'parallel' && matrix.os == 'macos-10.15'
|
||||
run: |
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
brew install openmpi
|
||||
@@ -133,40 +123,39 @@ jobs:
|
||||
# Install will only run on cache miss.
|
||||
- name: cache hypre
|
||||
id: hypre-cache
|
||||
if: matrix.mpi == 'par'
|
||||
if: matrix.mpi == 'parallel'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-v2.0
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.0
|
||||
if: matrix.mpi == 'parallel' && steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v1.0
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: ${{ matrix.hypre-target }}
|
||||
hypre-archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
|
||||
# Get Metis through cache, or build it.
|
||||
# Install will only run on cache miss.
|
||||
- name: cache metis
|
||||
id: metis-cache
|
||||
if: matrix.mpi == 'par'
|
||||
if: matrix.mpi == 'parallel'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.0
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2
|
||||
|
||||
- name: install metis
|
||||
if: matrix.mpi == 'par' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.0
|
||||
if: matrix.mpi == 'parallel' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v1.0
|
||||
with:
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
metis-archive: ${{ env.METIS_ARCHIVE }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.0
|
||||
uses: mfem/github-actions/build-mfem@v1.0
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
target: ${{ matrix.target }}
|
||||
@@ -179,17 +168,17 @@ jobs:
|
||||
|
||||
# Run checks (and only checks) on debug targets
|
||||
- name: checks
|
||||
if: matrix.build-system == 'make' && matrix.target == 'dbg'
|
||||
if: matrix.build-system == 'make' && matrix.target == 'debug'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make check
|
||||
|
||||
- name: unit tests
|
||||
if: matrix.build-system == 'make' && matrix.target == 'opt'
|
||||
if: matrix.build-system == 'make' && matrix.target == 'optim'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make unittest
|
||||
|
||||
- name: tests
|
||||
if: matrix.build-system == 'make' && matrix.target == 'opt'
|
||||
if: matrix.build-system == 'make' && matrix.target == 'optim'
|
||||
run: |
|
||||
cd ${{ env.MFEM_TOP_DIR }} && make test
|
||||
|
||||
@@ -201,8 +190,8 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v2.0
|
||||
uses: mfem/github-actions/upload-coverage@v1.0
|
||||
with:
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
name: ${{ matrix.os }}-${{ matrix.mpi }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
directories: "fem general linalg mesh"
|
||||
|
||||
@@ -53,33 +53,32 @@ jobs:
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.0
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.0
|
||||
uses: mfem/github-actions/build-hypre@master
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: int32
|
||||
hypre-archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
|
||||
- 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.0
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.0
|
||||
uses: mfem/github-actions/build-metis@master
|
||||
with:
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
metis-archive: ${{ env.METIS_ARCHIVE }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build-mfem
|
||||
uses: mfem/github-actions/build-mfem@v2.0
|
||||
uses: mfem/github-actions/build-mfem@master
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: optim
|
||||
|
||||
@@ -28,24 +28,49 @@ jobs:
|
||||
access_token: ${{ github.token }}
|
||||
- name: checkout mfem
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: mfem
|
||||
|
||||
- name: copyright check
|
||||
id: copyright
|
||||
run: |
|
||||
./config/githooks/pre-push --copyright
|
||||
|
||||
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
|
||||
continue-on-error: true
|
||||
|
||||
- name: license check
|
||||
id: license
|
||||
run: |
|
||||
./config/githooks/pre-push --license
|
||||
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
|
||||
continue-on-error: true
|
||||
|
||||
- name: release check
|
||||
id: release
|
||||
run: |
|
||||
./config/githooks/pre-push --release
|
||||
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
|
||||
continue-on-error: true
|
||||
|
||||
- name: wrap-up
|
||||
@@ -63,7 +88,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
code-style:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-16.04 # needed for astyle 2.05.1
|
||||
|
||||
steps:
|
||||
- name: checkout mfem
|
||||
@@ -71,11 +96,12 @@ jobs:
|
||||
|
||||
- name: get astyle
|
||||
run: |
|
||||
sudo apt-get install astyle=3.1-1ubuntu2
|
||||
sudo apt-get install astyle=2.05.1-0ubuntu1
|
||||
|
||||
- name: style check
|
||||
run: |
|
||||
./config/githooks/pre-push --style
|
||||
cd tests/scripts
|
||||
./runtest code-style
|
||||
|
||||
documentation:
|
||||
runs-on: ubuntu-18.04
|
||||
@@ -105,9 +131,7 @@ jobs:
|
||||
|
||||
- name: branch-history
|
||||
run: |
|
||||
# We override origin to make sure we point to the main repo.
|
||||
# This is to have consistent test results on PRs from forks.
|
||||
git remote remove origin
|
||||
git remote add origin https://github.com/mfem/mfem.git
|
||||
git fetch origin master:master
|
||||
git checkout -b gh-actions-branch-history
|
||||
./config/githooks/pre-push --history
|
||||
cd tests/scripts
|
||||
./runtest branch-history
|
||||
|
||||
-19
@@ -26,7 +26,6 @@ CMakeFiles/
|
||||
config/_config.hpp
|
||||
config/config.mk
|
||||
config/sample-runs-build.log
|
||||
config/user.mk
|
||||
doc/CodeDocumentation.conf
|
||||
doc/CodeDocumentation.html
|
||||
doc/CodeDocumentation
|
||||
@@ -51,8 +50,6 @@ examples/ex1[04-9]
|
||||
examples/ex1[0-9]p
|
||||
examples/ex2[0-9]
|
||||
examples/ex2[0-9]p
|
||||
examples/ex30
|
||||
examples/ex30p
|
||||
|
||||
examples/refined.mesh
|
||||
examples/displaced.mesh
|
||||
@@ -254,7 +251,6 @@ miniapps/shifted/ParaViewDistance
|
||||
miniapps/shifted/diffusion
|
||||
miniapps/shifted/diffusion.mesh
|
||||
miniapps/shifted/diffusion.gf
|
||||
miniapps/shifted/ParaViewDiffusion
|
||||
|
||||
miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
@@ -290,27 +286,15 @@ miniapps/solvers/ParaView
|
||||
miniapps/solvers/mesh.*
|
||||
miniapps/solvers/sol.*
|
||||
|
||||
miniapps/parelag/MultilevelHcurlHdivSolver
|
||||
miniapps/parelag/*.mesh
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
tests/unit/punit_tests
|
||||
tests/unit/cunit_tests
|
||||
tests/unit/pcunit_tests
|
||||
tests/unit/sedov_tests_*
|
||||
tests/unit/psedov_tests_*
|
||||
tests/unit/tmop_pa_tests_*
|
||||
tests/unit/ptmop_pa_tests_*
|
||||
tests/unit/ceed_tests
|
||||
tests/unit/debug_device_tests
|
||||
|
||||
# Benchmark binaries
|
||||
tests/benchmarks/bench_ceed
|
||||
tests/benchmarks/bench_tmop
|
||||
tests/benchmarks/bench_vector
|
||||
tests/benchmarks/bench_virtuals
|
||||
|
||||
# Test script output
|
||||
tests/scripts/*.err
|
||||
@@ -328,6 +312,3 @@ build-*/*
|
||||
# PETSc automated build
|
||||
petsc-build/*
|
||||
pkg.gitcommit
|
||||
|
||||
# Jupyter Notebook Checkpoints
|
||||
.ipynb_checkpoints
|
||||
|
||||
+208
-37
@@ -13,52 +13,223 @@
|
||||
# at Lawrence Livermore National Laboratory (LLNL). This entire pipeline is
|
||||
# LLNL-specific!
|
||||
|
||||
# We define the following GitLab pipeline variables:
|
||||
#
|
||||
# BUILD_ROOT:
|
||||
# The path to the shared resources between all jobs. For example, external
|
||||
# repositories like 'tests' and 'tpls' are cloned here. Also, 'tpls' is built
|
||||
# once for all targets, so that build happen here. The BUILD_ROOT is unique to
|
||||
# the pipeline, preventing any form of concurrency with other pipelines. This
|
||||
# also means that the BUILD_ROOT directory will never be cleaned.
|
||||
# TODO: add a clean-up mechanism
|
||||
#
|
||||
# REBASELINE:
|
||||
# Defines the default choice for updating the saved baseline results. By default
|
||||
# the baseline can only be updated from the master branch. This variable offers
|
||||
# the option to manually ask for rebaselining from another branch if necessary.
|
||||
#
|
||||
# MFEM_ALLOC_NAME:
|
||||
# On LLNL's quartz, there is only one allocation shared among jobs in order to
|
||||
# save time and resources. This allocation has to be uniquely named so that we
|
||||
# are sure to retrieve it.
|
||||
#
|
||||
# TPLS_REPO & TESTS_REPO:
|
||||
# Git repositories used in the pipeline
|
||||
#
|
||||
# ARTIFACTS_DIR:
|
||||
# Directory used to place artifacts.
|
||||
|
||||
# The pipeline is divided into stages. Usually, jobs in a given stage wait for
|
||||
# the preceding stages to complete before to start. However, we sometimes use
|
||||
# the "needs" keyword and express the DAG of jobs for more efficiency.
|
||||
# - We use setup and setup_baseline phases to download content outside of mfem
|
||||
# directory.
|
||||
# - Allocate/Release is where quartz resource are allocated/released once for all.
|
||||
variables:
|
||||
BUILD_ROOT: ${CI_BUILDS_DIR}/MFEM/${CI_PROJECT_NAME}_${CI_COMMIT_REF_SLUG}_${CI_PIPELINE_ID}
|
||||
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM
|
||||
REBASELINE: "NO"
|
||||
AUTOTEST: "NO"
|
||||
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
|
||||
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
|
||||
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.
|
||||
# - 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:
|
||||
- sub-pipelines
|
||||
- q_allocate_resources
|
||||
- q_build_and_test
|
||||
- q_release_resources
|
||||
- l_build_and_test
|
||||
- c_build_and_test
|
||||
- setup
|
||||
- baseline_check
|
||||
- baseline_to_autotest
|
||||
- baseline_publish
|
||||
|
||||
variables:
|
||||
CUSTOM_CI_BUILDS_DIR: "/usr/workspace/mfem/gitlab-runner"
|
||||
|
||||
# Trigger subpipelines:
|
||||
quartz-build-and-test:
|
||||
stage: sub-pipelines
|
||||
# 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:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
stage: setup
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/quartz-build-and-test.yml
|
||||
strategy: depend
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d "tpls" ]; then git clone ${TPLS_REPO}; fi
|
||||
- if [ ! -d "tests" ]; then git clone ${TESTS_REPO}; fi
|
||||
- cd tpls && git pull && cd ..
|
||||
- cd tests && git pull && cd ..
|
||||
- cd ${AUTOTEST_ROOT}
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
needs: []
|
||||
|
||||
quartz-baseline:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/quartz-baseline.yml
|
||||
strategy: depend
|
||||
.build_toss_3_x86_64_ib_script:
|
||||
script:
|
||||
- export THREADS=12
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test
|
||||
|
||||
lassen-build-and-test:
|
||||
stage: sub-pipelines
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/lassen-build-and-test.yml
|
||||
strategy: depend
|
||||
.build_toss_3_x86_64_ib_corona_script:
|
||||
script:
|
||||
- srun -p mi60 -t 15 -N 1 tests/gitlab/build_and_test
|
||||
|
||||
corona-build-and-test:
|
||||
stage: sub-pipelines
|
||||
# Lassen uses a different job scheduler (spectrum lsf) that does not
|
||||
# allow pre-allocation the same way slurm does.
|
||||
# We use pdebug queue on lassen to speed-up the allocation.
|
||||
# However this would not be scalable to multiple builds.
|
||||
.build_blueos_3_ppc64le_ib_script:
|
||||
script:
|
||||
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test
|
||||
|
||||
# Shared script for baseline and sample-run-baseline, the value of BASELINE_TEST
|
||||
# differentiates between the two tests.
|
||||
.baseline_script: &baseline_script |
|
||||
# locals
|
||||
_glob_err=${BASELINE_TEST}.err
|
||||
_base_diff=${BASELINE_TEST}-${SYS_TYPE}.diff
|
||||
_base_patch=${BASELINE_TEST}-${SYS_TYPE}.patch
|
||||
_base_out=${BASELINE_TEST}-${SYS_TYPE}.out
|
||||
# prepare
|
||||
cd ${BUILD_ROOT}
|
||||
ln -snf ${CI_PROJECT_DIR} mfem
|
||||
cd tests
|
||||
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
# run
|
||||
srun --nodes=1 -p pdebug ../runtest ../../mfem "${BASELINE_TEST} ${ADDITIONAL_DIR}"
|
||||
# post
|
||||
mkdir ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}
|
||||
if [[ -s ${_glob_err} ]]
|
||||
then
|
||||
echo "ERROR during ${BASELINE_TEST} execution";
|
||||
echo "Here is the ${_glob_err} file content";
|
||||
cat ${_glob_err}
|
||||
cp ${_glob_err} ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/${_glob_err}
|
||||
exit 1;
|
||||
elif [[ ! -f ${_base_patch} && ! -f ${_base_out} ]]
|
||||
then
|
||||
echo "Something went WRONG in ${BASELINE_TEST}:";
|
||||
echo "Either ${_base_patch} or ${_base_out} should exists";
|
||||
exit 1;
|
||||
elif [[ -f ${_base_patch} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: Differences found, patch generated"
|
||||
cp ${_base_patch} ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/${_base_patch}
|
||||
elif [[ -f ${_base_out} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: Differences found, replacement file generated"
|
||||
cp ${_base_out} ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/${_base_out}
|
||||
fi
|
||||
# _base_diff won't even exist if there is no difference.
|
||||
if [[ -f ${_base_diff} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: Relevant differences (filtered diff) ..."
|
||||
cat ${_base_diff}
|
||||
cp ${_base_diff} ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/${_base_diff}
|
||||
# We create a .err file, because that's how we signal that there was a diff.
|
||||
cp ${_base_diff} ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/gitlab-${BASELINE_TEST}-${SYS_TYPE}.err
|
||||
fi
|
||||
if [[ ! -s ${_base_diff} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: PASSED"
|
||||
true
|
||||
else
|
||||
echo "${BASELINE_TEST}: FAILED"
|
||||
false
|
||||
fi
|
||||
|
||||
# Actual templates for baseline checks
|
||||
.baselinecheck_mfem:
|
||||
stage: baseline_check
|
||||
variables:
|
||||
_AUTOTEST: $AUTOTEST
|
||||
trigger:
|
||||
include: .gitlab/corona-build-and-test.yml
|
||||
strategy: depend
|
||||
BASELINE_TEST: baseline
|
||||
ADDITIONAL_DIR: ${BUILD_ROOT}/tpls
|
||||
script:
|
||||
- *baseline_script
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- ${ARTIFACTS_DIR}
|
||||
allow_failure: true
|
||||
|
||||
.samplebaselinecheck_mfem:
|
||||
stage: baseline_check
|
||||
variables:
|
||||
BASELINE_TEST: sample-runs-baseline
|
||||
ADDITIONAL_DIR: ""
|
||||
script:
|
||||
- *baseline_script
|
||||
timeout: 4h
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- ${ARTIFACTS_DIR}
|
||||
allow_failure: true
|
||||
|
||||
# This job can only be manually triggered on a pipeline for master branch, or if
|
||||
# the pipeline was triggered with REBASELINE="YES"
|
||||
.rebaseline_mfem:
|
||||
stage: baseline_publish
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
when: manual
|
||||
script:
|
||||
- export PATCH_FILE=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}.patch
|
||||
- export FULL_FILE=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}.out
|
||||
- export DIFF_FILE=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}.diff
|
||||
- cd ${BUILD_ROOT}/tests
|
||||
- |
|
||||
if [[ ! -f "${DIFF_FILE}" ]]
|
||||
then
|
||||
echo "Nothing to be done: no relevant change in baseline"
|
||||
exit 0
|
||||
elif [[ -f "${PATCH_FILE}" ]]
|
||||
then
|
||||
patch "./baseline-${SYS_TYPE}.saved" < "${PATCH_FILE}"
|
||||
elif [[ -f "${FULL_FILE}t" ]]
|
||||
then
|
||||
cp "${FULL_FILE}" "./baseline-${SYS_TYPE}.saved"
|
||||
else
|
||||
echo "File missing: expected ${PATCH_FILE} or ${FULL_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
- git add baseline-${SYS_TYPE}.saved
|
||||
- git commit -m "${SYS_TYPE} rebaselined in GitLab pipeline ${CI_PIPELINE_ID}"
|
||||
- git push origin master
|
||||
|
||||
# The list on jobs is defined in machine-specific files.
|
||||
include:
|
||||
- local: .gitlab/quartz.yml
|
||||
- local: .gitlab/lassen.yml
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
Finite Element Discretization Library
|
||||
__
|
||||
_ __ ___ / _| ___ _ __ ___
|
||||
| '_ ` _ \ | |_ / _ \| '_ ` _ \
|
||||
| | | | | || _|| __/| | | | | |
|
||||
|_| |_| |_||_| \___||_| |_| |_|
|
||||
|
||||
https://mfem.org
|
||||
|
||||
|
||||
This directory contains most of the GitLab CI configuration. MFEM runs both PR
|
||||
and nightly testing on GitLab.
|
||||
|
||||
# Structure
|
||||
|
||||
## Top level
|
||||
|
||||
The root configuration file is `.gitlab-ci.yml` at the root of MFEM repo.
|
||||
This file only defines one stage, in which we trigger several
|
||||
sub-pipelines.
|
||||
|
||||
We use sub-pipelines to isolate the test for one combination of `machine`
|
||||
and `test type`.
|
||||
|
||||
Machines typically include:
|
||||
|
||||
* Quartz: Intel bi-socket x86
|
||||
* Lassen: Power9 + Nvidia GPU
|
||||
* Corona: AMD GPU
|
||||
|
||||
Test types include:
|
||||
|
||||
* Build and test: Spack driven build of dependencies, mfem build, mfem
|
||||
test
|
||||
* Baseline: Script driven build of dependencies, thorough testing
|
||||
|
||||
⚠️ The sub-pipeline design allows to add a new machine or a new test type without
|
||||
altering the scheduling, execution and displaying of the others.
|
||||
|
||||
## Sub-pipelines
|
||||
|
||||
Each file is this directory is the root configuration file for one
|
||||
sub-pipeline. The naming reflects the corresponding couple (`machine`,
|
||||
`test_type`).
|
||||
|
||||
Those files define the *stages* and the *jobs* for the sub-pipeline. They
|
||||
also contain any configuration that cannot be shared. For the most part
|
||||
though, the configuration is shared and is placed in `.gitlab/configs`.
|
||||
|
||||
We try to keep scripts out of the CI config and share them among similar
|
||||
jobs. They are gathered in `.gitlab/scripts`.
|
||||
|
||||
## Scripts
|
||||
|
||||
Scripts specific to the CI only are in `.gitlab/scripts`. It is best practice
|
||||
to keep scripts outside the CI configuration (no bash scripts embedded in a
|
||||
yaml file) because it helps with readability, maintenance and also with
|
||||
transition to another CI system.
|
||||
|
||||
⚠️ Most of the scripts there are driven by environment variables and do not have a
|
||||
usage function. This should be improved.
|
||||
|
||||
|
||||
# More testing
|
||||
|
||||
## Adding a new target to a build_and_test pipeline
|
||||
|
||||
`build_and_test` pipelines rely on Spack to install dependencies. Spack is
|
||||
driven by Uberenv which helps freezing Spack configuration: the goal being to
|
||||
point to specific commit in Spack and isolate its configuration so that it is
|
||||
not influenced by the user environment. More documentation about this can be
|
||||
found in `tests/gitlab`.
|
||||
|
||||
In the end, the MFEM target for which to build the dependencies is expressed
|
||||
with a spack spec of MFEM, within the limits permitted by the MFEM spack
|
||||
package.
|
||||
|
||||
In any build-and-test sub-pipeline a job basically consists in defining the
|
||||
spack spec to use. Adding a job on quartz for example resumes to:
|
||||
|
||||
```yaml
|
||||
<job_name>:
|
||||
variables:
|
||||
SPEC: "<spack_spec>"
|
||||
extends: .build_and_test_on_quartz
|
||||
```
|
||||
|
||||
The remaining and non trivial work is to make sure this spec is working. To
|
||||
test a spec before adding it, or reproduce a CI configuration, please refer to
|
||||
`tests/gitlab/reproduce-ci-jobs-interactively.md`.
|
||||
|
||||
⚠️ It is assumed that the spack spec applies to `mfem@develop`. That's why in the
|
||||
CI all the specs start with the compiler or the variants to apply to mfem. The
|
||||
mechanism still works with a full spec.
|
||||
@@ -1,44 +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.
|
||||
|
||||
# We define the following GitLab pipeline variables:
|
||||
variables:
|
||||
|
||||
# The path to the shared resource between all jobs. For example, external
|
||||
# repositories like 'tests' and 'tpls' are cloned here. Also, 'tpls' is built
|
||||
# once for all targets, so that build happen here. The BUILD_ROOT is unique to
|
||||
# the pipeline, preventing any form of concurrency with other pipelines. This
|
||||
# also means that the BUILD_ROOT directory will never be cleaned.
|
||||
# TODO: add a clean-up mechanism
|
||||
BUILD_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}/${CI_PROJECT_NAME}_${CI_COMMIT_REF_SLUG}_${CI_PIPELINE_ID}
|
||||
|
||||
# On LLNL's quartz, there is only one allocation shared among jobs in order to
|
||||
# save time and resource. This allocation has to be uniquely named so that we
|
||||
# are sure to retrieve it.
|
||||
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
|
||||
|
||||
# Defines the default choice for updating the saved baseline results. By default
|
||||
# the baseline can only be updated from the master branch. This variable offers
|
||||
# the option to manually ask for rebaselining from another branch if necessary.
|
||||
_REBASELINE: "NO"
|
||||
_AUTOTEST: "NO"
|
||||
|
||||
# Git repositories used in the pipeline
|
||||
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
|
||||
|
||||
# Directory used to place artifacts.
|
||||
ARTIFACTS_DIR: artifacts
|
||||
SLURM_OVERLAP: 1
|
||||
|
||||
|
||||
@@ -1,54 +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.
|
||||
|
||||
# GitLab pipeline configuration for the Corona machine at LLNL
|
||||
variables:
|
||||
MACHINE_NAME: corona
|
||||
|
||||
.on_corona:
|
||||
tags:
|
||||
- shell
|
||||
- corona
|
||||
rules:
|
||||
# Don’t run corona jobs if...
|
||||
# Note: This makes corona an "opt-in" machine. To activate builds on corona
|
||||
# for a given GitLab clone of MFEM, go to Setting/CI-CD/variables, and set
|
||||
# "ON_CORONA" to "ON". An LC account on for corona is required to trigger a
|
||||
# pipeline there.
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_cnone/ || $ON_CORONA != "ON"'
|
||||
when: never
|
||||
# Don’t run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
# Always release resource
|
||||
- if: '$CI_JOB_NAME =~ /release_resource/'
|
||||
when: always
|
||||
# Default is to run if previous stage succeeded
|
||||
- when: on_success
|
||||
|
||||
# Spack helped builds
|
||||
# Generic corona build job, extending build script
|
||||
.build_and_test_on_corona:
|
||||
extends: [.on_corona]
|
||||
stage: build_and_test
|
||||
script:
|
||||
- export THREADS=12
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
|
||||
@@ -1,43 +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.
|
||||
|
||||
# GitLab pipelines configurations for the Lassen machine at LLNL
|
||||
variables:
|
||||
MACHINE_NAME: lassen
|
||||
|
||||
.on_lassen:
|
||||
tags:
|
||||
- shell
|
||||
- lassen
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_lnone/ || $ON_LASSEN == "OFF"' #run except if ...
|
||||
when: never
|
||||
# Don't run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
- when: on_success
|
||||
|
||||
# Lassen uses a different job scheduler (spectrum lsf) that does not allow
|
||||
# pre-allocation the same way slurm does. We use pdebug queue on lassen
|
||||
# to speed-up the allocation. However this would not be scalable to
|
||||
# multiple builds.
|
||||
.build_and_test_on_lassen:
|
||||
extends: [.on_lassen]
|
||||
stage: build_and_test
|
||||
script:
|
||||
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
needs: [setup]
|
||||
@@ -1,50 +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.
|
||||
|
||||
# GitLab pipelines configurations for the Quartz machine at LLNL
|
||||
variables:
|
||||
MACHINE_NAME: quartz
|
||||
|
||||
.on_quartz:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
rules:
|
||||
# Don't run quartz jobs if...
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"'
|
||||
when: never
|
||||
# Don't run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
# Always release resource
|
||||
- if: '$CI_JOB_NAME =~ /release_resource/'
|
||||
when: always
|
||||
# Default is to run if previous stage succeeded
|
||||
- when: on_success
|
||||
|
||||
# Spack helped builds
|
||||
# Generic quartz build job, extending build script
|
||||
.build_and_test_on_quartz:
|
||||
extends: [.on_quartz]
|
||||
stage: build_and_test
|
||||
script:
|
||||
- export THREADS=12
|
||||
- echo ${ALLOC_NAME}
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- echo ${JOBID}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
|
||||
|
||||
@@ -1,42 +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.
|
||||
|
||||
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
|
||||
# when call the runtest script in MFEM test repo.
|
||||
# Note: the value must be consistent with what setup_baseline does.
|
||||
variables:
|
||||
TPLS_DIR: ${BUILD_ROOT}/tpls
|
||||
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_baseline
|
||||
|
||||
# The setup_baseline job 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
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- echo "BUILD_ROOT ${BUILD_ROOT}"
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d "tpls" ]; then git clone ${TPLS_REPO}; fi
|
||||
- if [ ! -d "tests" ]; then git clone ${TESTS_REPO}; fi
|
||||
- cd tpls && git pull && cd ..
|
||||
- cd tests && git pull origin && cd ..
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
@@ -1,34 +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.
|
||||
|
||||
variables:
|
||||
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_build_and_test
|
||||
|
||||
# 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:
|
||||
- echo "BUILD_ROOT ${BUILD_ROOT}"
|
||||
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
|
||||
- if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi
|
||||
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
|
||||
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
|
||||
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
|
||||
- cd autotest && git pull && cd ..
|
||||
|
||||
@@ -1,63 +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.
|
||||
|
||||
stages:
|
||||
- setup
|
||||
- allocate_resource
|
||||
- build_and_test
|
||||
- release_resource_and_report
|
||||
|
||||
# Slurm shared allocation
|
||||
allocate_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_corona
|
||||
stage: allocate_resource
|
||||
script:
|
||||
- salloc --exclusive --nodes=1 --partition=mi60 --time=30 --no-shell --job-name=${ALLOC_NAME}
|
||||
timeout: 6h
|
||||
needs: [setup]
|
||||
|
||||
# Build and test jobs, simply provide a spec
|
||||
rocm_gcc_8.3.1:
|
||||
variables:
|
||||
SPEC: "@develop%gcc@8.3.1+rocm amdgpu_target=gfx906"
|
||||
extends: .build_and_test_on_corona
|
||||
needs: [allocate_resource]
|
||||
|
||||
# Release slurm allocation
|
||||
release_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
|
||||
needs: [rocm_gcc_8.3.1]
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_corona
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/corona-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
@@ -9,30 +9,25 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
stages:
|
||||
- setup
|
||||
- build_and_test
|
||||
- report
|
||||
# GitLab pipelines configurations for the Lassen machine at LLNL
|
||||
|
||||
.on_lassen:
|
||||
tags:
|
||||
- shell
|
||||
- lassen
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_lnone/ || $ON_LASSEN == "OFF"' #run except if ...
|
||||
when: never
|
||||
- when: on_success
|
||||
|
||||
# Spack helped builds
|
||||
# Generic lassen build job, extending build script
|
||||
.build_and_test_on_lassen:
|
||||
extends: [.build_blueos_3_ppc64le_ib_script, .on_lassen]
|
||||
stage: l_build_and_test
|
||||
needs: []
|
||||
|
||||
opt_mpi_cuda_xl_16_1_1_8:
|
||||
variables:
|
||||
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=70"
|
||||
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=sm_70"
|
||||
extends: .build_and_test_on_lassen
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_lassen
|
||||
stage: report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_lassen
|
||||
stage: report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/lassen-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
@@ -1,64 +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.
|
||||
|
||||
variables:
|
||||
BASELINE_TEST: baseline
|
||||
|
||||
stages:
|
||||
- setup
|
||||
- baseline_check
|
||||
- baseline_report
|
||||
- baseline_publish
|
||||
|
||||
baselinecheck_mfem_intel_quartz:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_check
|
||||
script:
|
||||
- .gitlab/scripts/baseline
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- ${ARTIFACTS_DIR}
|
||||
allow_failure: true
|
||||
|
||||
report_baseline:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_report
|
||||
script:
|
||||
- cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
- mkdir -p ${MACHINE_NAME}
|
||||
- rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
|
||||
- rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
|
||||
- cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
|
||||
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
|
||||
- |
|
||||
if [[ -f ${rundir}/*.err ]]
|
||||
then
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/*.err
|
||||
cp ${rundir}/*.err ${rundir}/autotest-email.html
|
||||
fi
|
||||
- git add ${rundir}
|
||||
- git commit -am "GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} with intel ($(date +%Y-%m-%d))"
|
||||
- git push origin master
|
||||
|
||||
baselinepublish_mfem_quartz:
|
||||
extends: [.on_quartz]
|
||||
stage: baseline_publish
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
|
||||
when: manual
|
||||
script:
|
||||
- .gitlab/scripts/rebaseline
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/quartz-config.yml
|
||||
- local: .gitlab/configs/setup-baseline.yml
|
||||
@@ -1,95 +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.
|
||||
|
||||
stages:
|
||||
- setup
|
||||
- allocate_resource
|
||||
- build_and_test
|
||||
- release_resource_and_report
|
||||
|
||||
# Allocate
|
||||
allocate_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
stage: allocate_resource
|
||||
script:
|
||||
- salloc --exclusive --nodes=1 --partition=pdebug --time=30 --no-shell --job-name=${ALLOC_NAME}
|
||||
timeout: 6h
|
||||
|
||||
# GitLab jobs for the Quartz machine at LLNL
|
||||
debug_ser_gcc_4_9_3:
|
||||
variables:
|
||||
SPEC: "%gcc@4.9.3 +debug~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
debug_ser_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +debug~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
debug_par_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +debug+mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_ser_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 ~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0_sundials:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +sundials"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0_petsc:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +petsc ^petsc+mumps"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0_pumi:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +pumi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
# Release
|
||||
release_resource:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
|
||||
|
||||
# Jobs report
|
||||
report_job_success:
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_success
|
||||
|
||||
report_job_failure:
|
||||
extends: .on_quartz
|
||||
stage: release_resource_and_report
|
||||
script:
|
||||
- .gitlab/scripts/report_build_and_test_failure
|
||||
|
||||
include:
|
||||
- local: .gitlab/configs/common.yml
|
||||
- local: .gitlab/configs/quartz-config.yml
|
||||
- local: .gitlab/configs/setup-build-and-test.yml
|
||||
@@ -0,0 +1,165 @@
|
||||
# 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.
|
||||
|
||||
# GitLab pipelines configurations for the Quartz machine at LLNL
|
||||
|
||||
.on_quartz:
|
||||
tags:
|
||||
- shell
|
||||
- quartz
|
||||
rules:
|
||||
# Don’t run quartz jobs if...
|
||||
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"'
|
||||
when: never
|
||||
# Don’t run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /update_autotest/ && $AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Don’t run autotest update if...
|
||||
- if: '$CI_JOB_NAME =~ /q_report/ && $AUTOTEST != "YES"'
|
||||
when: never
|
||||
# Report success on success status
|
||||
- if: '$CI_JOB_NAME =~ /q_report_success/ && $AUTOTEST == "YES"'
|
||||
when: on_success
|
||||
# Report failure on failure status
|
||||
- if: '$CI_JOB_NAME =~ /q_report_failure/ && $AUTOTEST == "YES"'
|
||||
when: on_failure
|
||||
# Always release resources
|
||||
- if: '$CI_JOB_NAME =~ /release_resources/'
|
||||
when: always
|
||||
# Default is to run if previous stage succeeded
|
||||
- when: on_success
|
||||
|
||||
# Allocate
|
||||
q_allocate_resources:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
stage: q_allocate_resources
|
||||
script:
|
||||
- salloc --exclusive --nodes=1 --partition=pdebug --time=30 --no-shell --job-name=${ALLOC_NAME}
|
||||
timeout: 6h
|
||||
|
||||
# Release
|
||||
q_release_resources:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
stage: q_release_resources
|
||||
script:
|
||||
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
|
||||
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
|
||||
|
||||
# Release
|
||||
q_report_success:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
stage: q_release_resources
|
||||
script:
|
||||
- echo "Can only run if all the quartz jobs passed"
|
||||
- rundir="gitlab/$(date +%Y-%m-%d)-github-${CI_COMMIT_REF_SLUG}"
|
||||
- cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
- mkdir -p ${rundir}
|
||||
- echo "The Quartz jobs were successful" > ${rundir}/gitlab.out
|
||||
- git add ${rundir}
|
||||
- git commit -am "Gitlab CI log for baseline on quartz with intel ($(date +%Y-%m-%d))"
|
||||
- git push origin master
|
||||
|
||||
q_report_failure:
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
extends: .on_quartz
|
||||
stage: q_release_resources
|
||||
script:
|
||||
- echo "Runs if there was at least one failure on quartz"
|
||||
- rundir="gitlab/$(date +%Y-%m-%d)-github-${CI_COMMIT_REF_SLUG}"
|
||||
- cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
- mkdir -p ${rundir}
|
||||
- echo "There was an error while running CI on Quartz" > ${rundir}/gitlab.err
|
||||
- cp ${rundir}/gitlab.err ${rundir}/autotest-email.html
|
||||
- git add ${rundir}
|
||||
- git commit -am "Gitlab CI log for baseline on quartz with intel ($(date +%Y-%m-%d))"
|
||||
- git push origin master
|
||||
|
||||
# Spack helped builds
|
||||
# Generic quartz build job, extending build script
|
||||
.build_and_test_on_quartz:
|
||||
extends: [.build_toss_3_x86_64_ib_script, .on_quartz]
|
||||
stage: q_build_and_test
|
||||
|
||||
# Build MFEM
|
||||
debug_ser_gcc_4_9_3:
|
||||
variables:
|
||||
SPEC: "%gcc@4.9.3 +debug~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
debug_ser_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +debug~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
debug_par_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +debug+mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_ser_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 ~mpi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0_sundials:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +sundials"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0_petsc:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +petsc ^petsc+mumps"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
opt_par_gcc_6_1_0_pumi:
|
||||
variables:
|
||||
SPEC: "%gcc@6.1.0 +pumi"
|
||||
extends: .build_and_test_on_quartz
|
||||
|
||||
# Baseline
|
||||
baselinecheck_mfem_intel_quartz:
|
||||
extends: [.baselinecheck_mfem, .on_quartz]
|
||||
needs: [setup]
|
||||
|
||||
update_autotest:
|
||||
extends: [.on_quartz]
|
||||
needs: [baselinecheck_mfem_intel_quartz]
|
||||
stage: baseline_to_autotest
|
||||
script:
|
||||
- rundir="quartz/$(date +%Y-%m-%d)-github-${CI_COMMIT_REF_SLUG}"
|
||||
- cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
- mkdir -p ${rundir}
|
||||
- cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
|
||||
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
|
||||
- |
|
||||
if [[ -f ${rundir}/*.err ]]
|
||||
then
|
||||
cp ${rundir}/*.err ${rundir}/autotest-email.html
|
||||
fi
|
||||
- git add ${rundir}
|
||||
- git commit -am "Gitlab CI log for baseline on quartz with intel ($(date +%Y-%m-%d))"
|
||||
- git push origin master
|
||||
|
||||
baselinepublish_mfem_quartz:
|
||||
extends: [.on_quartz, .rebaseline_mfem]
|
||||
needs: [baselinecheck_mfem_intel_quartz]
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
# locals
|
||||
glob_err=${BASELINE_TEST}.err
|
||||
base=${BASELINE_TEST}-${SYS_TYPE}
|
||||
base_diff=${base}.diff
|
||||
base_patch=${base}.patch
|
||||
base_out=${base}.out
|
||||
artifacts_path=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}
|
||||
|
||||
# prepare
|
||||
cd ${BUILD_ROOT}
|
||||
ln -snf ${CI_PROJECT_DIR} mfem
|
||||
cd tests
|
||||
[[ -d _${BASELINE_TEST} ]] && rm -rf _${BASELINE_TEST}
|
||||
mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "quartz" || "${MACHINE_NAME}" == "ruby" ]]; then
|
||||
srun --nodes=1 -p pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
srun --nodes=1 -t 60 -p mi60 ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
|
||||
lalloc 1 -q pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
fi
|
||||
|
||||
# post
|
||||
mkdir ${artifacts_path}
|
||||
|
||||
if [[ -s ${glob_err} ]]
|
||||
then
|
||||
echo "ERROR during ${BASELINE_TEST} execution";
|
||||
echo "Here is the ${glob_err} file content";
|
||||
cat ${glob_err}
|
||||
cp ${glob_err} ${artifacts_path}/${glob_err}
|
||||
exit 1;
|
||||
elif [[ ! -f ${base_patch} && ! -f ${base_out} ]]
|
||||
then
|
||||
echo "Something went WRONG in ${BASELINE_TEST}:";
|
||||
echo "Either ${base_patch} or ${base_out} should exists";
|
||||
exit 1;
|
||||
elif [[ -f ${base_patch} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: Differences found, patch generated"
|
||||
cp ${base_patch} ${artifacts_path}/${base_patch}
|
||||
elif [[ -f ${base_out} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: Differences found, replacement file generated"
|
||||
cp ${base_out} ${artifacts_path}/${base_out}
|
||||
fi
|
||||
|
||||
# base_diff won't even exist if there is no difference.
|
||||
if [[ -f ${base_diff} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: Relevant differences (filtered diff) ..."
|
||||
cat ${base_diff}
|
||||
cp ${base_diff} ${artifacts_path}/${base_diff}
|
||||
# We create a .err file, because that's how we signal that there was a diff.
|
||||
cp ${base_diff} ${artifacts_path}/gitlab-${BASELINE_TEST}-${MACHINE_NAME}.err
|
||||
fi
|
||||
|
||||
if [[ ! -s ${base_diff} ]]
|
||||
then
|
||||
echo "${BASELINE_TEST}: PASSED"
|
||||
true
|
||||
else
|
||||
echo "${BASELINE_TEST}: FAILED"
|
||||
false
|
||||
fi
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
# There will be collision between corona and quartz baselines.
|
||||
# Once the corresponding files have been generated, we can switch to machine
|
||||
# specific ref.
|
||||
ARTIFACT_PATH=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}
|
||||
#ARTIFACT_PATH=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/baseline-${SYS_TYPE}-${MACHINE_NAME}
|
||||
|
||||
PATCH_FILE=${ARTIFACT_PATH}.patch
|
||||
FULL_FILE=${ARTIFACT_PATH}.out
|
||||
DIFF_FILE=${ARTIFACT_PATH}.diff
|
||||
|
||||
# There will be collision between corona and quartz baselines.
|
||||
# Once the corresponding files have been generated, we can switch to machine
|
||||
# specific ref.
|
||||
SAVED_NAME=baseline-${SYS_TYPE}.saved
|
||||
#SAVED_NAME=baseline-${SYS_TYPE}-${MACHINE_NAME}.saved
|
||||
|
||||
cd ${BUILD_ROOT}/tests
|
||||
|
||||
if [[ ! -f "${DIFF_FILE}" ]]
|
||||
then
|
||||
echo "Nothing to be done: no relevant change in baseline"
|
||||
exit 0
|
||||
elif [[ -f "${PATCH_FILE}" ]]
|
||||
then
|
||||
patch "${SAVED_NAME}" < "${PATCH_FILE}"
|
||||
elif [[ -f "${FULL_FILE}" ]]
|
||||
then
|
||||
cp "${FULL_FILE}" "${SAVED_NAME}"
|
||||
else
|
||||
echo "File missing: expected ${PATCH_FILE} or ${FULL_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git add "${SAVED_NAME}"
|
||||
git commit -m "${SYS_TYPE} (${MACHINE_NAME}) rebaselined in GitLab pipeline ${CI_PIPELINE_ID}"
|
||||
git push origin master
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
echo "Runs if there was at least one failure on ${MACHINE_NAME}"
|
||||
|
||||
cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
mkdir -p ${MACHINE_NAME}
|
||||
|
||||
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
|
||||
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
|
||||
|
||||
echo "There was an error while running CI on ${MACHINE_NAME}" > ${rundir}/gitlab.err
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
|
||||
|
||||
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
|
||||
|
||||
cp ${rundir}/gitlab.err ${rundir}/autotest-email.html
|
||||
|
||||
git pull
|
||||
git add ${rundir}
|
||||
git commit -am "${msg}"
|
||||
git push origin master
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
echo "Can only run if all the ${MACHINE_NAME} jobs passed"
|
||||
|
||||
cd ${AUTOTEST_ROOT}/autotest && git pull
|
||||
mkdir -p ${MACHINE_NAME}
|
||||
|
||||
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
|
||||
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
|
||||
|
||||
echo "The ${MACHINE_NAME} jobs were successful" > ${rundir}/gitlab.out
|
||||
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
|
||||
|
||||
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
|
||||
|
||||
git pull
|
||||
git add ${rundir}
|
||||
git commit -am "${msg}"
|
||||
git push origin master
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/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.
|
||||
|
||||
# This script takes a seed for a directory name and appends it with a counter
|
||||
# incremented until it can create a new directory with it.
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# Expects 1 argument: a string that is use as a seed for the directory name.
|
||||
#
|
||||
# > rundir="desired_name"
|
||||
# > rundir=$(./safe_create_rundir $rundir)
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
|
||||
rundir=${1:-""}
|
||||
if [[ -z ${rundir} ]]; then
|
||||
>&2 echo "The script expects a string as argument for directory creation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! mkdir ${rundir}; then
|
||||
n=1
|
||||
while ! mkdir ${rundir}_${n}
|
||||
do
|
||||
n=$((n+1))
|
||||
done
|
||||
rundir=${rundir}_${n}
|
||||
fi
|
||||
|
||||
echo $rundir
|
||||
+469
@@ -0,0 +1,469 @@
|
||||
# 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
|
||||
@@ -8,127 +8,40 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.3.1 (development)
|
||||
Version 4.2.1 (development)
|
||||
===========================
|
||||
- Added support for mesh preprocessing to resolve fine scale problem data
|
||||
before simulation. This feature uses adaptive mesh refinement to control the
|
||||
associated data oscillation error. See the new Example 30/30p.
|
||||
- Added initial support for GPU-accelerated versions of PETSc that works with
|
||||
MFEM_USE_CUDA if PETSc has been configured with CUDA support. Examples 1 and 9
|
||||
in the examples/petsc directory have been modified to work with --device cuda.
|
||||
Examples with GAMG (ex1p) and SLEPc (ex11p) are also provided.
|
||||
|
||||
- Switched from Artistic Style (astyle) version 2.05.1 to version 3.1 for code
|
||||
formatting. See the "make style" target.
|
||||
- Memory management:
|
||||
* Added method Device::SetMemoryTypes that can be used to change the default
|
||||
host and device MemoryTypes before Device setup.
|
||||
* In class MemoryManager, added methods GetDualMemoryType and
|
||||
SetDualMemoryType; dual MemoryTypes are used to determine the second
|
||||
MemoryType (host or device) when only one MemoryType is specified in methods
|
||||
of class Memory.
|
||||
* Added Memory constructor for setting both the host and device MemoryTypes.
|
||||
* Switched the default behavior of device memory allocations so that they
|
||||
are deferred until the device pointer is needed.
|
||||
* Added a second Umpire device MemoryType, DEVICE_UMPIRE_2, with
|
||||
corresponding allocator that can be set with the method
|
||||
MemoryManager::SetUmpireDevice2AllocatorName.
|
||||
* Added HOST_PINNED MemoryType and a pinned host allocator for CUDA and HIP.
|
||||
|
||||
- Split the fem/fe.?pp files into separate files in the new fem/fe/ directory
|
||||
to simplify and clarify the organization of FiniteElement classes.
|
||||
- Added support for Caliper: a library to integrate performance profiling
|
||||
capabilities into applications. See examples/caliper for more details.
|
||||
|
||||
- Added support for hr-adaptivity using TMOP-based error estimator.
|
||||
- Added support for explicit vectorization in the high-performance templated
|
||||
code for Fujitsu's A64FX ARM microprocessor architecture.
|
||||
|
||||
- Coefficient::SetTime now propagates the new time into internally stored
|
||||
Coefficient objects.
|
||||
- Added AlgebraicCeedSolver that does matrix-free algebraic p-multigrid for
|
||||
diffusion problems with the Ceed backend.
|
||||
|
||||
- Added initial support for google-benchmarks in the tests/benchmarks directory.
|
||||
It can be enabled with MFEM_USE_BENCHMARK=YES.
|
||||
|
||||
- Added Binder (mybinder.org) configuration files for C++ MFEM Jupyter Notebooks
|
||||
with inline GLVis visualization as well as a new examples/jupyter/ directory
|
||||
with a sample notebook based on Example 1. Implementation based on xeus-cling,
|
||||
github.com/jupyter-xeus/xeus-cling + xeus-glvis, github.com/GLVis/xeus-glvis.
|
||||
|
||||
- Added 'double' atomicAdd implementation for previous versions of CUDA.
|
||||
|
||||
- Adding lowest order Nedelec and Raviart-Thomas basis functions on wedge
|
||||
shaped elements.
|
||||
|
||||
- Added initial support for meshes with pyramidal elements, including several
|
||||
pyramidal meshes in the data/ directory and support for the lowest order H1,
|
||||
Nedelec, Raviart-Thomas, and L2 basis functions on pyramids.
|
||||
|
||||
- Updated the hypre interface according to changes in hypre-2.22.1. The ADS
|
||||
solver is now fully working on GPUs.
|
||||
|
||||
- Tetrahedral meshes no longer need to be reordered to support high order
|
||||
Nedelec basis functions. This will allow future support for Nedelec basis
|
||||
functions on wedges and pyramids which are not amenable to reordering. The
|
||||
ReorientTetMesh method of the Mesh and ParMesh classes has been deprecated.
|
||||
|
||||
- Gmsh meshes where all elements have zero physical tag (the default Gmsh
|
||||
output format if no physical groups are defined) are now successfully loaded,
|
||||
and elements are reassigned attribute number 1.
|
||||
|
||||
- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the
|
||||
hierarchy of spaces created by the element-based AMG (AMGe) methodology in
|
||||
ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the
|
||||
miniapps/parelag directory for more details.
|
||||
|
||||
- Fixed several MinGW build issues on Windows.
|
||||
|
||||
- Remove the 'u' flag in the ar command, to update all files in the archive,
|
||||
avoiding file name collisions from different subdirectories.
|
||||
|
||||
- Added initial TMOP-based capabilities for surface fitting and tangential
|
||||
relaxation in the mesh-optimizer and pmesh-optimizer miniapps.
|
||||
|
||||
|
||||
Version 4.3, released on July 29, 2021
|
||||
======================================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Variable order spaces, p- and hp-refinement. This is the initial (serial)
|
||||
support for variable-order FiniteElementCollection and FiniteElementSpace.
|
||||
The new method FiniteElementSpace::SetElementOrder can be called to set an
|
||||
arbitrary order for each mesh element. The conforming interpolation matrix
|
||||
will now automatically constrain p- and hp- interfaces, enabling general
|
||||
hp-refinement in both 2D and 3D, on uniform or mixed NC meshes. Support for
|
||||
parallel variable-order spaces will follow shortly.
|
||||
|
||||
- Extended the support for field transfer between high-order and low-order
|
||||
refined finite element spaces to include: dual fields and H1 fields (both
|
||||
primary and dual). These are illustrated in the lor-transfer miniapp.
|
||||
|
||||
- Improved libCEED integration, including support for VectorCoefficient,
|
||||
ConvectionIntegrator, and VectorConvectionNLFIntegrator with libCEED backends.
|
||||
|
||||
- Extending support for L2 basis functions using MapTypes VALUE and INTEGRAL in
|
||||
linear interpolators and GridFunction "GetValue" methods.
|
||||
|
||||
- Changed the interface for the error estimator and implemented the Kelly error
|
||||
indicator for scalar-valued problems, supported in serial and parallel builds.
|
||||
|
||||
- Added support for the "BR2" discontinuous Galerkin discretization for
|
||||
diffusion via DGDiffusionBR2Integrator (see Example 14/14p).
|
||||
|
||||
- Added convective and skew-symmetric integrators for the nonlinear term in the
|
||||
Navier-Stokes equations.
|
||||
|
||||
- Added new classes DenseSymmetricMatrix and SymmetricMatrixCoefficient for
|
||||
efficient evaluation of symmetric matrix coefficients. This replaces the now
|
||||
deprecated EvalSymmetric in MatrixCoefficient. Added DiagonalMatrixCoefficient
|
||||
for clarity, which is a typedef of VectorCoefficient.
|
||||
|
||||
- Added support for nonscalar coefficient with VectorDiffusionIntegrator.
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added support for AMG preconditioners on GPUs based on the hypre library
|
||||
(version 2.22.0 or later). These include BoomerAMG, AMS and ADS and most
|
||||
MFEM examples that use hypre have been ported to support this functionality.
|
||||
The GPU preconditioners require that both hypre and MFEM are built with CUDA
|
||||
support. Hypre builds with CUDA and unified memory are also supported and
|
||||
can be used with `-d cuda:uvm` as a command-line option.
|
||||
|
||||
- Added support for AMG preconditioners for non-symmetric systems (e.g.
|
||||
advection-dominated problems) using hypre's approximate ideal restriction
|
||||
(AIR) AMG. Requires hypre version 2.14.0 or newer. Usage is illustrated in
|
||||
example 9/9p.
|
||||
|
||||
- Added new functionality for constructing low-order refined discretizations and
|
||||
solvers, see the LORDiscretization and LORSolver classes. A new basis type for
|
||||
H(curl) and H(div) spaces is introduced to give spectral equivalence. This
|
||||
functionality is illustrated in the LOR solvers miniapp in miniapps/solvers.
|
||||
|
||||
- Generalized the Multigrid class to support non-geometric multigrid. Previous
|
||||
functionality, based on FiniteElementSpaceHierarchy, is now available in the
|
||||
derived class GeometricMultigrid.
|
||||
- Introduced new options for the mesh-explorer miniapp to visualize the actual
|
||||
element attributes in parallel meshes while retaining the visualization of
|
||||
the domain decomposition.
|
||||
|
||||
- Introduced solver interface for linear problems with constraints, a few
|
||||
concrete solvers that implement the interface, and a demonstration of their
|
||||
@@ -139,18 +52,19 @@ Linear and nonlinear solvers
|
||||
as described in Barker and Kolev 2020 (https://doi.org/10.1002/nla.2348). See
|
||||
Example 3p and linalg/auxiliary.?pp.
|
||||
|
||||
- Improved interface for using the Ginkgo library, including: support for matrix-
|
||||
free operators in Ginkgo solvers, new wrappers for Ginkgo preconditioners, HIP
|
||||
support, and reduction of unnecessary data copies.
|
||||
- Added a new miniapp block-solvers that compares the performance of various
|
||||
solvers for mixed finite element discretization of the second order scalar
|
||||
elliptic equations. Currently available solvers in the miniapp include a
|
||||
block-diagonal preconditioner that is based on approximate Schur complement
|
||||
(implemented in ex5p), and a newly implemented solver DivFreeSolver, which
|
||||
exploits a multilevel decomposition of the Raviart-Thomas space and its
|
||||
divergence-free subspace. See the miniapps/solvers directory for more details.
|
||||
|
||||
- Added initial support for hypre's mixed integer (mixedint) capability, which
|
||||
uses different data types for local and global indices in order to save memory
|
||||
in large problems. This capability requires that hypre was configured with the
|
||||
--enable-mixedint option. Note that this option is currently tested only in
|
||||
ex1p, ex3p, and ex4p, and may not work in more general settings.
|
||||
- Added a new miniapp for computing (signed) distance functions to a point
|
||||
source or zero level set. See miniapps/shifted/distance.cpp.
|
||||
|
||||
- Added AlgebraicCeedSolver that does matrix-free algebraic p-multigrid for
|
||||
diffusion problems with the Ceed backend.
|
||||
- Added matrix-free GPU-enabled implementations of GradientInterpolator and
|
||||
IdentityInterpolator.
|
||||
|
||||
- Added interface to MUMPS direct solver. Its usage is demonstrated in ex25p.
|
||||
See http://mumps.enseeiht.fr/ for more details. Supported versions >= 5.1.1.
|
||||
@@ -158,17 +72,6 @@ Linear and nonlinear solvers
|
||||
- Added three ESDIRK time integrators: implicit trapezoid rule, L-stable
|
||||
ESDIRK-32, and A-stable ESDIRK-33.
|
||||
|
||||
- Implemented a variable step-size IMEX (VSSIMEX) method for the Navier miniapp.
|
||||
|
||||
- Implemented an adaptive linear solver tolerance option for NewtonSolver based
|
||||
on the algorithm of Eisenstat and Walker.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added support for reading high-order Lagrange meshes in VTK format. Arbitrary-
|
||||
orders and all element types are supported. See the VTK blog for more info:
|
||||
https://blog.kitware.com/wp-content/uploads/2018/09/Source_Issue_43.pdf.
|
||||
|
||||
- Introduced a new non-conforming mesh format that fixes known inconsistencies
|
||||
of legacy "MFEM mesh v1.1" NC format and works consistently in both serial and
|
||||
parallel. ParMesh::ParPrint can now print non-conforming AMR meshes that can
|
||||
@@ -177,26 +80,109 @@ Meshing improvements
|
||||
NC data files are compatible with serial code, e.g., can be viewed with serial
|
||||
GLVis. Loading of legacy NC mesh files is still supported.
|
||||
|
||||
- Added FMS support (https://github.com/CEED/FMS) to mfem. FMS can represent
|
||||
unstructured high-order meshes with general high-order finite element fields
|
||||
on them. When enabled, mfem can convert data collections to/from FMS data
|
||||
collections in memory. In addition, an FMS data collection class was added so
|
||||
the convert-dc miniapp can read and generate data files in FMS format.
|
||||
- Added support for 1D non-conforming meshes (which can be useful for parallel
|
||||
load balancing and derefinement).
|
||||
|
||||
- Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to
|
||||
help identify elements with poor mesh quality.
|
||||
|
||||
- Added support for the "BR2" discontinuous Galerkin discretization for
|
||||
diffusion via DGDiffusionBR2Integrator (see Example 14/14p).
|
||||
|
||||
- Generalized the Multigrid class to support non-geometric multigrid. The
|
||||
previous functionality, based on FiniteElementSpaceHierarchy, is now available
|
||||
in the derived class GeometricMultigrid.
|
||||
|
||||
- Upgraded the Catch unit test framework from version 2.13.0 to version 2.13.2.
|
||||
|
||||
- The TMOP mesh optimization algorithms were extended to GPU:
|
||||
- QualityMetric #1, #2, #7 and #77 are available in 2D, #302, #303, #315
|
||||
and #321 in 3D
|
||||
- Both AnalyticAdaptTC and DiscreteAdaptTC TargetConstructor are available
|
||||
- Kernels for normalization and limiting have been added
|
||||
- The AdvectorCG now also supports AssemblyLevel::PARTIAL
|
||||
|
||||
- Added a new command line boolean option (`--all`) to the unit tests to launch
|
||||
*all* non-regression tests.
|
||||
|
||||
- Added support for different modes of QuadratureInterpolator on GPU.
|
||||
The layout (QVectorLayout::byNODES|byVDIM) and the tensor products modes can
|
||||
be enabled before calling the Mult, Values, Derivatives, PhysDerivatives and
|
||||
Determinants methods.
|
||||
|
||||
- Implemented a filter method for the Navier miniapp to stabilize highly
|
||||
turbulent flows in direct numerical simulation.
|
||||
|
||||
- Added HIP support to the CMake build system.
|
||||
|
||||
- Added support for reading high-order Lagrange meshes in VTK format. Arbitrary-
|
||||
orders and all element types are supported. See the VTK blog for more info:
|
||||
https://blog.kitware.com/wp-content/uploads/2018/09/Source_Issue_43.pdf.
|
||||
|
||||
- Added support for reading VTK meshes in XML format.
|
||||
|
||||
- Added partial assembly and device support to Example 25/25p, with diagonal
|
||||
preconditioning.
|
||||
|
||||
- Implemented a variable step-size IMEX (VSSIMEX) method for the Navier miniapp.
|
||||
|
||||
- Added new mesh quality metrics and improved the untangling capabilities of the
|
||||
TMOP-based mesh optimization algorithms.
|
||||
|
||||
- The TMOP mesh optimization algorithms were extended to GPU:
|
||||
* QualityMetric 1, 2, 7, 77 are available in 2D, 302, 303, 315, 321 in 3D
|
||||
* Both AnalyticAdaptTC and DiscreteAdaptTC TargetConstructor are available
|
||||
* Kernels for normalization and limiting have been added
|
||||
* The AdvectorCG now also supports AssemblyLevel::PARTIAL
|
||||
- Added convective and skew-symmetric integrators for the nonlinear term in the
|
||||
Navier-Stokes equations.
|
||||
|
||||
- Added new miniapp directory mtop/ with optimization-oriented block parametric
|
||||
non-linear form and abstract integrators. Two new miniapps, ParHeat and
|
||||
SeqHeat, demonstrate parallel and sequential implementation of gradients
|
||||
evaluation for linear diffusion with discrete density.
|
||||
|
||||
- Changed the interface for the error estimator.
|
||||
|
||||
- Implemented the Kelly error indicator for scalar-valued problems, supported
|
||||
in serial and parallel builds.
|
||||
|
||||
- Added new classes DenseSymmetricMatrix and SymmetricMatrixCoefficient for
|
||||
efficient evaluation of symmetric matrix coefficients. This replaces the now
|
||||
deprecated EvalSymmetric in MatrixCoefficient. Added DiagonalMatrixCoefficient
|
||||
for clarity, which is a typedef of VectorCoefficient.
|
||||
|
||||
- Added support for AMG preconditioners for non-symmetric systems (e.g.
|
||||
advection-dominated problems) using hypre's approximate ideal restriction
|
||||
(AIR) AMG. Requires hypre version 2.14.0 or newer. Usage is illustrated in
|
||||
example 9/9p.
|
||||
|
||||
- Implemented an adaptive linear solver tolerance option for NewtonSolver based
|
||||
on the algorithm of Eisenstat and Walker.
|
||||
|
||||
- Added support for nonscalar coefficient with VectorDiffusionIntegrator.
|
||||
|
||||
- Extending support for L2 basis functions using MapTypes VALUE and INTEGRAL in
|
||||
linear interpolators and GridFunction "GetValue" methods.
|
||||
|
||||
- Variable order spaces, p- and hp-refinement. This is the initial (serial)
|
||||
support for variable-order FiniteElementCollection and FiniteElementSpace.
|
||||
The new method FiniteElementSpace::SetElementOrder can be called to set an
|
||||
arbitrary order for each mesh element. The conforming interpolation matrix
|
||||
will now automatically constrain p- and hp- interfaces, enabling general
|
||||
hp-refinement in both 2D and 3D, on uniform or mixed NC meshes. Support for
|
||||
parallel variable-order spaces will follow shortly.
|
||||
|
||||
- Added support for creating refined meshes for all element types (e.g. by
|
||||
splitting high-order elements into low-order refined elements), including
|
||||
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.
|
||||
|
||||
- Added new miniapps demonstrating: 1) the use of GSLIB for overlapping grids,
|
||||
see gslib/schwarz_ex1, and 2) coupling different physics in different domains,
|
||||
see navier/cht. Note that gslib v1.0.7 is require (see INSTALL for details).
|
||||
|
||||
- Added a new, very simple example (ex0 and parallel version ex0p). This
|
||||
example solves a simple Poisson problem using H1 elements (the same problem as
|
||||
ex1), but is intended to be extremely simple and approachable for new users.
|
||||
|
||||
- Meshes consisting of any type of elements (including mixed meshes) can be
|
||||
converted to all-simplex meshes using Mesh::MakeSimplicial.
|
||||
|
||||
@@ -209,147 +195,56 @@ Meshing improvements
|
||||
requisite periodic vertex mappings can be created with
|
||||
Mesh::CreatePeriodicVertexMapping.
|
||||
|
||||
- Added support for 1D non-conforming meshes (which can be useful for parallel
|
||||
load balancing and derefinement).
|
||||
- Added support for transferring dual fields between high-order and low-order
|
||||
refined finite element spaces using the transposed versions of the
|
||||
L2ProjectionGridTransfer operators. This functionality is illustrated in the
|
||||
lor-transfer miniapp.
|
||||
|
||||
- Improved interface for using the Ginkgo library, including: support for matrix-
|
||||
free operators in Ginkgo solvers, new wrappers for Ginkgo preconditioners, HIP
|
||||
support, and reduction of unnecessary data copies.
|
||||
|
||||
- Added initial support for hypre's mixed integer (mixedint) capability, which
|
||||
uses different data types for local and global indices in order to save memory
|
||||
in large problems. This capability requires that hypre was configured with the
|
||||
--enable-mixedint option. Note that this option is currently tested only in
|
||||
ex1p and may not work in more general settings.
|
||||
|
||||
- Added support for transferring fields (primary and dual) between high-order
|
||||
and low-order refined H1 finite element spaces using the
|
||||
L2ProjectionH1GridTransfer operators. This functionality is demonstrated
|
||||
through the lor-transfer miniapp when run with the -h1 option.
|
||||
|
||||
- Added new functionality for constructing low-order refined discretizations and
|
||||
solvers, see the LORDiscretization and LORSolver classes. A new basis type for
|
||||
H(curl) and H(div) spaces is introduced to give spectral equivalence. This
|
||||
functionality is illustrated in the LOR solvers miniapp in miniapps/solvers.
|
||||
|
||||
- Added sample meshes in the `data` subdirectory showing the reference elements
|
||||
of the six currently supported element types; ref-segment.mesh,
|
||||
ref-triangle.mesh, ref-square.mesh, ref-tetrahedron.mesh, ref-cube.mesh, and
|
||||
ref-prism.mesh.
|
||||
|
||||
High-performance computing
|
||||
--------------------------
|
||||
- Added initial support for GPU-accelerated versions of PETSc that works with
|
||||
MFEM_USE_CUDA if PETSc has been configured with CUDA support. Examples 1 and 9
|
||||
in the examples/petsc directory have been modified to work with --device cuda.
|
||||
Examples with GAMG (ex1p) and SLEPc (ex11p) are also provided.
|
||||
|
||||
- Added support for explicit vectorization in the high-performance templated
|
||||
code for Fujitsu's A64FX ARM microprocessor architecture.
|
||||
|
||||
- Added support for different modes of QuadratureInterpolator on GPU.
|
||||
The layout (QVectorLayout::byNODES|byVDIM) and the tensor products modes can
|
||||
be enabled before calling the Mult, Values, Derivatives, PhysDerivatives and
|
||||
Determinants methods.
|
||||
|
||||
- Added method Device::SetMemoryTypes that can be used to change the default
|
||||
host and device MemoryTypes before Device setup.
|
||||
|
||||
- In class MemoryManager, added methods GetDualMemoryType and SetDualMemoryType;
|
||||
dual MemoryTypes are used to determine the second MemoryType (host or device)
|
||||
when only one MemoryType is specified in methods of class Memory.
|
||||
|
||||
- Added Memory constructor for setting both the host and device MemoryTypes.
|
||||
|
||||
- Switched the default behavior of device memory allocations so that they are
|
||||
deferred until the device pointer is needed.
|
||||
|
||||
- Added a second Umpire device MemoryType, DEVICE_UMPIRE_2, with corresponding
|
||||
allocator that can be set with the method SetUmpireDevice2AllocatorName.
|
||||
|
||||
- Added HOST_PINNED MemoryType and a pinned host allocator for CUDA and HIP.
|
||||
|
||||
- Added matrix-free GPU-enabled implementations of GradientInterpolator and
|
||||
IdentityInterpolator.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new, very simple example (ex0 and parallel version ex0p). This example
|
||||
solves a simple Poisson problem using H1 elements (the same problem as ex1),
|
||||
but is intended to be extremely simple and approachable for new users.
|
||||
|
||||
- Added new miniapps demonstrating: 1) the use of GSLIB for overlapping grids,
|
||||
see gslib/schwarz_ex1, and 2) coupling different physics in different domains,
|
||||
see navier/cht. Note that gslib v1.0.7 is require (see INSTALL for details).
|
||||
|
||||
- Added a new miniapp for computing (signed) distance functions to a point
|
||||
source or zero level set. See miniapps/shifted/distance.cpp.
|
||||
|
||||
- Added a high-order extension of the shifted boundary method to solve PDEs on
|
||||
non body-fitted meshes. This is illustrated in the new Shifted Diffusion
|
||||
miniapp, see miniapps/shifted/diffusion.cpp.
|
||||
|
||||
- Added new miniapp directory mtop/ with optimization-oriented block parametric
|
||||
non-linear form and abstract integrators. Two new miniapps, ParHeat and
|
||||
SeqHeat, demonstrate parallel and sequential implementation of gradients
|
||||
evaluation for linear diffusion with discrete density.
|
||||
|
||||
- Added a new miniapp block-solvers that compares the performance of various
|
||||
solvers for mixed finite element discretization of the second order scalar
|
||||
elliptic equations. Currently available solvers in the miniapp include a
|
||||
block-diagonal preconditioner that is based on approximate Schur complement
|
||||
(implemented in ex5p), and a newly implemented solver DivFreeSolver, which
|
||||
exploits a multilevel decomposition of the Raviart-Thomas space and its
|
||||
divergence-free subspace. See the miniapps/solvers directory for more details.
|
||||
|
||||
- Introduced new options for the mesh-explorer miniapp to visualize the actual
|
||||
element attributes in parallel meshes while retaining the visualization of the
|
||||
domain decomposition.
|
||||
|
||||
- Added partial assembly and device support to Example 25/25p, with diagonal
|
||||
preconditioning.
|
||||
|
||||
- Implemented a filter method for the Navier miniapp to stabilize highly
|
||||
turbulent flows in direct numerical simulation.
|
||||
|
||||
Improved testing
|
||||
----------------
|
||||
- Transitioned from Travis to GitHub Action for testing/CI on GitHub.
|
||||
|
||||
- Use Spack (and Uberenv) to automate TPL building in LLNL GitLab tests.
|
||||
|
||||
- Extended `make test` to include GPU tests when MFEM is built with CUDA or HIP
|
||||
support.
|
||||
|
||||
- Added a set of suggested git hooks for developers in config/githooks.
|
||||
|
||||
- Added support for Caliper: a library to integrate performance profiling
|
||||
capabilities into applications. See examples/caliper for more details.
|
||||
|
||||
- Added a new command line boolean option (`--all`) to the unit tests to launch
|
||||
*all* non-regression tests.
|
||||
|
||||
- Upgraded the Catch unit test framework from version 2.13.0 to version 2.13.2.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- The following integrations have updated minimum version requirements:
|
||||
* CUDA >= 10.1.168
|
||||
* Ginkgo >= 1.4.0
|
||||
* GSLIB >= 1.0.7
|
||||
* HIOP >= 0.4
|
||||
* HYPRE >= 2.20.0 for mixedint support
|
||||
* HYPRE >= 2.22.0 for CUDA support
|
||||
* libCEED >= 0.8
|
||||
* PETSc >= 3.15.0 for CUDA support
|
||||
* RAJA >= 0.13.0
|
||||
see INSTALL for more details.
|
||||
|
||||
- Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to
|
||||
help identify elements with poor mesh quality.
|
||||
|
||||
- Added support for reading VTK meshes in XML format.
|
||||
|
||||
- Added makefile rule to generate TAGS table for vi or Emacs users.
|
||||
|
||||
- Added HIP support to the CMake build system.
|
||||
libCEED integration improvements
|
||||
--------------------------------
|
||||
- Refactor the libCEED integration
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
- Add support for VectorCoefficient with libCEED backends.
|
||||
|
||||
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`.
|
||||
- Add support for ConvectionIntegrator, and VectorConvectionNLFIntegrator with
|
||||
libCEED backends.
|
||||
|
||||
|
||||
Version 4.2, released on October 30, 2020
|
||||
=========================================
|
||||
|
||||
High-performance computing
|
||||
High-Performance Computing
|
||||
--------------------------
|
||||
- Added support for explicit vectorization in the high-performance templated
|
||||
code, which can now take advantage of specific classes on the following
|
||||
@@ -431,6 +326,9 @@ Linear and nonlinear solvers
|
||||
matrix with the function HypreParMatrixFromBlocks. This could be useful for
|
||||
solving block systems with parallel direct solvers such as STRUMPACK.
|
||||
|
||||
- Added CUDA support for SUNDIALS ODE integrators. See the updated SUNDIALS
|
||||
modification of Example 9/9p.
|
||||
|
||||
- Added wrappers for hypre's flexible GMRES solver and the new parallel ILU
|
||||
preconditioner. The latter requires hypre version 2.19.0 or later.
|
||||
|
||||
@@ -541,7 +439,7 @@ New and updated examples and miniapps
|
||||
L2, with partial assembly support in Example 24/24p.
|
||||
* Weak Dirichlet boundary conditions (Nitsche) to the NURBS miniapp.
|
||||
|
||||
Data management and visualization
|
||||
Data management and Visualization
|
||||
---------------------------------
|
||||
- Added support for ADIOS2 for parallel I/O with ParaView visualization. See
|
||||
Examples 5, 9, 12, 16. The classes adios2stream and ADIOS2DataCollection
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use MFEM, please cite it as follows."
|
||||
authors:
|
||||
- family-names: "MFEM Team"
|
||||
title: "MFEM: Modular Finite Element Methods [Software]"
|
||||
doi: 10.11578/dc.20171025.1248
|
||||
url: "https://mfem.org"
|
||||
preferred-citation:
|
||||
type: article
|
||||
authors:
|
||||
- family-names: "Anderson"
|
||||
given-names: "Robert"
|
||||
orcid: "https://orcid.org/0000-0002-3508-9944"
|
||||
- family-names: "Andrej"
|
||||
given-names: "Julian"
|
||||
orcid: "https://orcid.org/0000-0001-7661-4840"
|
||||
- family-names: "Barker"
|
||||
given-names: "Andrew"
|
||||
orcid: "https://orcid.org/0000-0003-3572-911X"
|
||||
- family-names: "Bramwell"
|
||||
given-names: "Jamie"
|
||||
- family-names: "Camier"
|
||||
given-names: "Jean-Sylvain"
|
||||
orcid: "https://orcid.org/0000-0003-2421-1999"
|
||||
- family-names: "Cerveny"
|
||||
given-names: "Jakub"
|
||||
orcid: "https://orcid.org/0000-0003-4231-2531"
|
||||
- family-names: "Dobrev"
|
||||
given-names: "Veselin"
|
||||
orcid: "https://orcid.org/0000-0003-1793-5622"
|
||||
- family-names: "Dudouit"
|
||||
given-names: "Yohann"
|
||||
orcid: "https://orcid.org/0000-0001-5831-561X"
|
||||
- family-names: "Fisher"
|
||||
given-names: "Aaron"
|
||||
- family-names: "Kolev"
|
||||
given-names: "Tzanio"
|
||||
orcid: "https://orcid.org/0000-0002-2810-3090"
|
||||
- family-names: "Pazner"
|
||||
given-names: "Will"
|
||||
orcid: "https://orcid.org/0000-0003-4885-2934"
|
||||
- family-names: "Stowell"
|
||||
given-names: "Mark"
|
||||
orcid: "https://orcid.org/0000-0002-5389-7435"
|
||||
- family-names: "Tomov"
|
||||
given-names: "Vladimir"
|
||||
orcid: "https://orcid.org/0000-0002-1846-6816"
|
||||
- family-names: "Akkerman"
|
||||
given-names: "Ido"
|
||||
orcid: "https://orcid.org/0000-0002-5937-0300"
|
||||
- family-names: "Dahm"
|
||||
given-names: "Johann"
|
||||
orcid: "https://orcid.org/0000-0001-9657-3564"
|
||||
- family-names: "Medina"
|
||||
given-names: "David"
|
||||
- family-names: "Zampini"
|
||||
given-names: "Stefano"
|
||||
orcid: "https://orcid.org/0000-0002-0435-0433"
|
||||
doi: "10.1016/j.camwa.2020.06.009"
|
||||
journal: "Computers \\& Mathematics with Applications"
|
||||
month: 1
|
||||
start: 42 # First page number
|
||||
end: 74 # Last page number
|
||||
title: "MFEM: A Modular Finite Element Methods Library"
|
||||
volume: 81
|
||||
year: 2021
|
||||
+16
-47
@@ -16,7 +16,7 @@ set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
|
||||
|
||||
# Require C++11 and disable compiler-specific extensions
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
if (MFEM_USE_GINKGO)
|
||||
if (MFEM_USE_GINKGO)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -54,7 +54,7 @@ project(mfem NONE)
|
||||
# Current version of MFEM, see also `makefile`.
|
||||
# mfem_VERSION = (string)
|
||||
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
|
||||
set(${PROJECT_NAME}_VERSION 4.3.1)
|
||||
set(${PROJECT_NAME}_VERSION 4.2.1)
|
||||
|
||||
# Prohibit in-source build
|
||||
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
|
||||
@@ -90,11 +90,6 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/config/XSDKDefaults.cmake")
|
||||
|
||||
# Enable languages.
|
||||
enable_language(CXX)
|
||||
if (MINGW)
|
||||
# MinGW GCC does not expose the functions jn/_jn, yn/_yn (used in Example
|
||||
# 25/25p) unless we use '-std=gnu++11':
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
endif()
|
||||
if (MFEM_USE_CUDA)
|
||||
if (MFEM_USE_HIP)
|
||||
message(FATAL_ERROR " *** MFEM_USE_HIP cannot be combined with MFEM_USE_CUDA.")
|
||||
@@ -107,7 +102,7 @@ if (MFEM_USE_CUDA)
|
||||
endif()
|
||||
enable_language(CUDA)
|
||||
set(CMAKE_CUDA_STANDARD 11)
|
||||
if (MFEM_USE_GINKGO)
|
||||
if (MFEM_USE_GINKGO)
|
||||
set(CMAKE_CUDA_STANDARD 14)
|
||||
endif()
|
||||
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
|
||||
@@ -251,7 +246,6 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
|
||||
message(FATAL_ERROR " *** MFEM_USE_LEGACY_OPENMP requires MFEM_THREAD_SAFE=ON.")
|
||||
endif()
|
||||
find_package(OpenMP REQUIRED)
|
||||
set(OPENMP_LIBRARIES ${OpenMP_CXX_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# SuiteSparse (before SUNDIALS which may depend on KLU)
|
||||
@@ -333,11 +327,7 @@ if (MFEM_USE_AMGX)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CONDUIT)
|
||||
find_package(Conduit REQUIRED conduit relay blueprint)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_FMS)
|
||||
find_package(FMS REQUIRED fms)
|
||||
find_package(Conduit REQUIRED conduit relay blueprint )
|
||||
endif()
|
||||
|
||||
# Axom/Sidre
|
||||
@@ -382,11 +372,6 @@ if (MFEM_USE_UMPIRE)
|
||||
find_package(UMPIRE REQUIRED)
|
||||
endif()
|
||||
|
||||
# GOOGLE-BENCHMARK
|
||||
if (MFEM_USE_BENCHMARK)
|
||||
find_package(Benchmark REQUIRED)
|
||||
endif()
|
||||
|
||||
# Caliper
|
||||
if (MFEM_USE_CALIPER)
|
||||
find_package(Caliper REQUIRED)
|
||||
@@ -415,11 +400,6 @@ if (MFEM_USE_MKL_CPARDISO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# PARELAG
|
||||
if (MFEM_USE_PARELAG)
|
||||
find_package(PARELAG REQUIRED)
|
||||
endif()
|
||||
|
||||
# MFEM_TIMER_TYPE
|
||||
if (NOT DEFINED MFEM_TIMER_TYPE)
|
||||
if (APPLE)
|
||||
@@ -443,11 +423,10 @@ endif()
|
||||
# With newer versions of SuiteSparse which include METIS header using 64-bit
|
||||
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
|
||||
# be before SuiteSparse.
|
||||
set(MFEM_TPLS OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS PETSC
|
||||
SLEPC MESQUITE MUMPS STRUMPACK AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB NETCDF
|
||||
set(MFEM_TPLS MPI_CXX OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS PETSC
|
||||
SLEPC MESQUITE MUMPS STRUMPACK AXOM CONDUIT Ginkgo GNUTLS GSLIB NETCDF
|
||||
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
|
||||
CUSPARSE MKL_CPARDISO AMGX CALIPER BENCHMARK PARELAG MPI_CXX)
|
||||
|
||||
CUSPARSE MKL_CPARDISO AMGX CALIPER)
|
||||
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
|
||||
set(TPL_LIBRARIES "")
|
||||
set(TPL_INCLUDE_DIRS "")
|
||||
@@ -466,9 +445,6 @@ include_directories(${TPL_INCLUDE_DIRS})
|
||||
if (OPENMP_FOUND)
|
||||
message(STATUS "MFEM: using package OpenMP")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
if (MFEM_USE_CUDA)
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=${OpenMP_CXX_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "MFEM build type: CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
|
||||
@@ -511,9 +487,13 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} CACHE PATH
|
||||
# Declaring the library
|
||||
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
|
||||
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
|
||||
if (CMAKE_VERSION VERSION_GREATER 2.8.11)
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(mfem ${TPL_LIBRARIES})
|
||||
endif()
|
||||
if (MINGW)
|
||||
target_link_libraries(mfem PRIVATE ws2_32)
|
||||
target_link_libraries(mfem ws2_32)
|
||||
endif()
|
||||
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
|
||||
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
|
||||
@@ -552,21 +532,15 @@ endif()
|
||||
set(MFEM_CUSTOM_TARGET_PREFIX CACHE STRING "")
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Examples, miniapps, benchmarks and testing
|
||||
# Examples, miniapps, and testing
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# Enable testing and benchmarks if required
|
||||
# Enable testing if required
|
||||
if (MFEM_ENABLE_TESTING)
|
||||
enable_testing()
|
||||
set(MFEM_ALL_TESTS_TARGET_NAME tests)
|
||||
add_mfem_target(${MFEM_ALL_TESTS_TARGET_NAME} OFF)
|
||||
add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
|
||||
# Create a target for all benchmarks and, optionally, enable it.
|
||||
set(MFEM_ALL_BENCHMARKS_TARGET_NAME benchmarks)
|
||||
add_mfem_target(${MFEM_ALL_BENCHMARKS_TARGET_NAME}
|
||||
${MFEM_ENABLE_GOOGLE_BENCHMARKS})
|
||||
add_subdirectory(tests/benchmarks EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
# Define a target that all examples and miniapps will depend on.
|
||||
@@ -576,11 +550,7 @@ add_custom_target(${MFEM_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
# Create a target for all examples and, optionally, enable it.
|
||||
set(MFEM_ALL_EXAMPLES_TARGET_NAME examples)
|
||||
add_mfem_target(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${MFEM_ENABLE_EXAMPLES})
|
||||
if (MFEM_ENABLE_EXAMPLES)
|
||||
add_subdirectory(examples) #install examples if enabled
|
||||
else()
|
||||
add_subdirectory(examples EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
add_subdirectory(examples EXCLUDE_FROM_ALL)
|
||||
|
||||
# Create a target for all miniapps and, optionally, enable it.
|
||||
set(MFEM_ALL_MINIAPPS_TARGET_NAME miniapps)
|
||||
@@ -590,7 +560,6 @@ add_subdirectory(miniapps EXCLUDE_FROM_ALL)
|
||||
# Target to build all executables, i.e. everything.
|
||||
add_custom_target(exec)
|
||||
add_dependencies(exec
|
||||
${MFEM_ALL_BENCHMARKS_TARGET_NAME}
|
||||
${MFEM_ALL_EXAMPLES_TARGET_NAME}
|
||||
${MFEM_ALL_MINIAPPS_TARGET_NAME}
|
||||
${MFEM_ALL_TESTS_TARGET_NAME})
|
||||
|
||||
+25
-139
@@ -4,9 +4,7 @@
|
||||
|
||||
<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://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://travis-ci.org/mfem/mfem"><img alt="Build Status" src="https://travis-ci.org/mfem/mfem.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>
|
||||
@@ -42,7 +40,6 @@ back to them before issuing pull requests:
|
||||
- [New Feature Development](#new-feature-development)
|
||||
- [Developer Guidelines](#developer-guidelines)
|
||||
- [Pull Requests](#pull-requests)
|
||||
- [MFEM PR Rules](#mfem-pr-rules)
|
||||
- [Pull Request Checklist](#pull-request-checklist)
|
||||
- [Master/Next Workflow](#masternext-workflow)
|
||||
- [Releases](#releases)
|
||||
@@ -66,18 +63,14 @@ 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) and
|
||||
follow the [MFEM PR Rules](#mfem-pr-rules).
|
||||
- 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
|
||||
the `ready-for-review` label.
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning two
|
||||
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
|
||||
@@ -98,8 +91,8 @@ The MFEM source code has the following structure:
|
||||
```
|
||||
.
|
||||
├── config
|
||||
│ ├── cmake
|
||||
│ └── githooks
|
||||
│ └── cmake
|
||||
│ └── ...
|
||||
├── data
|
||||
├── doc
|
||||
├── examples
|
||||
@@ -107,14 +100,12 @@ The MFEM source code has the following structure:
|
||||
│ ├── caliper
|
||||
│ ├── ginkgo
|
||||
│ ├── hiop
|
||||
│ ├── jupyter
|
||||
│ ├── petsc
|
||||
│ ├── pumi
|
||||
│ ├── sundials
|
||||
| └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── fe
|
||||
│ ├── qinterp
|
||||
│ └── tmop
|
||||
├── general
|
||||
@@ -130,7 +121,6 @@ The MFEM source code has the following structure:
|
||||
│ ├── mtop
|
||||
│ ├── navier
|
||||
│ ├── nurbs
|
||||
│ ├── parelag
|
||||
│ ├── performance
|
||||
│ ├── shifted
|
||||
│ ├── solvers
|
||||
@@ -139,10 +129,10 @@ The MFEM source code has the following structure:
|
||||
└── tests
|
||||
├── convergence
|
||||
├── gitlab
|
||||
├── mem_manager
|
||||
├── par-mesh-format
|
||||
├── scripts
|
||||
└── unit
|
||||
└── ...
|
||||
```
|
||||
|
||||
#### Main directories and classes
|
||||
@@ -328,22 +318,15 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
change the code by default.
|
||||
|
||||
- Code specifics
|
||||
- All new public, protected, and private classes, methods, data members, and
|
||||
functions have Doxygen-style documentation in source comments.
|
||||
- In addition to arguments and functionality, documentation should include the
|
||||
current limitations of the code, any background information that is
|
||||
implicitly assumed in the implementation, and the ownership and lifetime
|
||||
of data.
|
||||
- All significant new classes, methods and functions have Doxygen-style
|
||||
documentation in source comments.
|
||||
- Consistent code styling is enforced with `make style` in the top-level
|
||||
directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we
|
||||
specifically use version 3.1). See also the file `config/mfem.astylerc`.
|
||||
specifically use version 2.05.1). See also the file `config/mfem.astylerc`.
|
||||
- Use `mfem::out` and `mfem::err` instead of `std::cout` and `std::cerr` in
|
||||
internal library code. (You can use `std` in examples and miniapps.)
|
||||
- When manually resolving conflicts during a merge, make sure to mention the
|
||||
conflicted files in the commit message.
|
||||
- All significant new features and changes should be documented in CHANGELOG.
|
||||
- New examples and miniapps should have documentation on the MFEM webpage.
|
||||
|
||||
|
||||
### Pull Requests
|
||||
|
||||
@@ -380,10 +363,6 @@ 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.
|
||||
|
||||
@@ -391,101 +370,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 GitHub Actions and Appveyor [continuous integration](#automated-testing)
|
||||
- Track the Travis CI, 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 GitHub Actions
|
||||
are safeguards that are allowed to fail in certain cases.
|
||||
- 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.
|
||||
|
||||
- Other tests, such as the `code-style`, `documentation` and `gitignore`
|
||||
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.
|
||||
checks in Travis and Github Actions enforce MFEM-specific rules which are
|
||||
explained in the error messages and the `tests/scripts` directory.
|
||||
|
||||
- If triggered, track the status of the LLNL GitLab tests. If failing, ask
|
||||
one of the _LLNL developers_ for details.
|
||||
|
||||
|
||||
### MFEM PR Rules
|
||||
|
||||
The Pull Request (PR) approval process in MFEM is similar to the approval of papers in a peer-reviewed journal. In particular:
|
||||
|
||||
1. There is an MFEM board of "editors" that evaluates new PRs and assigns "reviewers" for each PR.
|
||||
|
||||
2. The assigned reviewers are responsible to carefully review and test the proposed PR.
|
||||
|
||||
3. A PR can be (manually) merged in the *next* branch only if 2 of the assigned reviewers have approved it and it has passed internal testing. This merge can be performed by any of the assigned reviewers or by any of the editors.
|
||||
|
||||
4. A PR can be merged in the *master* branch only if it has been tested successfully for a week in *next* and an editor has (optionally) taken a final look. This merge can be performed only by one of the editors.
|
||||
|
||||
#### Responsibilities of Editors
|
||||
|
||||
The current list of MFEM editors is:
|
||||
|
||||
- @v-dobrev (Veselin Dobrev)
|
||||
- @tzanio (Tzanio Kolev)
|
||||
- @pazner (Will Pazner)
|
||||
- @mlstowell (Mark Stowell)
|
||||
|
||||
**The responsibilities of the editors are:**
|
||||
|
||||
1. To assign appropriate milestone and labels for new PRs, e.g. *bugfix*, *minor*, *api-change*, *high-impact*, etc.
|
||||
|
||||
2. To assign at least 2 reviewers for new PRs. An editor can also be a reviewer. The editor, reviewers, and author should be listed as "Assignees" on the GitHub PR page. After assignment, the `in-review` label should be added.
|
||||
|
||||
3. To complete the initial PR evaluation and assignments in a timely manner: 1 week from submission.
|
||||
|
||||
4. To assist reviewers when they need help with their reviews (but also to stay out of the way when they don't).
|
||||
|
||||
5. To remind the reviewers about timely completion of their review.
|
||||
|
||||
6. To take a final look and complete the PR merge in *master*. The final look step is optional and shouldn't take more than 3 days.
|
||||
|
||||
7. The assignment of bugfixes should be expedited proportional to their importance, e.g. in some cases the editor can assign much shorter review window.
|
||||
|
||||
#### Responsibilities of Reviewers
|
||||
|
||||
Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their area of expertise.
|
||||
|
||||
**The responsibilities of the reviewers are:**
|
||||
|
||||
1. To let the editors know if the proposed assignment is not a good match for them.
|
||||
|
||||
2. To communicate with the PR author, provide feedback and work with them to resolve issues.
|
||||
|
||||
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
|
||||
|
||||
3. To seek help from the editors in case of difficulties.
|
||||
|
||||
4. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
|
||||
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
|
||||
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
|
||||
#### Responsibilities of Authors
|
||||
|
||||
Authors should clearly indicate when a PR is ready for review (before that the PR should be marked as `Draft` or `[WIP]`).
|
||||
|
||||
**The responsibilities of the authors are:**
|
||||
|
||||
1. To follow the instructions and PR checklist in the `CONTRIBUTING.md` document in the MFEM repository.
|
||||
|
||||
2. To respond to reviewer feedback in a timely manner.
|
||||
|
||||
3. Authors are encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
4. Authors can use the "Reviewers" section of the GitHub PR page to suggest reviewers, but the "Assignees" section will show who the editor has assigned to do the reviews.
|
||||
|
||||
5. To indicate when the PR is ready for review by adding the `ready-for-review` label.
|
||||
|
||||
|
||||
### Pull Request Checklist
|
||||
|
||||
Before a PR can be merged, it should satisfy the following:
|
||||
@@ -501,7 +399,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)
|
||||
- [ ] `.github`
|
||||
- [ ] `.travis.yml`
|
||||
- [ ] `.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.
|
||||
@@ -539,9 +437,7 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New capability:
|
||||
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
|
||||
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
|
||||
- [ ] Any new functions should include descriptions of their intended use e.g. for internal use only, user-facing, etc., along with references to example code whenever possible/appropriate.
|
||||
- [ ] All significant new classes, methods and functions have Doxygen-style documentation in source comments.
|
||||
- [ ] Consider adding new sample runs in existing examples to highlight the new capability.
|
||||
- [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo.
|
||||
- [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*.
|
||||
@@ -552,7 +448,6 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] (LLNL only) After merging:
|
||||
- [ ] Update internal tests to include the new features.
|
||||
|
||||
|
||||
### Master/Next Workflow
|
||||
|
||||
MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
@@ -621,7 +516,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
|
||||
- [ ] `.github`
|
||||
- [ ] `.travis.yml`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update the `CHANGELOG` to organize all release contributions
|
||||
- [ ] Review the whole source code once over
|
||||
@@ -644,10 +539,8 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- Update version and shortlinks in `src/index.md` and `src/download.md`.
|
||||
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
|
||||
|
||||
|
||||
## LLNL Workflow
|
||||
|
||||
|
||||
### Mirroring on Bitbucket
|
||||
|
||||
- The GitHub `master` and `next` branches are mirrored to the LLNL institutional
|
||||
@@ -667,17 +560,16 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- `mfem:gh-next` -- Bleeding-edge development version, may be broken, use at
|
||||
your own risk.
|
||||
|
||||
|
||||
### Mirroring on GitLab
|
||||
|
||||
- MFEM repository is also mirrored on the LLNL GitLab instance, in a
|
||||
semi-automated manner.
|
||||
|
||||
- This instance is meant to complete CI testing with tests on Livermore
|
||||
Computing systems. GitLab pipeline status is reported in the corresponding
|
||||
Computing systems. Gitlab pipeline status is reported in the corresponding
|
||||
GitHub pull request.
|
||||
|
||||
- In GitLab pipelines, TPLs (dependencies) are built using Spack, driven by Uberenv.
|
||||
- In Gitlab pipelines, TPLs (dependencies) are built using Spack, driven by Uberenv.
|
||||
|
||||
- No change to the MFEM repo can be made on this instance.
|
||||
|
||||
@@ -686,23 +578,18 @@ 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### 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).
|
||||
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).
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
- 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.
|
||||
|
||||
|
||||
### Windows smoke test
|
||||
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor` file and the
|
||||
@@ -712,7 +599,6 @@ build logs at
|
||||
CMake is used to generate the MSVC Project files and drive the build. A release
|
||||
and debug build is performed with a simple run of `ex1` to verify the executable.
|
||||
|
||||
|
||||
### Tests at LLNL
|
||||
|
||||
- We mirror the `master` and `next` branches internally (to `gh-master` and
|
||||
|
||||
@@ -459,10 +459,6 @@ MFEM_USE_UMPIRE = YES/NO
|
||||
discovery, provision, and management of memory on machines with multiple
|
||||
memory devices like NUMA and GPUs.
|
||||
|
||||
MFEM_USE_BENCHMARK = YES/NO
|
||||
Enables support for Google Benchmark, a library to support the benchmarking
|
||||
of functions, in the tests/benchmarks directory.
|
||||
|
||||
MFEM_USE_HIOP = YES/NO
|
||||
Enable the usage of HiOp (https://github.com/LLNL/hiop) in MFEM. HiOp is an
|
||||
HPC solver for nonlinear optimization problems.
|
||||
@@ -478,7 +474,7 @@ MFEM_USE_HIP = YES/NO
|
||||
Enables support for AMD devices in MFEM. HIP is a heterogeneous-compute
|
||||
interface for portability developed by AMD that can target both AMD and
|
||||
NVIDIA GPUs. The variable HIP_ARCH is used to specify the AMD GPU processor
|
||||
used during compilation (by default, HIP_ARCH=gfx900). When enabled, this
|
||||
used during compilation (by default, HIP_ARCH=gfx900). When enabled, this
|
||||
option uses the HIP_* build options, see below.
|
||||
|
||||
MFEM_USE_RAJA = YES/NO
|
||||
@@ -520,18 +516,6 @@ MFEM_USE_CALIPER = YES/NO
|
||||
profiling at runtime with Caliper's configuration API. Alternatively, one
|
||||
can configure Caliper through environment variables or config files.
|
||||
|
||||
MFEM_USE_FMS = YES/NO
|
||||
Enables support for the FMS library which consists of the DataCollection
|
||||
sub-class mfem::FMSDataCollection for I/O in FMS formats, see the header file
|
||||
fem/fmsdatacollection.hpp. In addition, this option enables in-memory
|
||||
convetion routines between FMS's FmsDataCollection structure and MFEM's
|
||||
DataCollection class, see the header file fem/fmsconvert.hpp.
|
||||
|
||||
MFEM_USE_PARELAG = YES/NO
|
||||
Enables the miniapps that use the ParELAG library. MFEM does not currently
|
||||
use ParELAG. In fact, ParELAG is dependent on MFEM. Therefore, this option
|
||||
currently only concerns the miniapps.
|
||||
|
||||
MFEM_BUILD_TAG = (any value)
|
||||
An optional tag to characterize the build. Exported to config/config.mk.
|
||||
Can be used to identify the MFEM build from other makefiles.
|
||||
@@ -556,9 +540,8 @@ The specific libraries and their options are:
|
||||
See also the "Specific options for hypre" section at the end of this file.
|
||||
URL: https://github.com/hypre-space/hypre and https://www.llnl.gov/casc/hypre
|
||||
Options: HYPRE_OPT, HYPRE_LIB.
|
||||
Versions: HYPRE >= 2.10.0b (HYPRE built without CUDA)
|
||||
HYPRE >= 2.20.0 (HYPRE built with '--enable-mixedint')
|
||||
HYPRE >= 2.22.1 (HYPRE built with CUDA)
|
||||
Versions: HYPRE >= 2.10.0b,
|
||||
HYPRE >= 2.20.0 for '--enable-mixedint' support.
|
||||
|
||||
- METIS, used when MFEM_USE_METIS = YES. If using METIS 5, set
|
||||
MFEM_USE_METIS_5 = YES (default is to use METIS 4).
|
||||
@@ -632,7 +615,7 @@ The specific libraries and their options are:
|
||||
and dependencies of specific modules, see the Ginkgo webpage below.
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or Debug).
|
||||
Versions: Ginkgo >= 1.4.0.
|
||||
Versions: Ginkgo >= 1.4.0.
|
||||
|
||||
- AmgX (optional), used when MFEM_USE_AMGX = YES.
|
||||
URL: https://github.com/NVIDIA/AMGX
|
||||
@@ -701,7 +684,7 @@ The specific libraries and their options are:
|
||||
- HiOp (optional), used when MFEM_USE_HIOP = YES.
|
||||
URL: https://github.com/LLNL/hiop
|
||||
Options: HIOP_OPT, HIOP_LIB.
|
||||
Versions: HIOP >= 0.4.6.
|
||||
Versions: HIOP >= 0.4.
|
||||
|
||||
- GSLIB (optional), used when MFEM_USE_GSLIB = YES. The gslib library must be
|
||||
built prior to the MFEM build, as follows: download gslib-1.0.7, untar it at
|
||||
@@ -739,10 +722,10 @@ The specific libraries and their options are:
|
||||
Versions: libCEED >= 0.8.
|
||||
|
||||
- RAJA (optional), used when MFEM_USE_RAJA = YES.
|
||||
Beginning with MFEM v4.3, only RAJA v0.14.0+ is supported.
|
||||
Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported.
|
||||
URL: https://github.com/LLNL/RAJA
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 0.14.0.
|
||||
Versions: RAJA >= 0.13.0.
|
||||
|
||||
- Caliper (optional), used when MFEM_USE_CALIPER = YES.
|
||||
URL: https://github.com/LLNL/Caliper
|
||||
@@ -753,12 +736,7 @@ The specific libraries and their options are:
|
||||
Umpire requires camp when the Umpire version is >= 3.0.0.
|
||||
URL: https://github.com/LLNL/Umpire
|
||||
Options: UMPIRE_DIR, UMPIRE_OPT, UMPIRE_LIB.
|
||||
Versions: Umpire >= 3.0.0.
|
||||
|
||||
- Benchmark, used when MFEM_USE_BENCHMARK = YES.
|
||||
URL: https://github.com/google/benchmark
|
||||
Options: BENCHMARK_DIR, BENCHMARK_LIB.
|
||||
Versions: Benchmark >= 1.5.6.
|
||||
Versions: Umpire >= 2.0.0.
|
||||
|
||||
- MPFR (optional), used when MFEM_USE_MPFR = YES.
|
||||
URL: http://mpfr.org, it depends on the GMP library: https://gmplib.org
|
||||
@@ -775,15 +753,6 @@ The specific libraries and their options are:
|
||||
URL: https://zlib.net
|
||||
Options: ZLIB_OPT, ZLIB_LIB.
|
||||
|
||||
- FMS (optional), used when MFEM_USE_FMS = YES.
|
||||
URL: https://github.com/CEED/FMS
|
||||
Options: FMS_OPT, FMS_LIB.
|
||||
Versions: FMS >= 0.2.
|
||||
|
||||
- ParELAG, used when MFEM_USE_PARELAG = YES.
|
||||
URL: https://github.com/LLNL/parelag
|
||||
Options: PARELAG_DIR, PARELAG_OPT, PARELAG_LIB.
|
||||
|
||||
Building with CMake
|
||||
===================
|
||||
The MFEM build system consists of two steps: configuration and compilation.
|
||||
@@ -915,9 +884,6 @@ MFEM_USE_RAJA
|
||||
MFEM_USE_UMPIRE
|
||||
MFEM_USE_SIDRE
|
||||
MFEM_USE_CALIPER
|
||||
MFEM_USE_FMS
|
||||
MFEM_USE_BENCHMARK
|
||||
MFEM_USE_PARELAG
|
||||
|
||||
The following options are CMake specific:
|
||||
|
||||
@@ -972,9 +938,6 @@ The CMake build system adds auto-detection for the following packages/libraries:
|
||||
- UMPIRE
|
||||
- AXOM - Used when MFEM_USE_SIDRE is enabled
|
||||
- CALIPER
|
||||
- FMS
|
||||
- BENCHMARK
|
||||
- ParELAG
|
||||
|
||||
The following built-in CMake packages are also used:
|
||||
|
||||
@@ -992,7 +955,7 @@ config/config.hpp.in:
|
||||
|
||||
cp config/config.hpp.in config/_config.hpp
|
||||
|
||||
The file config/_config.hpp can then be edited to enable desired options. The
|
||||
The file config/_config.hpp can then be edited to enable desired options. The
|
||||
MFEM library is simply a combination of all object files obtained by compiling
|
||||
the .cpp source files in the source directories: general, linalg, mesh, and fem.
|
||||
|
||||
@@ -1000,7 +963,7 @@ the .cpp source files in the source directories: general, linalg, mesh, and fem.
|
||||
Specifying an MPI job launcher
|
||||
==============================
|
||||
By default, MFEM will use 'mpirun -np #' to launch any of its parallel tests or
|
||||
miniapps, where # is the number of MPI tasks. An alternate MPI launcher can be
|
||||
miniapps, where # is the number of MPI tasks. An alternate MPI launcher can be
|
||||
provided by setting the MFEM_MPIEXEC and MFEM_MPIEXEC_NP config variables.
|
||||
|
||||
MFEM will expect the launcher command, plus the command line option to allow it
|
||||
|
||||
@@ -256,10 +256,6 @@ IF (DEFINED TPL_ENABLE_SIDRE)
|
||||
SET(MFEM_USE_SIDRE ${TPL_ENABLE_SIDRE} CACHE BOOL "Enable Axom/Sidre usage" FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF (DEFINED TPL_ENABLE_FMS)
|
||||
SET(MFEM_USE_FMS ${TPL_ENABLE_FMS} CACHE BOOL "Enable FMS usage" FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF (DEFINED TPL_ENABLE_CONDUIT)
|
||||
SET(MFEM_USE_CONDUIT ${TPL_ENABLE_CONDUIT} CACHE BOOL "Enable Conduit usage" FORCE)
|
||||
ENDIF()
|
||||
@@ -283,11 +279,3 @@ ENDIF()
|
||||
IF (DEFINED TPL_ENABLE_UMPIRE)
|
||||
SET(MFEM_USE_UMPIRE ${TPL_ENABLE_UMPIRE} CACHE BOOL "Enable Umpire" FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF (DEFINED TPL_ENABLE_BENCHMARK)
|
||||
SET(MFEM_USE_BENCHMARK ${TPL_ENABLE_BENCHMARK} CACHE BOOL "Enable Google-Benchmark" FORCE)
|
||||
ENDIF()
|
||||
|
||||
IF (DEFINED TPL_ENABLE_PARELAG)
|
||||
SET(MFEM_USE_PARELAG ${TPL_ENABLE_PARELAG} CACHE BOOL "Enable ParELAG" FORCE)
|
||||
ENDIF()
|
||||
|
||||
@@ -44,7 +44,6 @@ set(MFEM_USE_PETSC @MFEM_USE_PETSC@)
|
||||
set(MFEM_USE_SLEPC @MFEM_USE_SLEPC@)
|
||||
set(MFEM_USE_MPFR @MFEM_USE_MPFR@)
|
||||
set(MFEM_USE_SIDRE @MFEM_USE_SIDRE@)
|
||||
set(MFEM_USE_FMS @MFEM_USE_FMS@)
|
||||
set(MFEM_USE_CONDUIT @MFEM_USE_CONDUIT@)
|
||||
set(MFEM_USE_PUMI @MFEM_USE_PUMI@)
|
||||
set(MFEM_USE_CUDA @MFEM_USE_CUDA@)
|
||||
@@ -55,8 +54,6 @@ set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
|
||||
set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
|
||||
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
|
||||
set(MFEM_USE_CALIPER @MFEM_USE_CALIPER@)
|
||||
set(MFEM_USE_BENCHMARK @MFEM_USE_BENCHMARK@)
|
||||
set(MFEM_USE_PARELAG @MFEM_USE_PARELAG@)
|
||||
|
||||
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
|
||||
set(MFEM_CXX_FLAGS "@CMAKE_CXX_FLAGS@")
|
||||
|
||||
@@ -119,9 +119,6 @@
|
||||
// Enable the use of SIMD in the high performance templated classes
|
||||
#cmakedefine MFEM_USE_SIMD
|
||||
|
||||
// Enable MFEM functionality based on the FMS library
|
||||
#cmakedefine MFEM_USE_FMS
|
||||
|
||||
// Enable MFEM functionality based on Conduit
|
||||
#cmakedefine MFEM_USE_CONDUIT
|
||||
|
||||
@@ -175,7 +172,4 @@
|
||||
// Enable interface to the MKL CPardiso library.
|
||||
#cmakedefine MFEM_USE_MKL_CPARDISO
|
||||
|
||||
// Enable MFEM functionality based on the Google Benchmark library.
|
||||
#cmakedefine MFEM_USE_BENCHMARK
|
||||
|
||||
#endif // MFEM_CONFIG_HEADER
|
||||
|
||||
@@ -1,22 +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.
|
||||
|
||||
# Defines the following variables:
|
||||
# - BENCHMARK_FOUND
|
||||
# - BENCHMARK_LIBRARIES
|
||||
# - BENCHMARK_INCLUDE_DIRS
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(Benchmark BENCHMARK BENCHMARK_DIR
|
||||
"include" "benchmark/benchmark.h"
|
||||
"lib" "benchmark"
|
||||
"Paths to headers required by Google Benchmark."
|
||||
"Libraries required by Google Benchmark.")
|
||||
@@ -1,20 +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.
|
||||
|
||||
# Defines the following variables:
|
||||
# - FMS_FOUND
|
||||
# - FMS_LIBRARIES
|
||||
# - FMS_INCLUDE_DIRS
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(FMS FMS FMS_DIR
|
||||
"include" fms.h "lib" fms
|
||||
"Paths to headers required by FMS." "Libraries required by FMS.")
|
||||
@@ -1,19 +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.
|
||||
|
||||
# Defines the following variables:
|
||||
# - PARELAG_FOUND
|
||||
# - PARELAG_LIBRARIES
|
||||
# - PARELAG_INCLUDE_DIRS
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
mfem_find_package(PARELAG PARELAG PARELAG_DIR "" "" "" ""
|
||||
"Paths to headers required by ParELAG." "Libraries required by ParELAG.")
|
||||
@@ -100,8 +100,6 @@ macro(add_mfem_examples EXE_SRCS)
|
||||
|
||||
string(REPLACE ".cpp" "" EXE_NAME "${EXE_PREFIX}${SRC_FILENAME}")
|
||||
mfem_add_executable(${EXE_NAME} ${SRC_FILE})
|
||||
install(TARGETS ${EXE_NAME}
|
||||
RUNTIME DESTINATION examples)
|
||||
add_dependencies(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${EXE_NAME})
|
||||
if (EXE_NEEDED_BY)
|
||||
add_dependencies(${EXE_NEEDED_BY} ${EXE_NAME})
|
||||
@@ -766,7 +764,7 @@ function(mfem_export_mk_files)
|
||||
MFEM_USE_GNUTLS MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC
|
||||
MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_CONDUIT MFEM_USE_PUMI
|
||||
MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA MFEM_USE_UMPIRE MFEM_USE_SIMD
|
||||
MFEM_USE_ADIOS2 MFEM_USE_BENCHMARK MFEM_USE_PARELAG)
|
||||
MFEM_USE_ADIOS2)
|
||||
foreach(var ${CONFIG_MK_BOOL_VARS})
|
||||
if (${var})
|
||||
set(${var} YES)
|
||||
|
||||
@@ -117,9 +117,6 @@
|
||||
// Enable the use of SIMD in the high performance templated classes
|
||||
// #define MFEM_USE_SIMD
|
||||
|
||||
// Enable FMS support
|
||||
// #define MFEM_USE_FMS
|
||||
|
||||
// Enable Conduit support
|
||||
// #define MFEM_USE_CONDUIT
|
||||
|
||||
@@ -180,7 +177,4 @@
|
||||
// Enable interface to the MKL CPardiso library.
|
||||
// #define MFEM_USE_MKL_CPARDISO
|
||||
|
||||
// Enable functionality based on the Google Benchmark library.
|
||||
// #define MFEM_USE_BENCHMARK
|
||||
|
||||
#endif // MFEM_CONFIG_HEADER
|
||||
|
||||
@@ -43,7 +43,6 @@ MFEM_USE_PETSC = @MFEM_USE_PETSC@
|
||||
MFEM_USE_SLEPC = @MFEM_USE_SLEPC@
|
||||
MFEM_USE_MPFR = @MFEM_USE_MPFR@
|
||||
MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
|
||||
MFEM_USE_FMS = @MFEM_USE_FMS@
|
||||
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
|
||||
MFEM_USE_PUMI = @MFEM_USE_PUMI@
|
||||
MFEM_USE_HIOP = @MFEM_USE_HIOP@
|
||||
@@ -58,8 +57,6 @@ MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
|
||||
MFEM_USE_SIMD = @MFEM_USE_SIMD@
|
||||
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
|
||||
MFEM_USE_MKL_CPARDISO = @MFEM_USE_MKL_CPARDISO@
|
||||
MFEM_USE_BENCHMARK = @MFEM_USE_BENCHMARK@
|
||||
MFEM_USE_PARELAG = @MFEM_USE_PARELAG@
|
||||
|
||||
# Compiler, compile options, and link options
|
||||
MFEM_CXX = @MFEM_CXX@
|
||||
|
||||
+2
-27
@@ -45,7 +45,6 @@ option(MFEM_USE_PETSC "Enable PETSc support." OFF)
|
||||
option(MFEM_USE_SLEPC "Enable SLEPc support." OFF)
|
||||
option(MFEM_USE_MPFR "Enable MPFR usage." OFF)
|
||||
option(MFEM_USE_SIDRE "Enable Axom/Sidre usage" OFF)
|
||||
option(MFEM_USE_FMS "Enable FMS usage" OFF)
|
||||
option(MFEM_USE_CONDUIT "Enable Conduit usage" OFF)
|
||||
option(MFEM_USE_PUMI "Enable PUMI" OFF)
|
||||
option(MFEM_USE_HIOP "Enable HiOp" OFF)
|
||||
@@ -58,8 +57,6 @@ option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" OFF)
|
||||
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
|
||||
option(MFEM_USE_CALIPER "Enable Caliper support" OFF)
|
||||
option(MFEM_USE_MKL_CPARDISO "Enable MKL CPardiso" OFF)
|
||||
option(MFEM_USE_BENCHMARK "Enable Google Benchmark" OFF)
|
||||
option(MFEM_USE_PARELAG "Enable ParELAG" OFF)
|
||||
|
||||
# Optional overrides for autodetected MPIEXEC and MPIEXEC_NUMPROC_FLAG
|
||||
# set(MFEM_MPIEXEC "mpirun" CACHE STRING "Command for running MPI tests")
|
||||
@@ -76,7 +73,6 @@ set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
|
||||
option(MFEM_ENABLE_TESTING "Enable the ctest framework for testing" ON)
|
||||
option(MFEM_ENABLE_EXAMPLES "Build all of the examples" OFF)
|
||||
option(MFEM_ENABLE_MINIAPPS "Build all of the miniapps" OFF)
|
||||
option(MFEM_ENABLE_GOOGLE_BENCHMARKS "Build all of the Google benchmarks" OFF)
|
||||
|
||||
# Setting CXX/MPICXX on the command line or in user.cmake will overwrite the
|
||||
# autodetected C++ compiler.
|
||||
@@ -100,11 +96,6 @@ set(HYPRE_DIR "${MFEM_DIR}/../hypre/src/hypre" CACHE PATH
|
||||
# If hypre was compiled to depend on BLAS and LAPACK:
|
||||
# set(HYPRE_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
|
||||
# "Packages that HYPRE depends on.")
|
||||
if (MFEM_USE_CUDA)
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
set(HYPRE_REQUIRED_LIBRARIES "-lcusparse" "-lcurand" CACHE STRING
|
||||
"Libraries that HYPRE depends on.")
|
||||
endif()
|
||||
|
||||
set(METIS_DIR "${MFEM_DIR}/../metis-4.0" CACHE PATH "Path to the METIS library.")
|
||||
|
||||
@@ -141,10 +132,10 @@ set(MUMPS_DIR "${MFEM_DIR}/../MUMPS_5.2.0" CACHE PATH
|
||||
"Path to the MUMPS library.")
|
||||
# Packages required by MUMPS, depending on how it was compiled.
|
||||
set(MUMPS_REQUIRED_PACKAGES "MPI" "BLAS" "METIS" "ScaLAPACK" CACHE STRING
|
||||
"Additional packages required by MUMPS.")
|
||||
"Additional packages required by MUMPS.")
|
||||
# If the MPI package does not find all required Fortran libraries:
|
||||
# set(MUMPS_REQUIRED_LIBRARIES "gfortran" "mpi_mpifh" CACHE STRING
|
||||
# "Additional libraries required by MUMPS.")
|
||||
# "Additional libraries required by MUMPS.")
|
||||
|
||||
set(STRUMPACK_DIR "${MFEM_DIR}/../STRUMPACK-build" CACHE PATH
|
||||
"Path to the STRUMPACK library.")
|
||||
@@ -196,12 +187,6 @@ set(SLEPC_ARCH "arch-linux2-c-debug" CACHE STRING "SLEPC build architecture.")
|
||||
|
||||
set(MPFR_DIR "" CACHE PATH "Path to the MPFR library.")
|
||||
|
||||
set(FMS_DIR "${MFEM_DIR}/../fms" CACHE PATH
|
||||
"Path to the FMS library.")
|
||||
# If FMS is built with Conduit:
|
||||
# set(FMS_REQUIRED_PACKAGES "Conduit/relay" CACHE STRING
|
||||
# "Additional packages required by FMS.")
|
||||
|
||||
set(CONDUIT_DIR "${MFEM_DIR}/../conduit" CACHE PATH
|
||||
"Path to the Conduit library.")
|
||||
|
||||
@@ -227,16 +212,6 @@ set(RAJA_DIR "${MFEM_DIR}/../raja" CACHE PATH "Path to RAJA")
|
||||
set(CEED_DIR "${MFEM_DIR}/../libCEED" CACHE PATH "Path to libCEED")
|
||||
set(UMPIRE_DIR "${MFEM_DIR}/../umpire" CACHE PATH "Path to Umpire")
|
||||
set(CALIPER_DIR "${MFEM_DIR}/../caliper" CACHE PATH "Path to Caliper")
|
||||
set(BENCHMARK_DIR "${MFEM_DIR}/../google-benchmark" CACHE PATH
|
||||
"Path to Google Benchmark")
|
||||
|
||||
# Provide paths, since ParELAG is dependent on MFEM and MFEM needs to be
|
||||
# compiled (or at least cmake needs to succeed) before compiling ParELAG.
|
||||
set(PARELAG_DIR "${MFEM_DIR}/../parelag" CACHE PATH "Path to ParELAG")
|
||||
set(PARELAG_INCLUDE_DIRS "${PARELAG_DIR}/src;${PARELAG_DIR}/build/src" CACHE
|
||||
STRING "Path to ParELAG headers.")
|
||||
set(PARELAG_LIBRARIES "${PARELAG_DIR}/build/src/libParELAG.a" CACHE STRING
|
||||
"The ParELAG library.")
|
||||
|
||||
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
|
||||
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
|
||||
|
||||
+2
-24
@@ -61,7 +61,7 @@ HIP_XLINKER = -Wl,
|
||||
|
||||
ifneq ($(NOTMAC),)
|
||||
AR = ar
|
||||
ARFLAGS = crv
|
||||
ARFLAGS = cruv
|
||||
RANLIB = ranlib
|
||||
PICFLAG = $(XCOMPILER)-fPIC
|
||||
SO_EXT = so
|
||||
@@ -73,7 +73,7 @@ ifneq ($(NOTMAC),)
|
||||
else
|
||||
# Silence "has no symbols" warnings on Mac OS X
|
||||
AR = ar
|
||||
ARFLAGS = Scrv
|
||||
ARFLAGS = Scruv
|
||||
RANLIB = ranlib -no_warning_for_no_symbols
|
||||
PICFLAG = $(XCOMPILER)-fPIC
|
||||
SO_EXT = dylib
|
||||
@@ -136,7 +136,6 @@ MFEM_USE_PETSC = NO
|
||||
MFEM_USE_SLEPC = NO
|
||||
MFEM_USE_MPFR = NO
|
||||
MFEM_USE_SIDRE = NO
|
||||
MFEM_USE_FMS = NO
|
||||
MFEM_USE_CONDUIT = NO
|
||||
MFEM_USE_PUMI = NO
|
||||
MFEM_USE_HIOP = NO
|
||||
@@ -151,8 +150,6 @@ MFEM_USE_UMPIRE = NO
|
||||
MFEM_USE_SIMD = NO
|
||||
MFEM_USE_ADIOS2 = NO
|
||||
MFEM_USE_MKL_CPARDISO = NO
|
||||
MFEM_USE_BENCHMARK = NO
|
||||
MFEM_USE_PARELAG = NO
|
||||
|
||||
# MPI library compile and link flags
|
||||
# These settings are used only when building MFEM with MPI + HIP
|
||||
@@ -177,10 +174,6 @@ LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
|
||||
HYPRE_DIR = @MFEM_DIR@/../hypre/src/hypre
|
||||
HYPRE_OPT = -I$(HYPRE_DIR)/include
|
||||
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
|
||||
ifeq (YES,$(MFEM_USE_CUDA))
|
||||
# This is only necessary when hypre is built with cuda:
|
||||
HYPRE_LIB += -lcusparse -lcurand
|
||||
endif
|
||||
|
||||
# METIS library configuration
|
||||
ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK)$(MFEM_USE_MUMPS),NONONO)
|
||||
@@ -364,11 +357,6 @@ endif
|
||||
MPFR_OPT =
|
||||
MPFR_LIB = -lmpfr
|
||||
|
||||
# FMS and required libraries configuration
|
||||
FMS_DIR = $(MFEM_DIR)/../fms
|
||||
FMS_OPT = -I$(FMS_DIR)/include
|
||||
FMS_LIB = -Wl,-rpath,$(FMS_DIR)/lib -L$(FMS_DIR)/lib -lfms
|
||||
|
||||
# Conduit and required libraries configuration
|
||||
CONDUIT_DIR = @MFEM_DIR@/../conduit
|
||||
CONDUIT_OPT = -I$(CONDUIT_DIR)/include/conduit
|
||||
@@ -431,11 +419,6 @@ CALIPER_DIR = @MFEM_DIR@/../caliper
|
||||
CALIPER_OPT = -I$(CALIPER_DIR)/include
|
||||
CALIPER_LIB = $(XLINKER)-rpath,$(CALIPER_DIR)/lib64 -L$(CALIPER_DIR)/lib64 -lcaliper
|
||||
|
||||
# BENCHMARK library configuration
|
||||
BENCHMARK_DIR = @MFEM_DIR@/../google-benchmark
|
||||
BENCHMARK_OPT = -I$(BENCHMARK_DIR)/include
|
||||
BENCHMARK_LIB = -L$(BENCHMARK_DIR)/lib -lbenchmark -lpthread
|
||||
|
||||
# libCEED library configuration
|
||||
CEED_DIR ?= @MFEM_DIR@/../libCEED
|
||||
CEED_OPT = -I$(CEED_DIR)/include
|
||||
@@ -466,11 +449,6 @@ MKL_CPARDISO_LIB = $(XLINKER)-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
|
||||
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
|
||||
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
|
||||
|
||||
# PARELAG library configuration
|
||||
PARELAG_DIR = @MFEM_DIR@/../parelag
|
||||
PARELAG_OPT = -I$(PARELAG_DIR)/src -I$(PARELAG_DIR)/build/src
|
||||
PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
|
||||
|
||||
# If YES, enable some informational messages
|
||||
VERBOSE = NO
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
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.
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Apply automated code formatting
|
||||
make -C $(git rev-parse --show-toplevel) style
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/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 3.1" ]]; then
|
||||
cd tests/scripts
|
||||
if ! ./runtest code-style; then code=1; fi
|
||||
cd -
|
||||
else
|
||||
echo "Warning: astyle not found or version is not 3.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
|
||||
+6
-31
@@ -57,27 +57,22 @@ TIMECMD := $(word 1,$(TIMECMD))
|
||||
ifneq (,$(filter test%,$(MAKECMDGOALS)))
|
||||
MAKEFLAGS += -k
|
||||
endif
|
||||
# Test runs of the examples/miniapps with parameters - check exit code:
|
||||
# 0 means success, 255 means the test was skipped, anything else means error
|
||||
# Test runs of the examples/miniapps with parameters - check exit code
|
||||
mfem-test = \
|
||||
printf " $(3) [$(2) $(1) ... ]: "; \
|
||||
$(call $(TIMEFUN),$(TIMECMD),$(2) ./$(1) $(if $(5),,-no-vis )$(4) \
|
||||
> $(1).stderr 2>&1); \
|
||||
err="$$3"; \
|
||||
if [ "$$3" = 0 ]; then $(PRINT_OK); \
|
||||
else if [ "$$3" = 255 ]; then $(PRINT_SKIP); err=0; \
|
||||
else $(PRINT_FAILED); cat $(1).stderr; fi; fi; \
|
||||
rm -f $(1).stderr; exit $$err
|
||||
if [ "$$3" = 0 ]; \
|
||||
then $(PRINT_OK); else $(PRINT_FAILED); cat $(1).stderr; fi; \
|
||||
rm -f $(1).stderr; exit $$3
|
||||
|
||||
# Test runs of the examples/miniapps - check exit code and if a file exists
|
||||
# See mfem-test for the interpretation of the error code
|
||||
mfem-test-file = \
|
||||
printf " $(3) [$(2) $(1) ... ]: "; \
|
||||
$(call $(TIMEFUN),$(TIMECMD),$(2) ./$(1) -no-vis > $(1).stderr 2>&1); \
|
||||
err="$$3"; \
|
||||
if [ "$$3" = 0 ] && [ -e $(4) ]; then $(PRINT_OK); \
|
||||
else if [ "$$3" = 255 ] && [ -e $(4) ]; then $(PRINT_SKIP); err=0; \
|
||||
else $(PRINT_FAILED); cat $(1).stderr; err=64; fi; fi; \
|
||||
if [ "$$3" = 0 ] && [ -e $(4) ]; \
|
||||
then $(PRINT_OK); else $(PRINT_FAILED); cat $(1).stderr; err=64; fi; \
|
||||
rm -f $(1).stderr; exit $$err
|
||||
|
||||
.PHONY: test test-par-YES test-par-NO test-ser test-par test-clean test-print
|
||||
@@ -85,26 +80,6 @@ mfem-test-file = \
|
||||
# What sets of tests to run in serial and parallel
|
||||
test-par-YES: $(PAR_$(MFEM_TESTS):=-test-par) $(SEQ_$(MFEM_TESTS):=-test-seq)
|
||||
test-par-NO: $(SEQ_$(MFEM_TESTS):=-test-seq)
|
||||
ifeq ($(MFEM_USE_CUDA),YES)
|
||||
.PHONY: test-par-YES-cuda test-par-NO-cuda test-ser-cuda test-par-cuda test-cuda
|
||||
test-par-YES: test-par-YES-cuda
|
||||
test-par-NO: test-par-NO-cuda
|
||||
test-par-YES-cuda: test-par-cuda test-ser-cuda
|
||||
test-par-NO-cuda: test-ser-cuda
|
||||
test-ser-cuda: $(SEQ_DEVICE_$(MFEM_TESTS):=-test-seq-cuda)
|
||||
test-par-cuda: $(PAR_DEVICE_$(MFEM_TESTS):=-test-par-cuda)
|
||||
test-cuda: test-par-$(MFEM_USE_MPI)-cuda clean-exec
|
||||
endif
|
||||
ifeq ($(MFEM_USE_HIP),YES)
|
||||
.PHONY: test-par-YES-hip test-par-NO-hip test-ser-hip test-par-hip test-hip
|
||||
test-par-YES: test-par-YES-hip
|
||||
test-par-NO: test-par-NO-hip
|
||||
test-par-YES-hip: test-par-hip test-ser-hip
|
||||
test-par-NO-hip: test-ser-hip
|
||||
test-ser-hip: $(SEQ_DEVICE_$(MFEM_TESTS):=-test-seq-hip)
|
||||
test-par-hip: $(PAR_DEVICE_$(MFEM_TESTS):=-test-par-hip)
|
||||
test-hip: test-par-$(MFEM_USE_MPI)-hip clean-exec
|
||||
endif
|
||||
test-ser: test-par-NO
|
||||
test-par: test-par-YES
|
||||
test: all test-par-$(MFEM_USE_MPI) clean-exec
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
MFEM INLINE mesh v1.0
|
||||
|
||||
type = pyramid
|
||||
nx = 4
|
||||
ny = 4
|
||||
nz = 4
|
||||
sx = 1.0
|
||||
sy = 1.0
|
||||
sz = 1.0
|
||||
@@ -1,43 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
2
|
||||
1 7 4 3 2 1 0
|
||||
1 7 1 2 3 4 5
|
||||
|
||||
boundary
|
||||
8
|
||||
1 2 0 2 1
|
||||
2 2 0 3 2
|
||||
3 2 0 4 3
|
||||
4 2 0 1 4
|
||||
5 2 1 2 5
|
||||
6 2 2 3 5
|
||||
7 2 3 4 5
|
||||
8 2 4 1 5
|
||||
|
||||
vertices
|
||||
6
|
||||
3
|
||||
0 0 -1
|
||||
1 0 0
|
||||
0 1 0
|
||||
-1 0 0
|
||||
0 -1 0
|
||||
0 0 1
|
||||
@@ -1,38 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
1
|
||||
1 7 0 1 2 3 4
|
||||
|
||||
boundary
|
||||
5
|
||||
1 3 3 2 1 0
|
||||
2 2 0 1 4
|
||||
3 2 1 2 4
|
||||
4 2 2 3 4
|
||||
5 2 3 0 4
|
||||
|
||||
vertices
|
||||
5
|
||||
3
|
||||
0 0 0
|
||||
1 0 0
|
||||
1 1 0
|
||||
0 1 0
|
||||
0 0 1
|
||||
@@ -1,246 +0,0 @@
|
||||
FMS: 100
|
||||
DataCollection/Name: star
|
||||
DataCollection/NumberOfFieldDescriptors: 1
|
||||
DataCollection/FieldDescriptors/0/Name: CoordsDescriptor
|
||||
DataCollection/FieldDescriptors/0/ComponentName: volume
|
||||
DataCollection/FieldDescriptors/0/Type: 0
|
||||
DataCollection/FieldDescriptors/0/FixedOrder/Size: 3
|
||||
DataCollection/FieldDescriptors/0/FixedOrder/Type: FMS_UINT64
|
||||
DataCollection/FieldDescriptors/0/FixedOrder/Values: [0, 1, 3]
|
||||
DataCollection/FieldDescriptors/0/NumDofs: 211
|
||||
DataCollection/NumberOfFields: 1
|
||||
DataCollection/Fields/0/Name: Coords
|
||||
DataCollection/Fields/0/LayoutType: 0
|
||||
DataCollection/Fields/0/NumberOfVectorComponents: 2
|
||||
DataCollection/Fields/0/FieldDescriptorName: CoordsDescriptor
|
||||
DataCollection/Fields/0/Data/Size: 422
|
||||
DataCollection/Fields/0/Data/Type: FMS_DOUBLE
|
||||
DataCollection/Fields/0/Data/Values: [-0.016886, 1.000000, 0.309017,
|
||||
1.309020, -0.809017, -0.500000,
|
||||
-0.809017, -1.618030, 0.309017,
|
||||
-0.500000, 1.309020, 0.519420,
|
||||
1.154510, 0.809019, 0.147680,
|
||||
-0.095492, -0.654508, -0.415586,
|
||||
-1.213520, -1.213520, -0.392210,
|
||||
-0.654508, -0.095492, 0.139949,
|
||||
0.809019, 1.154510, 0.660184,
|
||||
-0.264063, -0.800064, -0.231060,
|
||||
0.663691, 0.183114, 0.317639,
|
||||
0.543082, 0.598483, 0.345112,
|
||||
0.478298, 0.027703, 0.095229,
|
||||
0.012368, -0.092534, -0.334412,
|
||||
-0.313767, -0.140526, -0.293881,
|
||||
-0.534056, -0.660290, -0.537646,
|
||||
-0.655590, -0.121396, -0.274504,
|
||||
-0.346497, -0.296570, 0.004737,
|
||||
-0.098835, 0.069287, 0.082675,
|
||||
0.318799, 0.467183, 0.564505,
|
||||
0.595190, 0.846237, 0.671735,
|
||||
1.051500, 1.103010, 0.964008,
|
||||
0.821603, 1.257520, 1.206010,
|
||||
1.142350, 0.975686, 0.781273,
|
||||
0.717257, 0.475684, 0.642352,
|
||||
0.268930, 0.211049, 0.174181,
|
||||
0.039345, -0.147746, -0.177481,
|
||||
-0.365164, -0.230328, -0.551503,
|
||||
-0.603005, -0.497587, -0.389864,
|
||||
-0.757514, -0.706011, -0.675487,
|
||||
-0.528946, -0.943851, -1.078690,
|
||||
-1.087600, -0.955467, -1.483190,
|
||||
-1.348360, -1.483190, -1.348360,
|
||||
-1.085930, -0.938010, -0.943851,
|
||||
-1.078690, -0.681476, -0.540944,
|
||||
-0.757514, -0.706011, -0.540614,
|
||||
-0.367058, -0.551503, -0.603005,
|
||||
-0.365164, -0.230328, -0.138552,
|
||||
-0.206896, 0.174181, 0.039345,
|
||||
0.268468, 0.222269, 0.475684,
|
||||
0.642352, 0.759791, 0.719381,
|
||||
1.142350, 0.975686, 1.257520,
|
||||
1.206010, 0.972837, 0.836119,
|
||||
1.051500, 1.103010, 0.214572,
|
||||
0.407449, 0.288323, 0.449827,
|
||||
-0.086700, -0.027358, -0.200560,
|
||||
-0.166595, -0.271802, -0.418426,
|
||||
-0.426131, -0.551441, -0.096117,
|
||||
-0.206969, -0.027946, -0.184969,
|
||||
0.211136, 0.260131, 0.407172,
|
||||
0.430781, 0.718277, 0.885068,
|
||||
0.753103, 0.957692, 0.866273,
|
||||
1.024530, 0.934099, 1.093820,
|
||||
0.348422, 0.524463, 0.404903,
|
||||
0.587376, 0.054525, 0.146431,
|
||||
-0.078026, -0.007795, -0.329488,
|
||||
-0.302967, -0.488115, -0.439332,
|
||||
-0.498515, -0.453527, -0.633059,
|
||||
-0.570251, -0.655787, -0.791132,
|
||||
-0.802013, -0.956872, -1.094720,
|
||||
-1.208560, -1.207720, -1.339910,
|
||||
-0.693371, -0.795404, -0.803594,
|
||||
-0.945068, -0.464668, -0.631721,
|
||||
-0.458968, -0.547876, -0.332878,
|
||||
-0.485696, -0.286385, -0.408481,
|
||||
0.053021, -0.058200, 0.110846,
|
||||
-0.002086, 0.381384, 0.416784,
|
||||
0.551133, 0.613261, 0.872474,
|
||||
0.901208, 1.038300, 1.084660,
|
||||
0.737459, 0.751250, 0.890002,
|
||||
0.915210, 0.010915, 0.000000,
|
||||
0.951057, 0.951057, 0.587785,
|
||||
1.538840, -0.587785, 0.000000,
|
||||
-0.951057, -1.538840, -0.951057,
|
||||
-0.015847, 0.475529, 0.951057,
|
||||
0.492248, 1.244950, 1.063310,
|
||||
0.274399, 0.293893, -0.293892,
|
||||
-0.296404, -1.063310, -1.244950,
|
||||
-0.453865, -0.951057, -0.475529,
|
||||
0.466620, 0.792932, -0.013913,
|
||||
-0.748783, -0.497528, 0.021382,
|
||||
-0.017158, 0.172591, 0.330125,
|
||||
0.458568, 0.457971, 0.137740,
|
||||
0.299049, 0.588394, 0.667324,
|
||||
0.432341, 0.634346, 0.117322,
|
||||
0.193603, 0.211702, 0.098278,
|
||||
-0.199438, -0.077304, -0.082243,
|
||||
-0.216296, -0.458634, -0.592374,
|
||||
-0.563926, -0.680404, -0.135751,
|
||||
-0.302942, -0.469005, -0.453640,
|
||||
-0.182727, -0.314240, 0.024270,
|
||||
0.021546, 0.158510, 0.317019,
|
||||
0.485799, 0.492951, 0.792548,
|
||||
0.634038, 0.951057, 0.951057,
|
||||
0.777915, 0.613430, 0.951057,
|
||||
0.951057, 0.793994, 0.635800,
|
||||
1.049020, 1.146990, 1.084480,
|
||||
0.924310, 1.440880, 1.342910,
|
||||
1.380330, 1.221820, 0.948209,
|
||||
0.856297, 0.746293, 0.904802,
|
||||
0.476242, 0.393234, 0.489821,
|
||||
0.391857, 0.194471, 0.075751,
|
||||
0.097964, 0.195929, -0.097964,
|
||||
-0.195928, -0.173234, -0.078922,
|
||||
-0.489821, -0.391856, -0.467007,
|
||||
-0.397859, -0.746293, -0.904802,
|
||||
-0.945206, -0.849559, -1.380330,
|
||||
-1.221820, -1.440880, -1.342910,
|
||||
-1.100830, -0.923191, -1.049020,
|
||||
-1.146990, -0.774515, -0.621542,
|
||||
-0.951057, -0.951057, -0.803055,
|
||||
-0.635255, -0.951057, -0.951057,
|
||||
-0.792548, -0.634038, -0.454301,
|
||||
-0.479369, -0.158510, -0.317019,
|
||||
0.149331, 0.178643, 0.295860,
|
||||
0.306275, 0.246225, 0.406610,
|
||||
0.367954, 0.498458, -0.014929,
|
||||
0.084917, -0.094272, -0.000726,
|
||||
-0.245374, -0.340755, -0.435351,
|
||||
-0.490564, -0.176355, -0.294974,
|
||||
-0.148010, -0.328246, 0.136248,
|
||||
0.182883, 0.328957, 0.309903,
|
||||
0.646446, 0.622546, 0.800859,
|
||||
0.801803, 0.643087, 0.654473,
|
||||
0.796963, 0.816799, 0.748189,
|
||||
0.898148, 0.819092, 0.970033,
|
||||
1.049540, 1.162660, 1.115310,
|
||||
1.266700, 0.539960, 0.728396,
|
||||
0.656318, 0.783152, 0.302716,
|
||||
0.403858, 0.219006, 0.309252,
|
||||
0.009667, 0.107161, -0.118211,
|
||||
0.011786, -0.284704, -0.179858,
|
||||
-0.411567, -0.296676, -0.570168,
|
||||
-0.635348, -0.726784, -0.793211,
|
||||
-1.016860, -1.112970, -1.170910,
|
||||
-1.281530, -0.737860, -0.851723,
|
||||
-0.883732, -0.995070, -0.653153,
|
||||
-0.769939, -0.631918, -0.797688,
|
||||
-0.616289, -0.806819, -0.638485,
|
||||
-0.790356, -0.136399, -0.322769,
|
||||
-0.165339, -0.309622]
|
||||
DataCollection/Mesh/PartitionInfo/Size: 2
|
||||
DataCollection/Mesh/PartitionInfo/Type: FMS_UINT64
|
||||
DataCollection/Mesh/PartitionInfo/Values: [0, 1]
|
||||
DataCollection/Mesh/NumDomainNames: 1
|
||||
DataCollection/Mesh/NumComponents: 1
|
||||
DataCollection/Mesh/NumTags: 0
|
||||
DataCollection/Mesh/DomainNames/0/Name: Domain
|
||||
DataCollection/Mesh/DomainNames/0/NumDomains: 1
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Dimension: 2
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/NumVertices: 31
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/0/EntityType: FMS_EDGE
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/0/NumEntities: 50
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/0/Size: 100
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/0/Type: FMS_INT32
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/0/Values: [11, 0, 26,
|
||||
11, 26, 14,
|
||||
14, 0, 27,
|
||||
14, 27, 17,
|
||||
17, 0, 28,
|
||||
17, 28, 20,
|
||||
20, 0, 29,
|
||||
20, 29, 23,
|
||||
23, 0, 30,
|
||||
23, 30, 11,
|
||||
11, 1, 12,
|
||||
1, 26, 12,
|
||||
12, 3, 13,
|
||||
3, 26, 13,
|
||||
13, 2, 14,
|
||||
2, 15, 2,
|
||||
27, 15, 15,
|
||||
5, 16, 5,
|
||||
27, 16, 16,
|
||||
4, 17, 4,
|
||||
18, 4, 28,
|
||||
18, 18, 7,
|
||||
19, 7, 28,
|
||||
19, 19, 6,
|
||||
20, 6, 21,
|
||||
6, 29, 21,
|
||||
21, 9, 22,
|
||||
9, 29, 22,
|
||||
22, 8, 23,
|
||||
8, 24, 8,
|
||||
30, 24, 24,
|
||||
10, 25, 10,
|
||||
30, 25, 25, 1]
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/1/EntityType: FMS_QUADRILATERAL
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/1/NumEntities: 20
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/1/Size: 80
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/1/Type: FMS_INT32
|
||||
DataCollection/Mesh/DomainNames/0/Domains/0/Entities/1/Values: [0, 1, 2,
|
||||
3, 3, 4,
|
||||
5, 6, 6,
|
||||
7, 8, 9,
|
||||
9, 10, 11,
|
||||
12, 12, 13,
|
||||
14, 0, 15,
|
||||
16, 17, 1,
|
||||
17, 18, 19,
|
||||
20, 2, 20,
|
||||
21, 22, 22,
|
||||
23, 24, 4,
|
||||
24, 25, 26,
|
||||
27, 5, 27,
|
||||
28, 29, 29,
|
||||
30, 31, 7,
|
||||
31, 32, 33,
|
||||
34, 8, 34,
|
||||
35, 36, 36,
|
||||
37, 38, 10,
|
||||
38, 39, 40,
|
||||
41, 11, 41,
|
||||
42, 43, 43,
|
||||
44, 45, 13,
|
||||
45, 46, 47,
|
||||
48, 14, 48,
|
||||
49, 15]
|
||||
DataCollection/Mesh/Components/0/Name: volume
|
||||
DataCollection/Mesh/Components/0/Dimension: 2
|
||||
DataCollection/Mesh/Components/0/NumEntities: 20
|
||||
DataCollection/Mesh/Components/0/Coordinates: Coords
|
||||
DataCollection/Mesh/Components/0/NumParts: 1
|
||||
DataCollection/Mesh/Components/0/Parts/0/DomainName: Domain
|
||||
DataCollection/Mesh/Components/0/Parts/0/DomainID: 0
|
||||
DataCollection/Mesh/Components/0/Parts/0/FullDomain: Yes
|
||||
DataCollection/Mesh/Components/0/Relations/Size: 0
|
||||
DataCollection/Mesh/Components/0/Relations/Type: FMS_UINT64
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = "MFEM"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v4.3.1
|
||||
PROJECT_NUMBER = v4.2.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -765,7 +765,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/linalg \
|
||||
@MFEM_SOURCE_DIR@/mesh \
|
||||
@MFEM_SOURCE_DIR@/fem \
|
||||
@MFEM_SOURCE_DIR@/fem/fe \
|
||||
@MFEM_SOURCE_DIR@/examples \
|
||||
@MFEM_SOURCE_DIR@/examples/caliper \
|
||||
@MFEM_SOURCE_DIR@/examples/amgx \
|
||||
@@ -787,8 +786,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/shifted \
|
||||
@MFEM_SOURCE_DIR@/miniapps/solvers \
|
||||
@MFEM_SOURCE_DIR@/miniapps/tools \
|
||||
@MFEM_SOURCE_DIR@/miniapps/toys \
|
||||
@MFEM_SOURCE_DIR@/miniapps/parelag
|
||||
@MFEM_SOURCE_DIR@/miniapps/toys
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
||||
+2
-33
@@ -37,7 +37,6 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex27.cpp
|
||||
ex28.cpp
|
||||
ex29.cpp
|
||||
ex30.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -71,7 +70,6 @@ if (MFEM_USE_MPI)
|
||||
ex27p.cpp
|
||||
ex28p.cpp
|
||||
ex29p.cpp
|
||||
ex30p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -86,9 +84,8 @@ foreach(SRC_FILE ${ALL_EXE_SRCS})
|
||||
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
|
||||
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
|
||||
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
if (${TEST_NAME} MATCHES "ex0p?")
|
||||
set(THIS_TEST_OPTIONS)
|
||||
if (NOT (${TEST_NAME} MATCHES "ex0p?"))
|
||||
set(THIS_TEST_OPTIONS "-no-vis")
|
||||
endif()
|
||||
if (${TEST_NAME} MATCHES "ex10p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-tf" "5")
|
||||
@@ -110,34 +107,6 @@ foreach(SRC_FILE ${ALL_EXE_SRCS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Add CUDA/HIP tests.
|
||||
set(DEVICE_EXAMPLES
|
||||
# serial examples with device support:
|
||||
ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26
|
||||
# parallel examples with device support:
|
||||
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p ex24p ex25p ex26p)
|
||||
set(MFEM_TEST_DEVICE)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_TEST_DEVICE "cuda")
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_TEST_DEVICE "hip")
|
||||
endif()
|
||||
if (MFEM_TEST_DEVICE)
|
||||
foreach(TEST_NAME ${DEVICE_EXAMPLES})
|
||||
set(THIS_TEST_OPTIONS "-no-vis" "-d" "${MFEM_TEST_DEVICE}")
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
elseif (MFEM_USE_MPI)
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# If STRUMPACK is enabled, add a test run that uses it.
|
||||
if (MFEM_USE_STRUMPACK)
|
||||
add_test(NAME ex11p_strumpack_np=${MFEM_MPI_NP}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
Finite Element Discretization Library
|
||||
__
|
||||
_ __ ___ / _| ___ _ __ ___
|
||||
| '_ ` _ \ | |_ / _ \| '_ ` _ \
|
||||
| | | | | || _|| __/| | | | | |
|
||||
|_| |_| |_||_| \___||_| |_| |_|
|
||||
|
||||
https://mfem.org
|
||||
|
||||
This directory contains modifications of the example codes that illustrate the
|
||||
use of MFEM features based on the Caliper performance profiling library.
|
||||
|
||||
To build these examples, make sure that MFEM is configured with the option
|
||||
"MFEM_USE_CALIPER = YES", see the top-level INSTALL file for details (version
|
||||
2.5.0 of Caliper is recommended, though older versions may work too).
|
||||
|
||||
We recommend comparing the original example codes with the corresponding files
|
||||
in the current directory.
|
||||
@@ -206,9 +206,9 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
MFEM_PERF_BEGIN("Solve A X=B");
|
||||
if (!pa)
|
||||
{
|
||||
MFEM_PERF_SCOPE("Solve A X=B (FA)");
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
@@ -223,7 +223,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
MFEM_PERF_SCOPE("Solve A X=B (PA)");
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
@@ -234,6 +233,7 @@ int main(int argc, char *argv[])
|
||||
CG(*A, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
}
|
||||
MFEM_PERF_END("Solve A X=B");
|
||||
// 12. Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
|
||||
+18
-19
@@ -231,29 +231,28 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
MFEM_PERF_BEGIN("Solve A X = B");
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
MFEM_PERF_SCOPE("Solve A X=B");
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
MFEM_PERF_END("Solve A X = B");
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
// ex1 -m ../data/fichera.mesh
|
||||
// ex1 -m ../data/fichera-mixed.mesh
|
||||
// ex1 -m ../data/toroid-wedge.mesh
|
||||
// ex1 -m ../data/octahedron.mesh -o 1
|
||||
// ex1 -m ../data/periodic-annulus-sector.msh
|
||||
// ex1 -m ../data/periodic-torus-sector.msh
|
||||
// ex1 -m ../data/square-disc-p2.vtk -o 2
|
||||
|
||||
+13
-20
@@ -55,7 +55,6 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
int nev = 5;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -72,8 +71,6 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -89,18 +86,13 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement (2 by default, or
|
||||
// specified on the command line with -rs).
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
@@ -108,7 +100,7 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution (1 time by
|
||||
// default, or specified on the command line with -rp). Once the parallel
|
||||
// mesh is defined, the serial mesh can be deleted.
|
||||
@@ -118,8 +110,9 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
@@ -129,7 +122,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
|
||||
// 7. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
|
||||
// element space. The first corresponds to the curl curl, while the second
|
||||
// is a simple mass matrix needed on the right hand side of the
|
||||
// generalized eigenvalue problem below. The boundary conditions are
|
||||
@@ -171,7 +164,7 @@ int main(int argc, char *argv[])
|
||||
delete a;
|
||||
delete m;
|
||||
|
||||
// 9. Define and configure the AME eigensolver and the AMS preconditioner for
|
||||
// 8. Define and configure the AME eigensolver and the AMS preconditioner for
|
||||
// A to be used within the solver. Set the matrices which define the
|
||||
// generalized eigenproblem A x = lambda M x.
|
||||
HypreAMS *ams = new HypreAMS(*A,fespace);
|
||||
@@ -187,15 +180,15 @@ int main(int argc, char *argv[])
|
||||
ame->SetMassMatrix(*M);
|
||||
ame->SetOperator(*A);
|
||||
|
||||
// 10. Compute the eigenmodes and extract the array of eigenvalues. Define a
|
||||
// parallel grid function to represent each of the eigenmodes returned by
|
||||
// the solver.
|
||||
// 9. Compute the eigenmodes and extract the array of eigenvalues. Define a
|
||||
// parallel grid function to represent each of the eigenmodes returned by
|
||||
// the solver.
|
||||
Array<double> eigenvalues;
|
||||
ame->Solve();
|
||||
ame->GetEigenvalues(eigenvalues);
|
||||
ParGridFunction x(fespace);
|
||||
|
||||
// 11. Save the refined mesh and the modes in parallel. This output can be
|
||||
// 10. Save the refined mesh and the modes in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g mode".
|
||||
{
|
||||
ostringstream mesh_name, mode_name;
|
||||
@@ -220,7 +213,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 12. Send the solution by socket to a GLVis server.
|
||||
// 11. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -260,7 +253,7 @@ int main(int argc, char *argv[])
|
||||
mode_sock.close();
|
||||
}
|
||||
|
||||
// 13. Free the used memory.
|
||||
// 12. Free the used memory.
|
||||
delete ame;
|
||||
delete ams;
|
||||
delete M;
|
||||
|
||||
+2
-2
@@ -135,8 +135,8 @@ int main(int argc, char *argv[])
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
}
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
|
||||
+4
-4
@@ -199,8 +199,8 @@ int main(int argc, char *argv[])
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
}
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
@@ -221,7 +221,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
HyprePCG pcg(*A);
|
||||
pcg.SetTol(1e-12);
|
||||
pcg.SetMaxIter(500);
|
||||
pcg.SetMaxIter(200);
|
||||
pcg.SetPrintLevel(2);
|
||||
pcg.SetPreconditioner(*amg);
|
||||
pcg.Mult(*B, *X);
|
||||
@@ -232,7 +232,7 @@ int main(int argc, char *argv[])
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetRelTol(1e-12);
|
||||
gmres.SetMaxIter(500);
|
||||
gmres.SetMaxIter(200);
|
||||
gmres.SetKDim(10);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetOperator(*A);
|
||||
|
||||
+4
-7
@@ -24,10 +24,7 @@
|
||||
// class ConductionOperator defining C(u)), as well as their
|
||||
// implicit time integration. Note that implementing the method
|
||||
// ConductionOperator::ImplicitSolve is the only requirement for
|
||||
// high-order implicit (SDIRK) time integration. In this example,
|
||||
// the diffusion operator is linearized by evaluating with the
|
||||
// lagged solution from the previous timestep, so there is only
|
||||
// a linear solve.
|
||||
// high-order implicit (SDIRK) time integration.
|
||||
//
|
||||
// We recommend viewing examples 2, 9 and 10 before viewing this
|
||||
// example.
|
||||
@@ -329,8 +326,8 @@ ConductionOperator::ConductionOperator(FiniteElementSpace &f, double al,
|
||||
void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
{
|
||||
// Compute:
|
||||
// du_dt = M^{-1}*-Ku
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
// du_dt = M^{-1}*-K(u)
|
||||
// for du_dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg(); // z = -z
|
||||
M_solver.Mult(z, du_dt);
|
||||
@@ -341,7 +338,7 @@ void ConductionOperator::ImplicitSolve(const double dt,
|
||||
{
|
||||
// Solve the equation:
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
// for du_dt
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
|
||||
+5
-8
@@ -24,11 +24,8 @@
|
||||
// class ConductionOperator defining C(u)), as well as their
|
||||
// implicit time integration. Note that implementing the method
|
||||
// ConductionOperator::ImplicitSolve is the only requirement for
|
||||
// high-order implicit (SDIRK) time integration. In this example,
|
||||
// the diffusion operator is linearized by evaluating with the
|
||||
// lagged solution from the previous timestep, so there is only
|
||||
// a linear solve. Optional saving with ADIOS2
|
||||
// (adios2.readthedocs.io) is also illustrated.
|
||||
// high-order implicit (SDIRK) time integration. Optional saving
|
||||
// with ADIOS2 (adios2.readthedocs.io) is also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 2, 9 and 10 before viewing this
|
||||
// example.
|
||||
@@ -423,8 +420,8 @@ ConductionOperator::ConductionOperator(ParFiniteElementSpace &f, double al,
|
||||
void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
{
|
||||
// Compute:
|
||||
// du_dt = M^{-1}*-Ku
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
// du_dt = M^{-1}*-K(u)
|
||||
// for du_dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg(); // z = -z
|
||||
M_solver.Mult(z, du_dt);
|
||||
@@ -435,7 +432,7 @@ void ConductionOperator::ImplicitSolve(const double dt,
|
||||
{
|
||||
// Solve the equation:
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
// for du_dt
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
|
||||
+8
-24
@@ -196,12 +196,6 @@ void InitialDeformation(const Vector &x, Vector &y);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef HYPRE_USING_CUDA
|
||||
cout << "\nAs of mfem-4.3 and hypre-2.22.0 (July 2021) this example\n"
|
||||
<< "is NOT supported with the CUDA version of hypre.\n\n";
|
||||
return 255;
|
||||
#endif
|
||||
|
||||
// 1. Initialize MPI
|
||||
MPI_Session mpi;
|
||||
const int myid = mpi.WorldRank();
|
||||
@@ -444,19 +438,15 @@ JacobianPreconditioner::JacobianPreconditioner(Array<ParFiniteElementSpace *>
|
||||
void JacobianPreconditioner::Mult(const Vector &k, Vector &y) const
|
||||
{
|
||||
// Extract the blocks from the input and output vectors
|
||||
Vector disp_in;
|
||||
disp_in.MakeRef(const_cast<Vector&>(k), block_trueOffsets[0],
|
||||
block_trueOffsets[1]-block_trueOffsets[0]);
|
||||
Vector pres_in;
|
||||
pres_in.MakeRef(const_cast<Vector&>(k), block_trueOffsets[1],
|
||||
block_trueOffsets[2]-block_trueOffsets[1]);
|
||||
Vector disp_in(k.GetData() + block_trueOffsets[0],
|
||||
block_trueOffsets[1]-block_trueOffsets[0]);
|
||||
Vector pres_in(k.GetData() + block_trueOffsets[1],
|
||||
block_trueOffsets[2]-block_trueOffsets[1]);
|
||||
|
||||
Vector disp_out;
|
||||
disp_out.MakeRef(y, block_trueOffsets[0],
|
||||
block_trueOffsets[1]-block_trueOffsets[0]);
|
||||
Vector pres_out;
|
||||
pres_out.MakeRef(y, block_trueOffsets[1],
|
||||
block_trueOffsets[2]-block_trueOffsets[1]);
|
||||
Vector disp_out(y.GetData() + block_trueOffsets[0],
|
||||
block_trueOffsets[1]-block_trueOffsets[0]);
|
||||
Vector pres_out(y.GetData() + block_trueOffsets[1],
|
||||
block_trueOffsets[2]-block_trueOffsets[1]);
|
||||
|
||||
Vector temp(block_trueOffsets[1]-block_trueOffsets[0]);
|
||||
Vector temp2(block_trueOffsets[1]-block_trueOffsets[0]);
|
||||
@@ -469,9 +459,6 @@ void JacobianPreconditioner::Mult(const Vector &k, Vector &y) const
|
||||
subtract(disp_in, temp, temp2);
|
||||
|
||||
stiff_pcg->Mult(temp2, disp_out);
|
||||
|
||||
disp_out.SyncAliasMemory(y);
|
||||
pres_out.SyncAliasMemory(y);
|
||||
}
|
||||
|
||||
void JacobianPreconditioner::SetOperator(const Operator &op)
|
||||
@@ -486,10 +473,7 @@ void JacobianPreconditioner::SetOperator(const Operator &op)
|
||||
|
||||
if (!spaces[0]->GetParMesh()->Nonconforming())
|
||||
{
|
||||
#ifndef HYPRE_USING_CUDA
|
||||
// Not available yet when hypre is built with CUDA
|
||||
stiff_prec_amg->SetElasticityOptions(spaces[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
stiff_prec = stiff_prec_amg;
|
||||
|
||||
+6
-8
@@ -9,7 +9,6 @@
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
@@ -90,8 +89,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
#ifdef MFEM_USE_CEED
|
||||
args.AddOption(&algebraic_ceed, "-a", "--algebraic",
|
||||
"-no-a", "--no-algebraic",
|
||||
args.AddOption(&algebraic_ceed, "-a", "--algebraic", "-no-a", "--no-algebraic",
|
||||
"Use algebraic Ceed solver");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -199,15 +197,15 @@ int main(int argc, char *argv[])
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid
|
||||
// function corresponding to fespace. Initialize x with initial guess of
|
||||
// zero, which satisfies the boundary conditions.
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the
|
||||
// Diffusion domain integrator.
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 1
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2 -pa
|
||||
// ex22 -m ../data/inline-wedge.mesh -o 1
|
||||
// ex22 -m ../data/inline-pyramid.mesh -o 1
|
||||
// ex22 -m ../data/star.mesh -r 1 -o 2 -sigma 10.0
|
||||
//
|
||||
// Device sample runs:
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 1
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 2
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 1 -p 2 -pa
|
||||
// mpirun -np 4 ex22p -m ../data/inline-wedge.mesh -o 1
|
||||
// mpirun -np 4 ex22p -m ../data/inline-pyramid.mesh -o 1
|
||||
// mpirun -np 4 ex22p -m ../data/star.mesh -o 2 -sigma 10.0
|
||||
//
|
||||
// Device sample runs:
|
||||
|
||||
@@ -113,6 +113,7 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use Nedelec or
|
||||
// Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
@@ -141,6 +141,7 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use Nedelec or Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
+5
-3
@@ -92,7 +92,7 @@ class PMLDiagMatrixCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
CartesianPML * pml = nullptr;
|
||||
void (*Function)(const Vector &, CartesianPML *, Vector &);
|
||||
void (*Function)(const Vector &, CartesianPML * , Vector &);
|
||||
public:
|
||||
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
Vector &),
|
||||
@@ -277,8 +277,10 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Set element attributes in order to distinguish elements in the
|
||||
// PML region
|
||||
// 6. Reorient mesh in case of a tet mesh
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// Set element attributes in order to distinguish elements in the PML region
|
||||
pml->SetAttributes(mesh);
|
||||
|
||||
// 7. Define a finite element space on the mesh. Here we use the Nedelec
|
||||
|
||||
+4
-1
@@ -92,7 +92,7 @@ class PMLDiagMatrixCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
CartesianPML * pml = nullptr;
|
||||
void (*Function)(const Vector &, CartesianPML *, Vector &);
|
||||
void (*Function)(const Vector &, CartesianPML * , Vector &);
|
||||
public:
|
||||
PMLDiagMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
Vector &),
|
||||
@@ -316,6 +316,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 7a. Reorient mesh in case of a tet mesh
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 8. Set element attributes in order to distinguish elements in the PML
|
||||
pml->SetAttributes(pmesh);
|
||||
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ private:
|
||||
Vector diag(fespace.GetTrueVSize());
|
||||
bfs.Last()->AssembleDiagonal(diag);
|
||||
|
||||
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag,
|
||||
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), 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, diag,
|
||||
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
|
||||
*essentialTrueDofs.Last(), 2, fespace.GetParMesh()->GetComm());
|
||||
|
||||
AddLevel(opr.Ptr(), smoother, true, true);
|
||||
|
||||
+14
-4
@@ -295,7 +295,17 @@ int main(int argc, char *argv[])
|
||||
// element solution.
|
||||
a.RecoverFEMSolution(X, b, u);
|
||||
|
||||
// 13. Compute the various boundary integrals.
|
||||
// 13. Build a mass matrix to help solve for n.Grad(u) where 'n' is a surface
|
||||
// normal.
|
||||
BilinearForm m(&fespace);
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
m.Assemble();
|
||||
|
||||
ess_tdof_list.SetSize(0);
|
||||
OperatorPtr M;
|
||||
m.FormSystemMatrix(ess_tdof_list, M);
|
||||
|
||||
// 14. Compute the various boundary integrals.
|
||||
mfem::out << endl
|
||||
<< "Verifying boundary conditions" << endl
|
||||
<< "=============================" << endl;
|
||||
@@ -351,7 +361,7 @@ int main(int argc, char *argv[])
|
||||
<< " error " << err << endl;
|
||||
}
|
||||
|
||||
// 14. Save the refined mesh and the solution. This output can be viewed
|
||||
// 15. Save the refined mesh and the solution. This output can be viewed
|
||||
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
|
||||
{
|
||||
ofstream mesh_ofs("refined.mesh");
|
||||
@@ -362,7 +372,7 @@ int main(int argc, char *argv[])
|
||||
u.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
string title_str = h1 ? "H1" : "DG";
|
||||
@@ -375,7 +385,7 @@ int main(int argc, char *argv[])
|
||||
<< " keys 'mmc'" << flush;
|
||||
}
|
||||
|
||||
// 16. Free the used memory.
|
||||
// 17. Free the used memory.
|
||||
delete fec;
|
||||
delete mesh;
|
||||
|
||||
|
||||
+14
-4
@@ -314,7 +314,17 @@ int main(int argc, char *argv[])
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, u);
|
||||
|
||||
// 14. Compute the various boundary integrals.
|
||||
// 14. Build a mass matrix to help solve for n.Grad(u) where 'n' is a surface
|
||||
// normal.
|
||||
ParBilinearForm m(&fespace);
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
m.Assemble();
|
||||
|
||||
ess_tdof_list.SetSize(0);
|
||||
OperatorPtr M;
|
||||
m.FormSystemMatrix(ess_tdof_list, M);
|
||||
|
||||
// 15. Compute the various boundary integrals.
|
||||
mfem::out << endl
|
||||
<< "Verifying boundary conditions" << endl
|
||||
<< "=============================" << endl;
|
||||
@@ -370,7 +380,7 @@ int main(int argc, char *argv[])
|
||||
<< " error " << err << endl;
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can be
|
||||
// 16. Save the refined mesh and the solution in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
@@ -386,7 +396,7 @@ int main(int argc, char *argv[])
|
||||
u.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// 17. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
string title_str = h1 ? "H1" : "DG";
|
||||
@@ -401,7 +411,7 @@ int main(int argc, char *argv[])
|
||||
<< " keys 'mmc'" << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
// 18. Free the used memory.
|
||||
delete fec;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -81,12 +81,6 @@ Mesh * build_trapezoid_mesh(double offset)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef HYPRE_USING_CUDA
|
||||
cout << "\nAs of mfem-4.3 and hypre-2.22.0 (July 2021) this example\n"
|
||||
<< "is NOT supported with the CUDA version of hypre.\n\n";
|
||||
return 255;
|
||||
#endif
|
||||
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
@@ -366,7 +360,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
delete pmesh;
|
||||
|
||||
// HYPRE_Finalize();
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
|
||||
+23
-31
@@ -61,7 +61,6 @@ int main(int argc, char *argv[])
|
||||
bool visualization = 1;
|
||||
bool amg_elast = 0;
|
||||
bool reorder_space = false;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -79,8 +78,6 @@ int main(int argc, char *argv[])
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&reorder_space, "-nodes", "--by-nodes", "-vdim", "--by-vdim",
|
||||
"Use byNODES ordering of vector space instead of byVDIM");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -96,12 +93,7 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
@@ -117,14 +109,14 @@ int main(int argc, char *argv[])
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 5. Select the order of the finite element discretization space. For NURBS
|
||||
// 4. Select the order of the finite element discretization space. For NURBS
|
||||
// meshes, we increase the order by degree elevation.
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->DegreeElevate(order, order);
|
||||
}
|
||||
|
||||
// 6. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 1,000 elements.
|
||||
@@ -137,7 +129,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
@@ -150,7 +142,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use vector finite elements, i.e. dim copies of a scalar finite element
|
||||
// space. We use the ordering by vector dimension (the last argument of
|
||||
// the FiniteElementSpace constructor) which is expected in the systems
|
||||
@@ -183,7 +175,7 @@ int main(int argc, char *argv[])
|
||||
<< "Assembling: " << flush;
|
||||
}
|
||||
|
||||
// 9. Determine the list of true (i.e. parallel conforming) essential
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined by
|
||||
// marking only boundary attribute 1 from the mesh as essential and
|
||||
// converting it to a list of true dofs.
|
||||
@@ -192,14 +184,14 @@ int main(int argc, char *argv[])
|
||||
ess_bdr[0] = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
// 10. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system. In this case, b_i equals the
|
||||
// boundary integral of f*phi_i where f represents a "pull down" force on
|
||||
// the Neumann part of the boundary and phi_i are the basis functions in
|
||||
// the finite element fespace. The force is defined by the object f, which
|
||||
// is a vector of Coefficient objects. The fact that f is non-zero on
|
||||
// boundary attribute 2 is indicated by the use of piece-wise constants
|
||||
// coefficient for its last component.
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system. In this case, b_i equals the
|
||||
// boundary integral of f*phi_i where f represents a "pull down" force on
|
||||
// the Neumann part of the boundary and phi_i are the basis functions in
|
||||
// the finite element fespace. The force is defined by the object f, which
|
||||
// is a vector of Coefficient objects. The fact that f is non-zero on
|
||||
// boundary attribute 2 is indicated by the use of piece-wise constants
|
||||
// coefficient for its last component.
|
||||
VectorArrayCoefficient f(dim);
|
||||
for (int i = 0; i < dim-1; i++)
|
||||
{
|
||||
@@ -220,13 +212,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
b->Assemble();
|
||||
|
||||
// 11. Define the solution vector x as a parallel finite element grid
|
||||
// 10. Define the solution vector x as a parallel finite element grid
|
||||
// function corresponding to fespace. Initialize x with initial guess of
|
||||
// zero, which satisfies the boundary conditions.
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 12. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
// constants coefficient lambda and mu.
|
||||
Vector lambda(pmesh->attributes.Max());
|
||||
@@ -241,7 +233,7 @@ int main(int argc, char *argv[])
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_func, mu_func));
|
||||
|
||||
// 13. Assemble the parallel bilinear form and the corresponding linear
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
@@ -258,7 +250,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 14. Define and apply a parallel PCG solver for A X = B with the BoomerAMG
|
||||
// 13. Define and apply a parallel PCG solver for A X = B with the BoomerAMG
|
||||
// preconditioner from hypre.
|
||||
HypreBoomerAMG *amg = new HypreBoomerAMG(A);
|
||||
if (amg_elast && !a->StaticCondensationIsEnabled())
|
||||
@@ -276,11 +268,11 @@ int main(int argc, char *argv[])
|
||||
pcg->SetPreconditioner(*amg);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
// 15. Recover the parallel grid function corresponding to X. This is the
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 16. For non-NURBS meshes, make the mesh curved based on the finite element
|
||||
// 15. For non-NURBS meshes, make the mesh curved based on the finite element
|
||||
// space. This means that we define the mesh elements through a fespace
|
||||
// based transformation of the reference element. This allows us to save
|
||||
// the displaced mesh as a curved mesh when using high-order finite
|
||||
@@ -292,7 +284,7 @@ int main(int argc, char *argv[])
|
||||
pmesh->SetNodalFESpace(fespace);
|
||||
}
|
||||
|
||||
// 17. Save in parallel the displaced mesh and the inverted solution (which
|
||||
// 16. Save in parallel the displaced mesh and the inverted solution (which
|
||||
// gives the backward displacements to the original grid). This output
|
||||
// can be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
@@ -313,7 +305,7 @@ int main(int argc, char *argv[])
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 18. Send the above data by socket to a GLVis server. Use the "n" and "b"
|
||||
// 17. Send the above data by socket to a GLVis server. Use the "n" and "b"
|
||||
// keys in GLVis to visualize the displacements.
|
||||
if (visualization)
|
||||
{
|
||||
@@ -325,7 +317,7 @@ int main(int argc, char *argv[])
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 19. Free the used memory.
|
||||
// 18. Free the used memory.
|
||||
delete pcg;
|
||||
delete amg;
|
||||
delete a;
|
||||
|
||||
+1
-2
@@ -16,8 +16,6 @@
|
||||
// ex3 -m ../data/beam-hex-nurbs.mesh
|
||||
// ex3 -m ../data/amr-hex.mesh
|
||||
// ex3 -m ../data/fichera-amr.mesh
|
||||
// ex3 -m ../data/ref-prism.mesh -o 1
|
||||
// ex3 -m ../data/octahedron.mesh -o 1
|
||||
// ex3 -m ../data/star-surf.mesh -o 1
|
||||
// ex3 -m ../data/mobius-strip.mesh -f 0.1
|
||||
// ex3 -m ../data/klein-bottle.mesh -f 0.1
|
||||
@@ -115,6 +113,7 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use the Nedelec
|
||||
// finite elements of the specified order.
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
// MFEM Example 30
|
||||
//
|
||||
// Compile with: make ex30
|
||||
//
|
||||
// Sample runs: ex30 -m ../data/square-disc.mesh -o 1
|
||||
// ex30 -m ../data/square-disc.mesh -o 2
|
||||
// ex30 -m ../data/square-disc.mesh -o 2 -me 1e3
|
||||
// ex30 -m ../data/square-disc-nurbs.mesh -o 2
|
||||
// ex30 -m ../data/star.mesh -o 2 -eo 4
|
||||
// ex30 -m ../data/fichera.mesh -o 2 -me 1e4
|
||||
// ex30 -m ../data/disc-nurbs.mesh -o 2
|
||||
// ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4
|
||||
// ex30 -m ../data/star-surf.mesh -o 2
|
||||
// ex30 -m ../data/square-disc-surf.mesh -o 2
|
||||
// ex30 -m ../data/amr-quad.mesh -l 2
|
||||
//
|
||||
// Description: This is an example of adaptive mesh refinement preprocessing
|
||||
// which lowers the data oscillation [1] to a user-defined
|
||||
// relative threshold. There is no PDE being solved.
|
||||
//
|
||||
// MFEM's capability to work with both conforming and
|
||||
// nonconforming meshes is demonstrated in example 6. In some
|
||||
// problems, the material data or loading data is not sufficiently
|
||||
// resolved on the initial mesh. This missing fine scale data
|
||||
// reduces the accuracy of the solution as well as the accuracy
|
||||
// of some local error estimators. By preprocessing the mesh
|
||||
// before solving the PDE, many issues can be avoided.
|
||||
//
|
||||
// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000).
|
||||
// Data oscillation and convergence of adaptive FEM. SIAM
|
||||
// Journal on Numerical Analysis, 38(2), 466-488.
|
||||
//
|
||||
// [2] Mitchell, W. F. (2013). A collection of 2D elliptic
|
||||
// problems for testing adaptive grid refinement algorithms.
|
||||
// Applied mathematics and computation, 220, 350-364.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Piecewise-affine function which is sometimes mesh-conforming
|
||||
double affine_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
if (x < 0.0)
|
||||
{
|
||||
return 1.0 + x + y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Piecewise-constant function which is never mesh-conforming
|
||||
double jump_function(const Vector &p)
|
||||
{
|
||||
if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; }
|
||||
return 5.0;
|
||||
}
|
||||
|
||||
// Singular function derived from the Laplacian of the "steep wavefront"
|
||||
// problem in [2].
|
||||
double singular_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
double alpha = 1000.0;
|
||||
double xc = 0.75, yc = 0.5;
|
||||
double r0 = 0.7;
|
||||
double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0));
|
||||
double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) );
|
||||
double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \
|
||||
- 2 * pow(alpha,2) * r0 * r + 1.0 ),2);
|
||||
denom = max(denom,1e-8);
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int nc_limit = 1;
|
||||
int max_elems = 1e5;
|
||||
double double_max_elems = double(max_elems);
|
||||
bool visualization = true;
|
||||
double osc_threshold = 1e-3;
|
||||
int enriched_order = 5;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&nc_limit, "-l", "--nc-limit",
|
||||
"Maximum level of hanging nodes.");
|
||||
args.AddOption(&double_max_elems, "-me", "--max-elems",
|
||||
"Stop after reaching this many elements.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&osc_threshold, "-e", "--error",
|
||||
"relative data oscillation threshold.");
|
||||
args.AddOption(&enriched_order, "-eo", "--enriched_order",
|
||||
"Enriched quadrature order.");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
max_elems = int(double_max_elems);
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
|
||||
// 2. Since a NURBS mesh can currently only be refined uniformly, we need to
|
||||
// convert it to a piecewise-polynomial curved mesh. First we refine the
|
||||
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
mesh.SetCurvature(2);
|
||||
}
|
||||
|
||||
// 3. Define functions and refiner.
|
||||
FunctionCoefficient affine_coeff(affine_function);
|
||||
FunctionCoefficient jump_coeff(jump_function);
|
||||
FunctionCoefficient singular_coeff(singular_function);
|
||||
CoefficientRefiner coeffrefiner(affine_coeff, order);
|
||||
|
||||
// 4. Connect to GLVis.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost, visport);
|
||||
}
|
||||
|
||||
// 5. Define custom integration rule (optional).
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
int order_quad = 2*order + enriched_order;
|
||||
for (int i = 0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
// 6. Apply custom refiner settings.
|
||||
coeffrefiner.SetIntRule(irs);
|
||||
coeffrefiner.SetMaxElements(max_elems);
|
||||
coeffrefiner.SetThreshold(osc_threshold);
|
||||
coeffrefiner.SetNCLimit(nc_limit);
|
||||
coeffrefiner.PrintWarnings();
|
||||
|
||||
// 7. Preprocess mesh to control osc (piecewise-affine function).
|
||||
// This is mostly just a verification check. The oscillation should
|
||||
// be zero if the function is mesh-conforming and order > 0.
|
||||
coeffrefiner.PreprocessMesh(mesh);
|
||||
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 0 (affine) \n";
|
||||
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
|
||||
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
|
||||
|
||||
// 8. Preprocess mesh to control osc (jump function).
|
||||
coeffrefiner.ResetCoefficient(jump_coeff);
|
||||
coeffrefiner.PreprocessMesh(mesh);
|
||||
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 1 (discontinuous) \n";
|
||||
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
|
||||
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
|
||||
|
||||
// 9. Preprocess mesh to control osc (singular function).
|
||||
coeffrefiner.ResetCoefficient(singular_coeff);
|
||||
coeffrefiner.PreprocessMesh(mesh);
|
||||
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 2 (singular) \n";
|
||||
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
|
||||
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
|
||||
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "mesh\n" << mesh << flush;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
// MFEM Example 30 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex30p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 -me 1e3
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/star.mesh -o 2 -eo 4
|
||||
// mpirun -np 4 oscp -m ../data/fichera.mesh -o 2 -me 1e4
|
||||
// mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2
|
||||
// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2
|
||||
// mpirun -np 4 ex30p -m ../data/amr-quad.mesh -l 2
|
||||
//
|
||||
// Description: This is an example of adaptive mesh refinement preprocessing
|
||||
// which lowers the data oscillation [1] to a user-defined
|
||||
// relative threshold. There is no PDE being solved.
|
||||
//
|
||||
// MFEM's capability to work with both conforming and
|
||||
// nonconforming meshes is demonstrated in example 6. In some
|
||||
// problems, the material data or loading data is not sufficiently
|
||||
// resolved on the initial mesh. This missing fine scale data
|
||||
// reduces the accuracy of the solution as well as the accuracy
|
||||
// of some local error estimators. By preprocessing the mesh
|
||||
// before solving the PDE, many issues can be avoided.
|
||||
//
|
||||
// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000).
|
||||
// Data oscillation and convergence of adaptive FEM. SIAM
|
||||
// Journal on Numerical Analysis, 38(2), 466-488.
|
||||
//
|
||||
// [2] Mitchell, W. F. (2013). A collection of 2D elliptic
|
||||
// problems for testing adaptive grid refinement algorithms.
|
||||
// Applied mathematics and computation, 220, 350-364.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Piecewise-affine function which is sometimes mesh-conforming
|
||||
double affine_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
if (x < 0.0)
|
||||
{
|
||||
return 1.0 + x + y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Piecewise-constant function which is never mesh-conforming
|
||||
double jump_function(const Vector &p)
|
||||
{
|
||||
if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; }
|
||||
return 5.0;
|
||||
}
|
||||
|
||||
// Singular function derived from the Laplacian of the "steep wavefront"
|
||||
// problem in [2].
|
||||
double singular_function(const Vector &p)
|
||||
{
|
||||
double x = p(0), y = p(1);
|
||||
double alpha = 1000.0;
|
||||
double xc = 0.75, yc = 0.5;
|
||||
double r0 = 0.7;
|
||||
double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0));
|
||||
double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) );
|
||||
double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \
|
||||
- 2 * pow(alpha,2) * r0 * r + 1.0 ),2);
|
||||
denom = max(denom,1e-8);
|
||||
return num / denom;
|
||||
}
|
||||
|
||||
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/star.mesh";
|
||||
int order = 1;
|
||||
int nc_limit = 1;
|
||||
int max_elems = 1e5;
|
||||
double double_max_elems = double(max_elems);
|
||||
bool visualization = true;
|
||||
bool nc_simplices = true;
|
||||
double osc_threshold = 1e-3;
|
||||
int enriched_order = 5;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&nc_limit, "-l", "--nc-limit",
|
||||
"Maximum level of hanging nodes.");
|
||||
args.AddOption(&double_max_elems, "-me", "--max-elems",
|
||||
"Stop after reaching this many elements.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&osc_threshold, "-e", "--error",
|
||||
"relative data oscillation threshold.");
|
||||
args.AddOption(&enriched_order, "-eo", "--enriched_order",
|
||||
"Enriched quadrature order.");
|
||||
args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices",
|
||||
"-cs", "--conforming-simplices",
|
||||
"For simplicial meshes, enable/disable nonconforming"
|
||||
" refinement");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
max_elems = int(double_max_elems);
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
|
||||
// 2. Since a NURBS mesh can currently only be refined uniformly, we need to
|
||||
// convert it to a piecewise-polynomial curved mesh. First we refine the
|
||||
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
mesh.SetCurvature(2);
|
||||
}
|
||||
|
||||
// 3. Make sure the mesh is in the non-conforming mode to enable local
|
||||
// refinement of quadrilaterals/hexahedra. Simplices can be refined
|
||||
// either in conforming or in non-conforming mode. The conforming
|
||||
// mode however does not support dynamic partitioning.
|
||||
mesh.EnsureNCMesh(nc_simplices);
|
||||
|
||||
// 4. Define a parallel mesh by partitioning the serial mesh.
|
||||
// Once the parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// 5. Define functions and refiner.
|
||||
FunctionCoefficient affine_coeff(affine_function);
|
||||
FunctionCoefficient jump_coeff(jump_function);
|
||||
FunctionCoefficient singular_coeff(singular_function);
|
||||
CoefficientRefiner coeffrefiner(affine_coeff,order);
|
||||
|
||||
// 6. Connect to GLVis.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost, visport);
|
||||
}
|
||||
|
||||
// 7. Define custom integration rule (optional).
|
||||
const IntegrationRule *irs[Geometry::NumGeom];
|
||||
int order_quad = 2*order + enriched_order;
|
||||
for (int i=0; i < Geometry::NumGeom; ++i)
|
||||
{
|
||||
irs[i] = &(IntRules.Get(i, order_quad));
|
||||
}
|
||||
|
||||
// 8. Apply custom refiner settings.
|
||||
coeffrefiner.SetIntRule(irs);
|
||||
coeffrefiner.SetMaxElements(max_elems);
|
||||
coeffrefiner.SetThreshold(osc_threshold);
|
||||
coeffrefiner.SetNCLimit(nc_limit);
|
||||
coeffrefiner.PrintWarnings();
|
||||
|
||||
// 9. Preprocess mesh to control osc (piecewise-affine function).
|
||||
// This is mostly just a verification check. The oscillation should
|
||||
// be zero if the function is mesh-conforming and order > 0.
|
||||
coeffrefiner.PreprocessMesh(pmesh);
|
||||
|
||||
int globalNE = pmesh.GetGlobalNE();
|
||||
double osc = coeffrefiner.GetOsc();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 0 (affine) \n";
|
||||
mfem::out << "Number of Elements " << globalNE << "\n";
|
||||
mfem::out << "Osc error " << osc << "\n";
|
||||
}
|
||||
|
||||
// 10. Preprocess mesh to control osc (jump function).
|
||||
coeffrefiner.ResetCoefficient(jump_coeff);
|
||||
coeffrefiner.PreprocessMesh(pmesh);
|
||||
|
||||
globalNE = pmesh.GetGlobalNE();
|
||||
osc = coeffrefiner.GetOsc();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 1 (discontinuous) \n";
|
||||
mfem::out << "Number of Elements " << globalNE << "\n";
|
||||
mfem::out << "Osc error " << osc << "\n";
|
||||
}
|
||||
|
||||
// 11. Preprocess mesh to control osc (singular function).
|
||||
coeffrefiner.ResetCoefficient(singular_coeff);
|
||||
coeffrefiner.PreprocessMesh(pmesh);
|
||||
|
||||
globalNE = pmesh.GetGlobalNE();
|
||||
osc = coeffrefiner.GetOsc();
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n";
|
||||
mfem::out << "Function 2 (singular) \n";
|
||||
mfem::out << "Number of Elements " << globalNE << "\n";
|
||||
mfem::out << "Osc error " << osc << "\n";
|
||||
}
|
||||
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock << "mesh\n" << pmesh << flush;
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
+4
-4
@@ -16,8 +16,6 @@
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex3p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
@@ -105,7 +103,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
// HYPRE_Finalize();
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
@@ -141,7 +138,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -151,6 +150,7 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
// ex4 -m ../data/amr-hex.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/ref-prism.mesh -o 1
|
||||
// ex4 -m ../data/octahedron.mesh -o 1
|
||||
// ex4 -m ../data/star-surf.mesh -o 1
|
||||
//
|
||||
// Device sample runs:
|
||||
|
||||
+4
-3
@@ -19,8 +19,6 @@
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Device sample runs:
|
||||
@@ -137,7 +135,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them (this is needed in the ADS solver below).
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -147,6 +147,7 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
+3
-11
@@ -197,7 +197,6 @@ int main(int argc, char *argv[])
|
||||
SparseMatrix &M(mVarf->SpMat());
|
||||
SparseMatrix &B(bVarf->SpMat());
|
||||
B *= -1.;
|
||||
if (Device::IsEnabled()) { B.BuildTranspose(); }
|
||||
Bt = new TransposeOperator(&B);
|
||||
|
||||
darcyOp.SetBlock(0,0, &M);
|
||||
@@ -241,7 +240,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
SparseMatrix &M(mVarf->SpMat());
|
||||
M.GetDiag(Md);
|
||||
Md.HostReadWrite();
|
||||
|
||||
SparseMatrix &B(bVarf->SpMat());
|
||||
MinvBt = Transpose(B);
|
||||
@@ -289,18 +287,12 @@ int main(int argc, char *argv[])
|
||||
chrono.Stop();
|
||||
|
||||
if (solver.GetConverged())
|
||||
{
|
||||
std::cout << "MINRES converged in " << solver.GetNumIterations()
|
||||
<< " iterations with a residual norm of "
|
||||
<< solver.GetFinalNorm() << ".\n";
|
||||
}
|
||||
<< " iterations with a residual norm of " << solver.GetFinalNorm() << ".\n";
|
||||
else
|
||||
{
|
||||
std::cout << "MINRES did not converge in " << solver.GetNumIterations()
|
||||
<< " iterations. Residual norm is " << solver.GetFinalNorm()
|
||||
<< ".\n";
|
||||
}
|
||||
std::cout << "MINRES solver took " << chrono.RealTime() << "s.\n";
|
||||
<< " iterations. Residual norm is " << solver.GetFinalNorm() << ".\n";
|
||||
std::cout << "MINRES solver took " << chrono.RealTime() << "s. \n";
|
||||
|
||||
// 12. Create the grid functions u and p. Compute the L2 error norms.
|
||||
GridFunction u, p;
|
||||
|
||||
+13
-21
@@ -47,7 +47,6 @@ int main(int argc, char *argv[])
|
||||
int order = 2;
|
||||
bool always_snap = false;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&elem_type, "-e", "--elem",
|
||||
@@ -66,8 +65,6 @@ int main(int argc, char *argv[])
|
||||
"--snap-at-the-end",
|
||||
"If true, snap nodes to the sphere initially and after each refinement "
|
||||
"otherwise, snap only after the last refinement");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -83,12 +80,7 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Generate an initial high-order (surface) mesh on the unit sphere. The
|
||||
// 3. Generate an initial high-order (surface) mesh on the unit sphere. The
|
||||
// Mesh object represents a 2D mesh in 3 spatial dimensions. We first add
|
||||
// the elements and the vertices of the mesh, and then make it high-order
|
||||
// by specifying a finite element space for its nodes.
|
||||
@@ -154,7 +146,7 @@ int main(int argc, char *argv[])
|
||||
FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension());
|
||||
mesh->SetNodalFESpace(&nodal_fes);
|
||||
|
||||
// 5. Refine the mesh while snapping nodes to the sphere. Number of parallel
|
||||
// 4. Refine the mesh while snapping nodes to the sphere. Number of parallel
|
||||
// refinements is fixed to 2.
|
||||
for (int l = 0; l <= ref_levels; l++)
|
||||
{
|
||||
@@ -226,7 +218,7 @@ int main(int argc, char *argv[])
|
||||
SnapNodes(*pmesh);
|
||||
}
|
||||
|
||||
// 6. Define a finite element space on the mesh. Here we use isoparametric
|
||||
// 5. Define a finite element space on the mesh. Here we use isoparametric
|
||||
// finite elements -- the same as the mesh nodes.
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, &fec);
|
||||
HYPRE_BigInt size = fespace->GlobalTrueVSize();
|
||||
@@ -235,7 +227,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
|
||||
// the basis functions in the finite element fespace.
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
@@ -245,27 +237,27 @@ int main(int argc, char *argv[])
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(rhs_coef));
|
||||
b->Assemble();
|
||||
|
||||
// 8. Define the solution vector x as a finite element grid function
|
||||
// 7. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero.
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 9. Set up the bilinear form a(.,.) on the finite element space
|
||||
// 8. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// and Mass domain integrators.
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddDomainIntegrator(new MassIntegrator(one));
|
||||
|
||||
// 10. Assemble the parallel linear system, applying any transformations
|
||||
// such as: parallel assembly, applying conforming constraints, etc.
|
||||
// 9. Assemble the parallel linear system, applying any transformations
|
||||
// such as: parallel assembly, applying conforming constraints, etc.
|
||||
a->Assemble();
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
Array<int> empty_tdof_list;
|
||||
a->FormLinearSystem(empty_tdof_list, x, *b, A, X, B);
|
||||
|
||||
// 11. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
|
||||
// 10. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
|
||||
// preconditioner from hypre. Extract the parallel grid function x
|
||||
// corresponding to the finite element approximation X. This is the local
|
||||
// solution on each processor.
|
||||
@@ -281,14 +273,14 @@ int main(int argc, char *argv[])
|
||||
delete a;
|
||||
delete b;
|
||||
|
||||
// 12. Compute and print the L^2 norm of the error.
|
||||
// 11. Compute and print the L^2 norm of the error.
|
||||
double err = x.ComputeL2Error(sol_coef);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\nL2 norm of error: " << err << endl;
|
||||
}
|
||||
|
||||
// 13. Save the refined mesh and the solution. This output can be viewed
|
||||
// 12. Save the refined mesh and the solution. This output can be viewed
|
||||
// later using GLVis: "glvis -np <np> -m sphere_refined -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
@@ -304,7 +296,7 @@ int main(int argc, char *argv[])
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 14. Send the solution by socket to a GLVis server.
|
||||
// 13. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -315,7 +307,7 @@ int main(int argc, char *argv[])
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
// 14. Free the used memory.
|
||||
delete pcg;
|
||||
delete amg;
|
||||
delete fespace;
|
||||
|
||||
@@ -106,6 +106,7 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 6. Define the trial, interfacial (trace) and test DPG spaces:
|
||||
// - The trial space, x0_space, contains the non-interfacial unknowns and
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# Jupyter Notebooks using xeus-cling
|
||||
|
||||
[](https://mybinder.org/v2/gh/mfem/mfem/master?filepath=examples%2Fjupyter%2Fex.ipynb)
|
||||
|
||||
[xeus-cling](https://github.com/jupyter-xeus/xeus-cling) is a C++ Jupyter Kernel based on [cling](https://github.com/root-project/cling),
|
||||
which can be used to create interactive C++ MFEM and GLVis notebooks.
|
||||
|
||||
Click on the `binder` button above for an interactive example.
|
||||
|
||||
## Installing Locally
|
||||
|
||||
In order to run notebooks locally you will need `xeus-cling` along with `mfem` and `xglvis`. We recommend you use
|
||||
[miniconda](https://docs.conda.io/en/latest/miniconda.html) or, if you already have it installed,
|
||||
[conda](https://docs.conda.io/projects/conda/en/latest/).
|
||||
|
||||
1. Follow the install steps on https://github.com/jupyter-xeus/xeus-cling to install the C++ kernels
|
||||
2. Build and install a _shared_ version of mfem
|
||||
* for example: `make serial SHARED=YES`
|
||||
3. Install [pyglvis](https://github.com/glvis/pyglvis)
|
||||
* for the widget frontend
|
||||
4. Get [xeus-glvis](https://github.com/glvis/xeus-glvis) and `cp` the header to `{PREFIX}/glvis/xglvis.hpp`
|
||||
* (this could be improved)
|
||||
|
||||
## Running Locally
|
||||
|
||||
Once you've installed Jupyter, the C++ Kernel, mfem, and glvis start the notebook server (`jupyter-notebook`)
|
||||
and open an existing example or a new `C++ 1x` kernel.
|
||||
|
||||
You will _always_ need to `#pragma cling load("mfem")` and you may need to point the `cling` runtime at your
|
||||
mfem and/or glvis installs, do this with the
|
||||
`#pragma cling` [statements](https://xeus-cling.readthedocs.io/en/latest/build_options.html#using-third-party-libraries).
|
||||
@@ -1,155 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "owned-extraction",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Load the MFEM library\n",
|
||||
"\n",
|
||||
"Any non-default libraries must be loaded before you can `#include` files that use them. For more info see the [xeus-cling help](https://xeus-cling.readthedocs.io/en/latest/build_options.html)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "waiting-portrait",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pragma cling load(\"mfem\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "foreign-recycling",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## MFEM Example 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "public-white",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Laplace problem\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"-\\Delta u = 1\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"with homogeneous Dirichlet boundary conditions $u=0$.\n",
|
||||
"\n",
|
||||
"The example illustrates the use of the basic MFEM classes for defining the mesh, finite element space, as well as linear and bilinear forms corresponding to the left-hand side and right-hand side of the discrete linear system.\n",
|
||||
"\n",
|
||||
"Compare with MFEM's [ex1.cpp](https://github.com/mfem/mfem/blob/master/examples/ex1.cpp) and PyMFEM's [ex1.py](https://github.com/mfem/PyMFEM/blob/master/examples/ex1.py)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "protective-darkness",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#include <fstream>\n",
|
||||
"#include <iostream>\n",
|
||||
"#include <sstream>\n",
|
||||
"\n",
|
||||
"#include <mfem.hpp>\n",
|
||||
"#include <glvis/xglvis.hpp>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "falling-monkey",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"using namespace std;\n",
|
||||
"using namespace mfem;\n",
|
||||
"\n",
|
||||
"Mesh mesh = Mesh::MakeCartesian2D(5, 5, Element::TRIANGLE);\n",
|
||||
"mesh.UniformRefinement();\n",
|
||||
"\n",
|
||||
"H1_FECollection fec(2, mesh.Dimension());\n",
|
||||
"\n",
|
||||
"FiniteElementSpace fespace(&mesh, &fec);\n",
|
||||
"cout << \"Number of finite element unknowns: \" << fespace.GetTrueVSize() << endl;\n",
|
||||
"\n",
|
||||
"Array<int> ess_tdof_list;\n",
|
||||
"if (mesh.bdr_attributes.Size())\n",
|
||||
"{\n",
|
||||
" Array<int> ess_bdr(mesh.bdr_attributes.Max());\n",
|
||||
" ess_bdr = 1;\n",
|
||||
" fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"LinearForm b(&fespace);\n",
|
||||
"ConstantCoefficient one(1.0);\n",
|
||||
"b.AddDomainIntegrator(new DomainLFIntegrator(one));\n",
|
||||
"b.Assemble();\n",
|
||||
"\n",
|
||||
"GridFunction x(&fespace);\n",
|
||||
"x = 0.0;\n",
|
||||
"\n",
|
||||
"BilinearForm a(&fespace);\n",
|
||||
"a.AddDomainIntegrator(new DiffusionIntegrator(one));\n",
|
||||
"a.Assemble();\n",
|
||||
"\n",
|
||||
"OperatorPtr A;\n",
|
||||
"Vector B, X;\n",
|
||||
"a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);\n",
|
||||
"\n",
|
||||
"cout << \"Size of linear system: \" << A->Height() << endl;\n",
|
||||
"\n",
|
||||
"GSSmoother M((SparseMatrix&)(*A));\n",
|
||||
"PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);\n",
|
||||
"a.RecoverFEMSolution(X, b, x);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "hawaiian-republican",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## GLVis Visualization\n",
|
||||
"\n",
|
||||
"For now we save the computational mesh and finite element solution in a string and pass that to the glvis widget, see https://github.com/glvis/xeus-glvis for the widget backend and https://github.com/GLVis/pyglvis/tree/master/js for the widget frontend."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ordinary-equation",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"std::stringstream ss;\n",
|
||||
"ss << \"solution\\n\" << mesh << x << flush;\n",
|
||||
"\n",
|
||||
"auto glv = glvis::glvis();\n",
|
||||
"glv.plot(ss.str() + \"keys Rjml\"); // the `+ \"keys ....\"' is optional\n",
|
||||
"glv"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "C++14",
|
||||
"language": "C++14",
|
||||
"name": "xcpp14"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": "text/x-c++src",
|
||||
"file_extension": ".cpp",
|
||||
"mimetype": "text/x-c++src",
|
||||
"name": "c++",
|
||||
"version": "14"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
+2
-13
@@ -22,13 +22,10 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p \
|
||||
ex24p ex25p ex26p
|
||||
ex25p ex26p ex27p ex28p ex29p
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
@@ -102,14 +99,6 @@ RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
|
||||
%-test-seq: %
|
||||
@$(call mfem-test,$<,, Serial example)
|
||||
%-test-par-cuda: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel CUDA example,-d cuda)
|
||||
%-test-seq-cuda: %
|
||||
@$(call mfem-test,$<,, Serial CUDA example,-d cuda)
|
||||
%-test-par-hip: %
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel HIP example,-d hip)
|
||||
%-test-seq-hip: %
|
||||
@$(call mfem-test,$<,, Serial HIP example,-d hip)
|
||||
|
||||
# Testing: Specific execution options
|
||||
ex0-test-seq: ex0
|
||||
|
||||
@@ -0,0 +1,960 @@
|
||||
#include "DofMapsDST.hpp"
|
||||
|
||||
double testcoeff(const Vector & x)
|
||||
{
|
||||
return sin(3*M_PI*(x.Sum()));
|
||||
}
|
||||
|
||||
int get_rank(int tdof, std::vector<int> & tdof_offsets)
|
||||
{
|
||||
int size = tdof_offsets.size();
|
||||
if (size == 1) { return 0; }
|
||||
std::vector<int>::iterator up;
|
||||
up=std::upper_bound(tdof_offsets.begin(), tdof_offsets.end(),tdof); //
|
||||
return std::distance(tdof_offsets.begin(),up)-1;
|
||||
}
|
||||
|
||||
void ComputeTdofOffsets(const MPI_Comm & comm, const ParFiniteElementSpace * pfes,
|
||||
std::vector<int> & tdof_offsets)
|
||||
{
|
||||
int num_procs;
|
||||
MPI_Comm_size(comm, &num_procs);
|
||||
tdof_offsets.resize(num_procs);
|
||||
int mytoffset = pfes->GetMyTDofOffset();
|
||||
MPI_Allgather(&mytoffset,1,MPI_INT,&tdof_offsets[0],1,MPI_INT,comm);
|
||||
}
|
||||
|
||||
void GetSubdomainijk(int ip, const Array<int> nxyz, Array<int> & ijk)
|
||||
{
|
||||
ijk.SetSize(3);
|
||||
ijk[2] = ip/(nxyz[0]*nxyz[1]);
|
||||
ijk[1] = (ip-ijk[2]*nxyz[0]*nxyz[1])/nxyz[0];
|
||||
ijk[0] = (ip-ijk[2]*nxyz[0]*nxyz[1])%nxyz[0];
|
||||
}
|
||||
void GetDirectionijk(int id, Array<int> & ijk)
|
||||
{
|
||||
ijk.SetSize(3);
|
||||
int n = 3;
|
||||
ijk[2] = id/(n*n) - 1;
|
||||
ijk[1] = (id-(ijk[2]+1)*n*n)/n - 1;
|
||||
ijk[0] = (id-(ijk[2]+1)*n*n)%n - 1;
|
||||
}
|
||||
|
||||
int GetSubdomainId(const Array<int> nxyz, Array<int> & ijk)
|
||||
{
|
||||
int dim=ijk.Size();
|
||||
int k = (dim==2)? 0 : ijk[2];
|
||||
return k*nxyz[1]*nxyz[0] + ijk[1]*nxyz[0] + ijk[0];
|
||||
}
|
||||
|
||||
int GetDirectionId(const Array<int> & ijk)
|
||||
{
|
||||
int n = 3;
|
||||
int dim = ijk.Size();
|
||||
int k = (dim == 2) ? -1 : ijk[2];
|
||||
return (k+1)*n*n + (ijk[1]+1)*n + ijk[0]+1;
|
||||
}
|
||||
|
||||
void DofMaps::Init()
|
||||
{
|
||||
comm = pfes->GetComm();
|
||||
MPI_Comm_size(comm, &num_procs);
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
|
||||
dim = pfes->GetParMesh()->Dimension();
|
||||
ComputeTdofOffsets(comm, pfes, tdof_offsets);
|
||||
myelemoffset = part->myelem_offset;
|
||||
mytoffset = pfes->GetMyTDofOffset();
|
||||
subdomain_rank = part->subdomain_rank;
|
||||
nrsubdomains = part->nrsubdomains;
|
||||
nxyz.SetSize(3);
|
||||
for (int i = 0; i<3; i++) { nxyz[i] = part->nxyz[i]; }
|
||||
|
||||
//compute sign factors for tdofs
|
||||
int lsize = pfes->GetVSize();
|
||||
int tsize = pfes->GetTrueVSize();
|
||||
tdof_sign.SetSize(tsize);
|
||||
for (int i = 0; i<lsize; i++)
|
||||
{
|
||||
int j = pfes->GetGlobalTDofNumber(i);
|
||||
if (j<mytoffset || j>=mytoffset+tsize) continue;
|
||||
tdof_sign[j-mytoffset] = pfes->GetDofSign(i);
|
||||
}
|
||||
}
|
||||
|
||||
DofMaps::DofMaps(ParFiniteElementSpace *pfes_, ParMeshPartition * part_, bool CompFlag_)
|
||||
: pfes(pfes_), part(part_), CompFlag(CompFlag_)
|
||||
{
|
||||
Init();
|
||||
Setup();
|
||||
}
|
||||
|
||||
void DofMaps::Setup()
|
||||
{
|
||||
// Setup the local FiniteElementSpaces
|
||||
const FiniteElementCollection * fec = pfes->FEColl();
|
||||
fes.SetSize(nrsubdomains);
|
||||
for (int i = 0; i<nrsubdomains; i++)
|
||||
{
|
||||
fes[i] = nullptr; // initialize with null on all procs
|
||||
if (myid == subdomain_rank[i])
|
||||
{
|
||||
fes[i] = new FiniteElementSpace(part->subdomain_mesh[i],fec);
|
||||
}
|
||||
}
|
||||
// cout << "Computing Overlap Tdofs" << endl;
|
||||
SubdomainToSubdomainMapsSetup();
|
||||
// TestSubdomainToSubdomainMaps();
|
||||
|
||||
SubdomainToGlobalMapsSetup();
|
||||
// TestSubdomainToGlobalMaps();
|
||||
}
|
||||
|
||||
void DofMaps::SubdomainToSubdomainMapsSetup()
|
||||
{
|
||||
ComputeOvlpElems();
|
||||
ComputeOvlpTdofs();
|
||||
}
|
||||
|
||||
void DofMaps::AddElementToOvlpLists(int l, int iel,
|
||||
const Array<bool> & neg, const Array<bool> & pos)
|
||||
{
|
||||
int kbeg = (dim == 2) ? 0 : -1;
|
||||
int kend = (dim == 2) ? 0 : 1;
|
||||
Array<int> dijk(3);
|
||||
for (int k = kbeg; k<=kend; k++)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
if (k == -1 && !neg[2]) continue;
|
||||
if (k == 1 && !pos[2]) continue;
|
||||
}
|
||||
|
||||
for (int j = -1; j<=1; j++)
|
||||
{
|
||||
if (j== -1 && !neg[1]) continue;
|
||||
if (j== 1 && !pos[1]) continue;
|
||||
for (int i = -1; i<=1; i++)
|
||||
{
|
||||
// cases to skip
|
||||
if (i==-1 && !neg[0]) continue;
|
||||
if (i== 1 && !pos[0]) continue;
|
||||
|
||||
if (i==0 && j==0 && k == 0) continue;
|
||||
dijk[0] = i; dijk[1] = j; dijk[2] = (dim==2)?-1 : k;
|
||||
int DirId = GetDirectionId(dijk);
|
||||
OvlpElems[l][DirId].Append(iel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofMaps::ComputeOvlpElems()
|
||||
{
|
||||
// first compute the element in the overlaps
|
||||
OvlpElems.resize(nrsubdomains);
|
||||
int nlayers = 2*part->OvlpNlayers;
|
||||
// loop through subdomains
|
||||
for (int l = 0; l<nrsubdomains; l++)
|
||||
{
|
||||
if (myid == subdomain_rank[l])
|
||||
{
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(l,nxyz,ijk);
|
||||
Mesh * mesh = part->subdomain_mesh[l];
|
||||
OvlpElems[l].resize(pow(3,dim));
|
||||
Vector pmin, pmax;
|
||||
mesh->GetBoundingBox(pmin,pmax);
|
||||
double h = part->MeshSize;
|
||||
// loop through the elements in the mesh and assign them to the
|
||||
// appropriate lists of overlaps
|
||||
for (int iel=0; iel< mesh->GetNE(); iel++)
|
||||
{
|
||||
// Get element center
|
||||
Vector center(dim);
|
||||
int geom = mesh->GetElementBaseGeometry(iel);
|
||||
ElementTransformation * tr = mesh->GetElementTransformation(iel);
|
||||
tr->Transform(Geometries.GetCenter(geom),center);
|
||||
|
||||
Array<bool> pos(dim); pos = false;
|
||||
Array<bool> neg(dim); neg = false;
|
||||
// loop through dimensions
|
||||
for (int d=0;d<dim; d++)
|
||||
{
|
||||
if (ijk[d]>0 && center[d] < pmin[d]+h*nlayers)
|
||||
{
|
||||
neg[d] = true;
|
||||
}
|
||||
|
||||
if (ijk[d]<nxyz[d]-1 && center[d] > pmax[d]-h*nlayers)
|
||||
{
|
||||
pos[d] = true;
|
||||
}
|
||||
}
|
||||
// Add the element to the appropriate lists
|
||||
AddElementToOvlpLists(l,iel,neg,pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofMaps::ComputeOvlpTdofs()
|
||||
{
|
||||
OvlpTDofs.resize(nrsubdomains);
|
||||
int nrneighbors = pow(3,dim); // including its self
|
||||
|
||||
// loop through subdomains
|
||||
for (int l = 0; l<nrsubdomains; l++)
|
||||
{
|
||||
if (myid != subdomain_rank[l]) continue;
|
||||
int ntdofs = fes[l]->GetTrueVSize();
|
||||
Array<int> tdof_marker(ntdofs);
|
||||
OvlpTDofs[l].resize(nrneighbors);
|
||||
// loop through neighboring directions/neighbors
|
||||
for (int d=0; d<nrneighbors; d++)
|
||||
{
|
||||
tdof_marker = 0;
|
||||
Array<int> tdoflist;
|
||||
// Get the direction
|
||||
Array<int> dijk;
|
||||
GetDirectionijk(l,dijk);
|
||||
int nel = OvlpElems[l][d].Size();
|
||||
Array<int>Elems = OvlpElems[l][d];
|
||||
for (int iel = 0; iel<nel; ++iel)
|
||||
{
|
||||
int jel = Elems[iel];
|
||||
Array<int> ElemDofs;
|
||||
|
||||
fes[l]->GetElementDofs(jel,ElemDofs);
|
||||
int ndof = ElemDofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int dof_ = ElemDofs[i];
|
||||
int dof = (dof_ >= 0) ? dof_ : abs(dof_) - 1;
|
||||
if (!tdof_marker[dof])
|
||||
{
|
||||
tdoflist.Append(dof); // dofs of ip0 in ovlp
|
||||
tdof_marker[dof] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
OvlpTDofs[l][d] = tdoflist;
|
||||
if (CompFlag)
|
||||
{
|
||||
for (int i=0; i<tdoflist.Size(); i++)
|
||||
{
|
||||
tdoflist[i] += fes[l]->GetTrueVSize();
|
||||
}
|
||||
OvlpTDofs[l][d].Append(tdoflist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofMaps::PrintOvlpTdofs()
|
||||
{
|
||||
int nrneighbors = pow(3,dim); // including its self
|
||||
if (myid == 0)
|
||||
{
|
||||
for (int i = 0; i<nrsubdomains; i++)
|
||||
{
|
||||
if (myid != subdomain_rank[i]) continue;
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(i,nxyz,ijk);
|
||||
cout << "subdomain = " ; ijk.Print();
|
||||
cout << "myid = " << myid << endl;
|
||||
cout << "ip = " << i << endl;
|
||||
for (int d = 0; d<nrneighbors; d++)
|
||||
{
|
||||
Array<int> dijk;
|
||||
GetDirectionijk(d,dijk);
|
||||
cout << "direction = " ; dijk.Print();
|
||||
|
||||
if (OvlpTDofs[i][d].Size())
|
||||
{
|
||||
cout << "OvlpTdofs = " ;
|
||||
OvlpTDofs[i][d].Print(cout,OvlpTDofs[i][d].Size() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofMaps::TransferToNeighbors(const Array<int> & SubdomainIds, const Array<Vector *> & x,
|
||||
std::vector<std::vector<Vector * >> & OvlpSol)
|
||||
{
|
||||
// 2D for now....
|
||||
MFEM_VERIFY(SubdomainIds.Size() == x.Size(), "TransferToNeighbors: Size inconsistency");
|
||||
int nrsendIds = SubdomainIds.Size();
|
||||
int nrneighbors = pow(3,dim);
|
||||
MPI_Request *recv_requests = new MPI_Request[nrsendIds*nrneighbors];
|
||||
MPI_Request *send_requests = new MPI_Request[nrsendIds*nrneighbors];
|
||||
MPI_Status *recv_statuses = new MPI_Status[nrsendIds*nrneighbors];
|
||||
MPI_Status *send_statuses = new MPI_Status[nrsendIds*nrneighbors];
|
||||
Array<Vector * > send_buffer(nrsendIds*nrneighbors);
|
||||
Array<Vector * > recv_buffer(nrsendIds*nrneighbors);
|
||||
int send_counter = 0;
|
||||
int recv_counter = 0;
|
||||
for (int is = 0; is<nrsendIds; is++)
|
||||
{
|
||||
int i0 = SubdomainIds[is];
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(i0,nxyz,ijk);
|
||||
for (int d=0;d<nrneighbors; d++)
|
||||
{
|
||||
Array<int>directions;
|
||||
GetDirectionijk(d,directions);
|
||||
|
||||
if (dim == 2 && directions[0] == 0 && directions[1] == 0) continue;
|
||||
if (dim == 3 && directions[0] == 0
|
||||
&& directions[1] == 0
|
||||
&& directions[2] == 0) continue;
|
||||
int i = ijk[0] + directions[0];
|
||||
if (i<0 || i>=nxyz[0]) continue;
|
||||
int j = ijk[1] + directions[1];
|
||||
if (j<0 || j>=nxyz[1]) continue;
|
||||
int k = (dim ==3 ) ? ijk[2] + directions[2] : 0;
|
||||
if (k<0 || k>=nxyz[2]) continue;
|
||||
Array<int>ijk1(3);
|
||||
ijk1[0] = i;
|
||||
ijk1[1] = j;
|
||||
ijk1[2] = k;
|
||||
int i1 = GetSubdomainId(nxyz,ijk1);
|
||||
if (myid == subdomain_rank[i0])
|
||||
{
|
||||
Array<int> tdofs0 = OvlpTDofs[i0][d]; // map of dofs in the overlap
|
||||
send_buffer[send_counter] = new Vector(tdofs0.Size());
|
||||
x[is]->GetSubVector(tdofs0,*send_buffer[send_counter]);
|
||||
// Destination rank
|
||||
int dest = subdomain_rank[i1];
|
||||
int tag = i0 * nrneighbors + d;
|
||||
|
||||
int count = tdofs0.Size();
|
||||
MPI_Isend(send_buffer[send_counter]->GetData(),count,MPI_DOUBLE,dest,
|
||||
tag,comm,&send_requests[send_counter]);
|
||||
send_counter++;
|
||||
|
||||
}
|
||||
if (myid == subdomain_rank[i1])
|
||||
{
|
||||
Array<int> direction1(3); direction1 = -1;
|
||||
for (int dd=0;dd<dim;dd++)
|
||||
{
|
||||
direction1[dd] = -directions[dd];
|
||||
}
|
||||
int d1 = GetDirectionId(direction1);
|
||||
|
||||
int count = OvlpTDofs[i1][d1].Size();
|
||||
recv_buffer[recv_counter] = new Vector(count);
|
||||
int src = subdomain_rank[i0];
|
||||
int tag = i0 * nrneighbors + d;
|
||||
MPI_Irecv(recv_buffer[recv_counter]->GetData(), count,MPI_DOUBLE,src,
|
||||
tag,comm, &recv_requests[recv_counter]);
|
||||
recv_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
MPI_Waitall(send_counter, send_requests, send_statuses);
|
||||
MPI_Waitall(recv_counter, recv_requests, recv_statuses);
|
||||
|
||||
delete [] send_statuses;
|
||||
delete [] send_requests;
|
||||
delete [] recv_statuses;
|
||||
delete [] recv_requests;
|
||||
|
||||
for (int i = 0; i<send_counter; i++)
|
||||
{
|
||||
delete send_buffer[i];
|
||||
}
|
||||
send_buffer.DeleteAll();
|
||||
|
||||
|
||||
// Extract the transfered solutions
|
||||
recv_counter = 0;
|
||||
for (int is = 0; is<nrsendIds; is++)
|
||||
{
|
||||
int i0 = SubdomainIds[is];
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(i0,nxyz,ijk);
|
||||
for (int d=0;d<nrneighbors; d++)
|
||||
{
|
||||
Array<int>directions;
|
||||
GetDirectionijk(d,directions);
|
||||
if (dim == 2 && directions[0] == 0 && directions[1] == 0) continue;
|
||||
if (dim == 3 && directions[0] == 0
|
||||
&& directions[1] == 0
|
||||
&& directions[2] == 0) continue;
|
||||
int i = ijk[0] + directions[0];
|
||||
if (i<0 || i>=nxyz[0]) continue;
|
||||
int j = ijk[1] + directions[1];
|
||||
if (j<0 || j>=nxyz[1]) continue;
|
||||
int k = (dim ==3 ) ? ijk[2] + directions[2] : 0;
|
||||
if (k<0 || k>=nxyz[2]) continue;
|
||||
|
||||
Array<int>ijk1(3);
|
||||
ijk1[0] = i;
|
||||
ijk1[1] = j;
|
||||
ijk1[2] = k;
|
||||
int i1 = GetSubdomainId(nxyz,ijk1);
|
||||
if (myid == subdomain_rank[i1])
|
||||
{
|
||||
Array<int> direction1(3); direction1 = -1;
|
||||
for (int d=0;d<dim;d++)
|
||||
{
|
||||
direction1[d] = -directions[d];
|
||||
}
|
||||
int d1 = GetDirectionId(direction1);
|
||||
Array<int> tdofs1 = OvlpTDofs[i1][d1];
|
||||
if (!OvlpSol[i1][d1])
|
||||
{
|
||||
OvlpSol[i1][d1] = new Vector(2*fes[i1]->GetTrueVSize());
|
||||
}
|
||||
*OvlpSol[i1][d1] = 0.0;
|
||||
OvlpSol[i1][d1]->SetSubVector(tdofs1,*recv_buffer[recv_counter]);
|
||||
recv_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i<recv_counter; i++)
|
||||
{
|
||||
delete recv_buffer[i];
|
||||
}
|
||||
recv_buffer.DeleteAll();
|
||||
}
|
||||
|
||||
void DofMaps::TestSubdomainToSubdomainMaps()
|
||||
{
|
||||
// testing inter-subdomain communication
|
||||
FunctionCoefficient c1(testcoeff);
|
||||
int nrsub = nrsubdomains;
|
||||
Array<int> subdomain_ids(nrsub);
|
||||
Array<Vector*> x(nrsub);
|
||||
for (int i = 0; i<nrsub; i++)
|
||||
{
|
||||
x[i] = nullptr;
|
||||
subdomain_ids[i] = i;
|
||||
if (fes[i])
|
||||
{
|
||||
ComplexGridFunction gf(fes[i]);
|
||||
gf = 0.0;
|
||||
gf.ProjectCoefficient(c1,c1);
|
||||
x[i] = new Vector(2*fes[i]->GetTrueVSize());
|
||||
*x[i] = gf;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<Vector * >> OvlpSol;
|
||||
|
||||
OvlpSol.resize(nrsubdomains);
|
||||
int nrneighbors = pow(3,dim);
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == subdomain_rank[ip])
|
||||
{
|
||||
OvlpSol[ip].resize(nrneighbors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TransferToNeighbors(subdomain_ids,x,OvlpSol);
|
||||
|
||||
string keys = "keys amrRljc\n";
|
||||
for (int i0 = 0 ; i0< nrsubdomains; i0++)
|
||||
{
|
||||
if (fes[i0])
|
||||
{
|
||||
ComplexGridFunction gf0(fes[i0]);
|
||||
for (int d = 0; d<nrneighbors; d++)
|
||||
{
|
||||
if(OvlpSol[i0][d])
|
||||
{
|
||||
Array<int>dijk;
|
||||
GetDirectionijk(d,dijk);
|
||||
Array<int>ijk;
|
||||
GetSubdomainijk(i0,nxyz,ijk);
|
||||
ostringstream oss;
|
||||
oss << "myid: " << myid
|
||||
<< ", subdomain: (" << ijk[0] << "," << ijk[1] <<")"
|
||||
<< ", direction: (" << dijk[0] << "," << dijk[1] <<")";
|
||||
|
||||
gf0 = 0.0;
|
||||
gf0.real().SetVector(*OvlpSol[i0][d],0);
|
||||
gf0.imag().SetVector(*OvlpSol[i0][d],fes[i0]->GetTrueVSize());
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *(part->subdomain_mesh[i0]) << gf0.real()
|
||||
<< keys
|
||||
<< "window_title '" << oss.str() << "'" << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i<nrsub; i++)
|
||||
{
|
||||
delete x[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DofMaps::SubdomainToGlobalMapsSetup()
|
||||
{
|
||||
// workspace for MPI_AlltoAll
|
||||
send_count.SetSize(num_procs); send_count = 0;
|
||||
send_displ.SetSize(num_procs); send_displ = 0;
|
||||
recv_count.SetSize(num_procs); recv_count = 0;
|
||||
recv_displ.SetSize(num_procs); recv_displ = 0;
|
||||
|
||||
// 1. Communicate to the subdomain rank the list of tdofs
|
||||
// a. Compute send count
|
||||
for (int ip = 0; ip<nrsubdomains; ++ip)
|
||||
{
|
||||
// avoid any communication if on subdomain rank
|
||||
int nel = part->local_element_map[ip].Size();
|
||||
|
||||
for (int iel = 0; iel<nel; iel++)
|
||||
{
|
||||
int elem_idx = part->local_element_map[ip][iel] - myelemoffset;
|
||||
// int ndofs = local_tdofs[ip].Size();
|
||||
int ndofs = pfes->GetFE(elem_idx)->GetDof();
|
||||
|
||||
send_count[subdomain_rank[ip]] += 2 + ndofs;
|
||||
}
|
||||
}
|
||||
// b. Compute receive count
|
||||
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
sbuff_size = send_count.Sum();
|
||||
rbuff_size = recv_count.Sum();
|
||||
// c. Allocate and fill the send buffer
|
||||
Array<int> sendbuf(sbuff_size); sendbuf = 0;
|
||||
Array<int> soffs(num_procs); soffs = 0;
|
||||
for (int ip = 0; ip<nrsubdomains; ++ip)
|
||||
{
|
||||
int nel = part->local_element_map[ip].Size();
|
||||
for (int iel = 0; iel<nel; iel++)
|
||||
{
|
||||
int elem_idx = part->local_element_map[ip][iel] - myelemoffset;
|
||||
Array<int>ElemDofs;
|
||||
pfes->GetElementDofs(elem_idx,ElemDofs);
|
||||
int ndofs = ElemDofs.Size();
|
||||
|
||||
int j = send_displ[subdomain_rank[ip]] + soffs[subdomain_rank[ip]];
|
||||
sendbuf[j] = ip;
|
||||
sendbuf[j+1] = ndofs;
|
||||
|
||||
for (int k = 0; k < ndofs ; ++k)
|
||||
{
|
||||
int edof_ = ElemDofs[k];
|
||||
int edof = (edof_ >= 0) ? edof_ : abs(edof_) - 1;
|
||||
sendbuf[j+2+k] = pfes->GetGlobalTDofNumber(edof);
|
||||
}
|
||||
soffs[subdomain_rank[ip]] += 2 + ndofs;
|
||||
}
|
||||
}
|
||||
|
||||
// d. Communication
|
||||
Array<int> recvbuf(rbuff_size);
|
||||
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_INT, recvbuf,
|
||||
recv_count, recv_displ, MPI_INT, comm);
|
||||
|
||||
// 3. Extract from recv_buffer
|
||||
std::vector<Array<int>> global_tdofs(nrsubdomains);
|
||||
int k=0;
|
||||
while (k<rbuff_size)
|
||||
{
|
||||
int ip = recvbuf[k++];
|
||||
int ndofs = recvbuf[k++];
|
||||
for (int i = 0; i < ndofs; ++i)
|
||||
{
|
||||
global_tdofs[ip].Append(recvbuf[i+k]);
|
||||
}
|
||||
k += ndofs;
|
||||
}
|
||||
|
||||
SubdomainGTrueDofs.resize(nrsubdomains);
|
||||
// 4. Construct SubdomainTdof to Global mesh tdof maps
|
||||
for (int ip=0; ip<nrsubdomains; ++ip)
|
||||
{
|
||||
if (myid != subdomain_rank[ip]) continue;
|
||||
int nrdof = fes[ip]->GetTrueVSize();
|
||||
|
||||
SubdomainGTrueDofs[ip].SetSize(nrdof);
|
||||
int nel = part->element_map[ip].Size();
|
||||
int k = 0;
|
||||
for (int iel = 0; iel<nel; ++iel)
|
||||
{
|
||||
Array<int> elem_dofs;
|
||||
fes[ip]->GetElementDofs(iel,elem_dofs);
|
||||
int ndof = elem_dofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int edof_ = elem_dofs[i];
|
||||
int edof = (edof_ >= 0) ? edof_ : abs(edof_) - 1;
|
||||
// rearranging dofs from serial fespace to pfes ordering
|
||||
SubdomainGTrueDofs[ip][edof] = global_tdofs[ip][k++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Communicate SubdomainGTrueDofs to participating ranks
|
||||
send_count = 0; send_displ = 0;
|
||||
recv_count = 0; recv_displ = 0;
|
||||
|
||||
for (int ip = 0; ip < nrsubdomains; ++ip)
|
||||
{
|
||||
if (myid != subdomain_rank[ip]) continue;
|
||||
int ndofs = SubdomainGTrueDofs[ip].Size();
|
||||
for (int i = 0; i<ndofs; ++i)
|
||||
{
|
||||
int tdof = SubdomainGTrueDofs[ip][i];
|
||||
int rank = get_rank(tdof,tdof_offsets);
|
||||
if (rank == subdomain_rank[ip]) continue; // <--------------
|
||||
send_count[rank] += 2; // 1 for the dof and 1 for the ip that goes to
|
||||
}
|
||||
}
|
||||
|
||||
// communicate so that recv_count is constructed
|
||||
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
|
||||
//
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
sbuff_size = send_count.Sum();
|
||||
rbuff_size = recv_count.Sum();
|
||||
|
||||
sendbuf.SetSize(sbuff_size);
|
||||
sendbuf = 0; soffs = 0;
|
||||
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != subdomain_rank[ip]) continue;
|
||||
int ndofs = SubdomainGTrueDofs[ip].Size();
|
||||
// loop through dofs
|
||||
for (int i = 0; i<ndofs; ++i)
|
||||
{
|
||||
int tdof = SubdomainGTrueDofs[ip][i];
|
||||
int irank = get_rank(tdof,tdof_offsets);
|
||||
if (irank == subdomain_rank[ip]) continue; // <--------------
|
||||
int j = send_displ[irank] + soffs[irank];
|
||||
sendbuf[j] = ip;
|
||||
sendbuf[j+1] = SubdomainGTrueDofs[ip][i];
|
||||
soffs[irank] += 2 ;
|
||||
}
|
||||
}
|
||||
|
||||
recvbuf.SetSize(rbuff_size);
|
||||
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_INT, recvbuf,
|
||||
recv_count, recv_displ, MPI_INT, comm);
|
||||
|
||||
// List of tdofs owned by the processor for subdomains not owned
|
||||
SubdomainLTrueDofs.resize(nrsubdomains);
|
||||
for (int k=0; k<rbuff_size/2; k++)
|
||||
{
|
||||
int ip = recvbuf[2*k];
|
||||
int tdof = recvbuf[2*k+1];
|
||||
SubdomainLTrueDofs[ip].Append(tdof);
|
||||
}
|
||||
}
|
||||
|
||||
// Restriction of global residual to subdomain residuals
|
||||
void DofMaps::GlobalToSubdomains(const Vector & y, Array<Vector*> & x)
|
||||
{
|
||||
send_count = 0; send_displ = 0;
|
||||
recv_count = 0; recv_displ = 0;
|
||||
|
||||
// Compute send_counts
|
||||
int m = (CompFlag) ? 2 : 1 ;
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == subdomain_rank[ip]) continue; // <---------------
|
||||
int ndofs = SubdomainLTrueDofs[ip].Size();
|
||||
send_count[subdomain_rank[ip]] += m * ndofs;
|
||||
}
|
||||
|
||||
// communicate so that recv_count is constructed
|
||||
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
|
||||
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
sbuff_size = send_count.Sum();
|
||||
rbuff_size = recv_count.Sum();
|
||||
|
||||
Array<double> sendbuf(sbuff_size); sendbuf = 0;
|
||||
Array<int> soffs(num_procs); soffs = 0;
|
||||
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == subdomain_rank[ip]) continue; // <---------------
|
||||
int ndofs = SubdomainLTrueDofs[ip].Size();
|
||||
for (int i = 0; i<ndofs; i++)
|
||||
{
|
||||
int tdof = SubdomainLTrueDofs[ip][i];
|
||||
int j = send_displ[subdomain_rank[ip]] + soffs[subdomain_rank[ip]];
|
||||
soffs[subdomain_rank[ip]] +=m;
|
||||
int k = tdof - mytoffset;
|
||||
// sendbuf[j] = y[k];
|
||||
sendbuf[j] = tdof_sign[k]*y[k];
|
||||
if (CompFlag)
|
||||
{ // if complex valued
|
||||
int tsize = pfes->GetTrueVSize();
|
||||
// sendbuf[j+1] = y[k+tsize];
|
||||
sendbuf[j+1] = tdof_sign[k]*y[k+tsize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// communication
|
||||
Array<double> recvbuf(rbuff_size);
|
||||
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_DOUBLE, recvbuf,
|
||||
recv_count, recv_displ, MPI_DOUBLE, comm);
|
||||
Array<int> roffs(num_procs);
|
||||
roffs = 0;
|
||||
// Now each process will construct the res vector
|
||||
x.SetSize(nrsubdomains);
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != subdomain_rank[ip]) continue;
|
||||
int ndof = SubdomainGTrueDofs[ip].Size();
|
||||
if (!x[ip]) x[ip] = new Vector(m*ndof);
|
||||
*x[ip] = 0.0;
|
||||
// extract the data from receiv buffer
|
||||
for (int i=0; i<ndof; i++)
|
||||
{
|
||||
// pick up the tdof and find its rank
|
||||
int tdof = SubdomainGTrueDofs[ip][i];
|
||||
int tdof_rank = get_rank(tdof,tdof_offsets);
|
||||
if (tdof_rank != subdomain_rank[ip]) // <---------------
|
||||
{
|
||||
int k = recv_displ[tdof_rank] + roffs[tdof_rank];
|
||||
roffs[tdof_rank] += m;
|
||||
(*x[ip])[i] = recvbuf[k];
|
||||
if (CompFlag)
|
||||
{
|
||||
(*x[ip])[i+ndof] = recvbuf[k+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int k = tdof - mytoffset;
|
||||
// (*x[ip])[i] = y[k];
|
||||
(*x[ip])[i] = tdof_sign[k]*y[k];
|
||||
if (CompFlag)
|
||||
{
|
||||
int gtsize = pfes->GetTrueVSize();
|
||||
(*x[ip])[i+ndof] = tdof_sign[k]*y[k+gtsize];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prolongation of subdomain solutions to the global solution
|
||||
void DofMaps::SubdomainsToGlobal(const Array<Vector*> & x, Vector & y)
|
||||
{
|
||||
send_count = 0; send_displ = 0;
|
||||
recv_count = 0; recv_displ = 0;
|
||||
|
||||
// Compute send_counts
|
||||
int m = (CompFlag) ? 2 : 1 ;
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != subdomain_rank[ip]) continue;
|
||||
int ndofs = SubdomainGTrueDofs[ip].Size();
|
||||
for (int i=0; i<ndofs; i++)
|
||||
{
|
||||
// pick up the tdof and find its rank
|
||||
int tdof = SubdomainGTrueDofs[ip][i];
|
||||
int tdof_rank = get_rank(tdof,tdof_offsets);
|
||||
if (tdof_rank == subdomain_rank[ip]) continue;
|
||||
send_count[tdof_rank] +=m;
|
||||
}
|
||||
}
|
||||
|
||||
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
|
||||
|
||||
for (int k=0; k<num_procs-1; k++)
|
||||
{
|
||||
send_displ[k+1] = send_displ[k] + send_count[k];
|
||||
recv_displ[k+1] = recv_displ[k] + recv_count[k];
|
||||
}
|
||||
sbuff_size = send_count.Sum();
|
||||
rbuff_size = recv_count.Sum();
|
||||
|
||||
Array<double> sendbuf(sbuff_size); sendbuf = 0;
|
||||
Array<int> soffs(num_procs); soffs = 0;
|
||||
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != subdomain_rank[ip]) continue;
|
||||
int ndofs = SubdomainGTrueDofs[ip].Size();
|
||||
// loop through dofs
|
||||
for (int i=0; i<ndofs; i++)
|
||||
{
|
||||
// pick up the dof and find its tdof_rank
|
||||
int tdof = SubdomainGTrueDofs[ip][i];
|
||||
int tdof_rank = get_rank(tdof,tdof_offsets);
|
||||
// offset
|
||||
if (tdof_rank == subdomain_rank[ip]) continue;
|
||||
int k = send_displ[tdof_rank] + soffs[tdof_rank];
|
||||
soffs[tdof_rank] +=m;
|
||||
sendbuf[k] = (*x[ip])[i];
|
||||
if (CompFlag)
|
||||
{
|
||||
sendbuf[k+1] = (*x[ip])[i+ndofs];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<double> recvbuf(rbuff_size);
|
||||
Array<int> roffs(num_procs); roffs = 0;
|
||||
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_DOUBLE, recvbuf,
|
||||
recv_count, recv_displ, MPI_DOUBLE, comm);
|
||||
|
||||
for (int ip = 0; ip < nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == subdomain_rank[ip])
|
||||
{
|
||||
int ndofs = SubdomainGTrueDofs[ip].Size();
|
||||
for (int i = 0; i<ndofs; i++)
|
||||
{
|
||||
int tdof = SubdomainGTrueDofs[ip][i];
|
||||
int k = tdof - mytoffset;
|
||||
if (k<0 || k>=pfes->GetTrueVSize()) continue;
|
||||
y[k] += tdof_sign[k] * (*x[ip])[i];
|
||||
if (CompFlag)
|
||||
{
|
||||
int gtsize = pfes->GetTrueVSize();
|
||||
y[k+gtsize] += tdof_sign[k]*(*x[ip])[i+ndofs];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ndofs = SubdomainLTrueDofs[ip].Size();
|
||||
for (int i = 0; i<ndofs; i++)
|
||||
{
|
||||
int tdof = SubdomainLTrueDofs[ip][i];
|
||||
int k = tdof - mytoffset;
|
||||
int j = recv_displ[subdomain_rank[ip]] + roffs[subdomain_rank[ip]];
|
||||
roffs[subdomain_rank[ip]] +=m;
|
||||
y[k] += tdof_sign[k] * recvbuf[j];
|
||||
if (CompFlag)
|
||||
{
|
||||
int tsize = pfes->GetTrueVSize();
|
||||
y[k+tsize] += tdof_sign[k]*recvbuf[j+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofMaps::TestSubdomainToGlobalMaps()
|
||||
{
|
||||
cout << "Testing Subdomain To Global Maps" << endl;
|
||||
FunctionCoefficient c1(testcoeff);
|
||||
Array<Vector*> x(nrsubdomains);
|
||||
Vector y(pfes->GetTrueVSize()); y = 0.0;
|
||||
for (int i = 0 ; i<nrsubdomains; i++)
|
||||
{
|
||||
if (myid != subdomain_rank[i]) continue;
|
||||
x[i] = new Vector(fes[i]->GetTrueVSize());
|
||||
GridFunction gf(fes[i]);
|
||||
gf = 0.0;
|
||||
|
||||
if (i==3) gf.ProjectCoefficient(c1);
|
||||
*x[i] = gf;
|
||||
}
|
||||
|
||||
SubdomainsToGlobal(x,y);
|
||||
|
||||
// cout << "1: myid = " << myid << ", y = "; y.Print();
|
||||
|
||||
string keys = (dim==2) ? "keys amrRljc\n": "keys m\n";
|
||||
ParGridFunction pgf(pfes);
|
||||
|
||||
const Operator &P = *pfes->GetProlongationMatrix();
|
||||
P.Mult(y, pgf);
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pfes->GetParMesh() << pgf
|
||||
<< keys << flush;
|
||||
|
||||
ParGridFunction pgf1(pfes);
|
||||
pgf1.ProjectCoefficient(c1);
|
||||
Vector y1(pfes->GetTrueVSize());
|
||||
const SparseMatrix * R = pfes->GetRestrictionMatrix();
|
||||
|
||||
R->Mult(pgf1,y1);
|
||||
// P.MultTranspose(pgf1,y1);
|
||||
Array<Vector*> x1;
|
||||
GlobalToSubdomains(y1,x1);
|
||||
|
||||
|
||||
// for (int i = 0 ; i<nrsubdomains; i++)
|
||||
// {
|
||||
// if (myid != subdomain_rank[i]) continue;
|
||||
// ostringstream mesh_name;
|
||||
// mesh_name << "output/mesh." << setfill('0') << setw(6) << i;
|
||||
// ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
// mesh_ofs.precision(8);
|
||||
// fes[i]->GetMesh()->Print(mesh_ofs);
|
||||
// GridFunction gf(fes[i]);
|
||||
// gf = x1[i];
|
||||
// ostringstream gf_name;
|
||||
// gf_name << "output/gf." << setfill('0') << setw(6) << i;
|
||||
// ofstream gf_ofs(gf_name.str().c_str());
|
||||
// gf_ofs.precision(8);
|
||||
// gf.Save(gf_ofs);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
int nrsub = nrsubdomains;
|
||||
for (int i = 0 ; i<nrsub; i++)
|
||||
{
|
||||
if (myid == subdomain_rank[i])
|
||||
{
|
||||
socketstream sol_sock1(vishost, visport);
|
||||
sol_sock1.precision(8);
|
||||
sol_sock1 << "parallel " << nrsub << " " << i << "\n";
|
||||
GridFunction gf(fes[i]);
|
||||
GridFunction gf1(fes[i]);
|
||||
gf1.ProjectCoefficient(c1);
|
||||
gf = *x1[i];
|
||||
gf1-=gf;
|
||||
cout << "ip, Diff norm = " <<i<<", " << gf1.Norml2() << endl;
|
||||
sol_sock1 << "solution\n" << *fes[i]->GetMesh() << gf
|
||||
<< keys << flush;
|
||||
}
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
socketstream gf_sock(vishost, visport);
|
||||
gf_sock.precision(8);
|
||||
gf_sock << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pfes->GetParMesh() << pgf1
|
||||
<< keys << flush;
|
||||
}
|
||||
|
||||
|
||||
DofMaps::~DofMaps()
|
||||
{
|
||||
for (int i = 0; i<nrsubdomains; i++)
|
||||
{
|
||||
delete fes[i];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#include "../common/Utilities.hpp"
|
||||
#include "../common/PML.hpp"
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
double testcoeff(const Vector & x);
|
||||
int get_rank(int tdof, std::vector<int> & tdof_offsets);
|
||||
|
||||
|
||||
void ComputeTdofOffsets(const MPI_Comm & comm, const ParFiniteElementSpace * pfes,
|
||||
std::vector<int> & tdof_offsets);
|
||||
|
||||
void GetSubdomainijk(int ip, const Array<int> nxyz, Array<int> & ijk);
|
||||
void GetDirectionijk(int id, Array<int> & ijk);
|
||||
int GetSubdomainId(const Array<int> nxyz, Array<int> & ijk);
|
||||
int GetDirectionId(const Array<int> & ijk);
|
||||
|
||||
|
||||
// class handling two types of dof maps
|
||||
// 1. Subdomain truedofs ---> Global truedofs
|
||||
// 2. Subdomain truedofs ---> Neighbor truedofs
|
||||
class DofMaps
|
||||
{
|
||||
private:
|
||||
// The FE space of the problem (H1/Hcurl)
|
||||
ParFiniteElementSpace *pfes = nullptr;
|
||||
|
||||
// The given partition of the parmesh
|
||||
ParMeshPartition *part = nullptr;
|
||||
// partition in x-y-z
|
||||
Array<int> nxyz;
|
||||
|
||||
// MPI parameters
|
||||
MPI_Comm comm = MPI_COMM_WORLD;
|
||||
int num_procs, myid;
|
||||
|
||||
// true dof offset and element offset of the processor
|
||||
vector<int> tdof_offsets;
|
||||
int mytoffset;
|
||||
int myelemoffset;
|
||||
|
||||
int dim;
|
||||
// Total number of subdomains
|
||||
int nrsubdomains;
|
||||
|
||||
// Array specifying the subdomain rank
|
||||
Array<int> subdomain_rank;
|
||||
|
||||
// Complex flag
|
||||
bool CompFlag;
|
||||
|
||||
// sign factors
|
||||
Array<int> tdof_sign;
|
||||
// Initializing mpi and helper parameters
|
||||
void Init();
|
||||
|
||||
// 1. Setting up the subdomains FE spaces
|
||||
// 2. Setting up the subdomains-to-subdomains maps
|
||||
// 3. Setting up the subdomain-to-global maps
|
||||
void Setup();
|
||||
|
||||
// -----------------------------------------------
|
||||
// Subdomain to Subdomain maps
|
||||
// -----------------------------------------------
|
||||
std::vector<std::vector<Array<int>>> OvlpElems;
|
||||
void AddElementToOvlpLists(int l, int iel,
|
||||
const Array<bool> & neg,
|
||||
const Array<bool> & pos);
|
||||
std::vector<std::vector<Array<int>>> OvlpTDofs;
|
||||
void SubdomainToSubdomainMapsSetup();
|
||||
void ComputeOvlpElems();
|
||||
void ComputeOvlpTdofs();
|
||||
void PrintOvlpTdofs();
|
||||
|
||||
// -----------------------------------------------
|
||||
// Subdomain to Global maps
|
||||
// -----------------------------------------------
|
||||
std::vector<Array<int>> SubdomainGTrueDofs; // Subdomain Tdofs to Global Tdofs
|
||||
std::vector<Array<int>> SubdomainLTrueDofs; // Subdomain Tdofs to Local (on rank) Tdofs
|
||||
|
||||
Array<int> send_count, send_displ;
|
||||
Array<int> recv_count, recv_displ;
|
||||
int sbuff_size = 0;
|
||||
int rbuff_size = 0;
|
||||
void SubdomainToGlobalMapsSetup();
|
||||
|
||||
// Testing
|
||||
void TestSubdomainToGlobalMaps();
|
||||
void TestSubdomainToSubdomainMaps();
|
||||
|
||||
public:
|
||||
// constructor
|
||||
|
||||
// FiniteElementSpaces of the subdomains
|
||||
Array<FiniteElementSpace *> fes;
|
||||
|
||||
DofMaps(ParFiniteElementSpace *fespace_, ParMeshPartition * part_, bool CompFlag_ = false);
|
||||
~DofMaps();
|
||||
// Transfering from subdomains SubdomainIds to all their neighbors
|
||||
void TransferToNeighbors(const Array<int> & SubdomainIds, const Array<Vector *> & x,
|
||||
std::vector<std::vector<Vector * >> & OvlpSol);
|
||||
|
||||
// Prolongation of subdomain solutions to the global solution
|
||||
void SubdomainsToGlobal(const Array<Vector*> & x, Vector & y);
|
||||
// Restriction of global residual to subdomain residuals
|
||||
// bool comp: true for complex valued problems
|
||||
void GlobalToSubdomains(const Vector & y, Array<Vector*> & x);
|
||||
};
|
||||
@@ -0,0 +1,849 @@
|
||||
//Parallel Diagonal Source Transfer Preconditioner
|
||||
|
||||
#include "ParDST.hpp"
|
||||
|
||||
ParDST::ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
|
||||
double omega_, Coefficient * Q_, int nrlayers_ , int nx_, int ny_, int nz_)
|
||||
: Solver(2*bf_->ParFESpace()->GetTrueVSize(), 2*bf_->ParFESpace()->GetTrueVSize()),
|
||||
bf(bf_), Pmllength(Pmllength_), omega(omega_),
|
||||
Q(Q_), nrlayers(nrlayers_)
|
||||
{
|
||||
nx = nx_; ny = ny_; nz = nz_;
|
||||
Init();
|
||||
}
|
||||
ParDST::ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
|
||||
double omega_, VectorCoefficient * VQ_, int nrlayers_ , int nx_, int ny_, int nz_)
|
||||
: Solver(2*bf_->ParFESpace()->GetTrueVSize(), 2*bf_->ParFESpace()->GetTrueVSize()),
|
||||
bf(bf_), Pmllength(Pmllength_), omega(omega_),
|
||||
VQ(VQ_), nrlayers(nrlayers_)
|
||||
{
|
||||
nx = nx_; ny = ny_; nz = nz_;
|
||||
Init();
|
||||
}
|
||||
ParDST::ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
|
||||
double omega_, MatrixCoefficient * MQ_, int nrlayers_ , int nx_, int ny_, int nz_)
|
||||
: Solver(2*bf_->ParFESpace()->GetTrueVSize(), 2*bf_->ParFESpace()->GetTrueVSize()),
|
||||
bf(bf_), Pmllength(Pmllength_), omega(omega_),
|
||||
MQ(MQ_), nrlayers(nrlayers_)
|
||||
{
|
||||
nx = nx_; ny = ny_; nz = nz_;
|
||||
Init();
|
||||
}
|
||||
|
||||
void ParDST::Init()
|
||||
{
|
||||
pfes = bf->ParFESpace();
|
||||
fec = pfes->FEColl();
|
||||
|
||||
comm = pfes->GetComm();
|
||||
MPI_Comm_size(comm, &num_procs);
|
||||
MPI_Comm_rank(comm, &myid);
|
||||
|
||||
//1. Indentify problem ... Helmholtz or Maxwell
|
||||
prob_kind = fec->GetContType();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << " 1. Indentify problem to be solved ... " << endl;
|
||||
if (prob_kind == 0) cout << " Helmholtz" << endl;
|
||||
if (prob_kind == 1) cout << " Maxwell" << endl;
|
||||
}
|
||||
|
||||
//2. Create the parallel mesh partition
|
||||
pmesh = pfes->GetParMesh();
|
||||
dim = pmesh->Dimension();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n 2. Generating ParMesh partitioning ... " << endl;
|
||||
}
|
||||
ovlpnrlayers = nrlayers+1;
|
||||
part = new ParMeshPartition(pmesh,nx,ny,nz,ovlpnrlayers);
|
||||
nxyz.SetSize(3);
|
||||
nxyz[0] = nx = part->nxyz[0];
|
||||
nxyz[1] = ny = part->nxyz[1];
|
||||
nxyz[2] = nz = part->nxyz[2];
|
||||
|
||||
nrsubdomains = part->nrsubdomains;
|
||||
SubdomainRank = part->subdomain_rank;
|
||||
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == SubdomainRank[ip])
|
||||
{
|
||||
RankSubdomains.Append(ip);
|
||||
}
|
||||
}
|
||||
|
||||
cout << " myid: " << myid
|
||||
<< ", nrsubdomains: " << RankSubdomains.Size() << endl;
|
||||
|
||||
MPI_Barrier(comm);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << " Done ! " << endl;
|
||||
}
|
||||
//3. Setup info for sweeps
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n 3. Computing sweeps info ..." << endl;
|
||||
}
|
||||
sweeps = new Sweep(dim);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << " Done ! " << endl;
|
||||
}
|
||||
//4. Create LocalToGlobal maps
|
||||
// (local GridFunctions/Vector to Global ParGridFunction/Vector)
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n 4. Computing true dofs maps ..." << endl;
|
||||
}
|
||||
|
||||
// if (myid == SubdomainRank[0])
|
||||
// {
|
||||
// cout << "myid = " << myid << endl;
|
||||
// char vishost[] = "localhost";
|
||||
// int visport = 19916;
|
||||
// socketstream mesh_sock1(vishost, visport);
|
||||
// mesh_sock1.precision(8);
|
||||
// mesh_sock1 << "mesh\n"
|
||||
// << *part->subdomain_mesh[0] << "window_title 'Subdomain'" << flush;
|
||||
// part->subdomain_mesh[0]->Print();
|
||||
|
||||
// }
|
||||
bool comp = true;
|
||||
|
||||
dmaps = new DofMaps(pfes,part, comp);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << " Done ! " << endl;
|
||||
}
|
||||
// 4. Setting up the local problems
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n 5. Setting up the subdomain problems ..." << endl;
|
||||
}
|
||||
|
||||
SetupSubdomainProblems();
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << " Done ! " << endl;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n 6. Mark subdomain overlap truedofs ..." << endl;
|
||||
}
|
||||
MarkSubdomainOverlapDofs(comp);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << " Done ! " << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ParDST::Mult(const Vector &r, Vector &z) const
|
||||
{
|
||||
// Initialize transfered residuals to 0.0;
|
||||
for (int ip=0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
for (int i=0;i<sweeps->nsweeps; i++)
|
||||
{
|
||||
*f_transf[ip][i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// restrict given residual to subdomains
|
||||
dmaps->GlobalToSubdomains(r,f_orig);
|
||||
|
||||
for (int ip=0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
Array<int> ijk(3);
|
||||
GetSubdomainijk(ip,nxyz,ijk);
|
||||
Array2D<int> direct(dim,2); direct = 0;
|
||||
for (int d=0;d<dim; d++)
|
||||
{
|
||||
if (ijk[d] > 0) direct[d][0] = 1;
|
||||
if (ijk[d] < part->nxyz[d]-1) direct[d][1] = 1;
|
||||
}
|
||||
GetChiRes(*f_orig[ip],ip,direct);
|
||||
}
|
||||
|
||||
z = 0.0;
|
||||
int nsteps;
|
||||
switch(dim)
|
||||
{
|
||||
case 1: nsteps = nx; break;
|
||||
case 2: nsteps = nx+ny-1; break;
|
||||
default: nsteps = nx+ny+nz-2; break;
|
||||
}
|
||||
int nsweeps = sweeps->nsweeps;
|
||||
// 1. Loop through sweeps
|
||||
if (dim == 3 && nz == 1) { nsweeps = 4; } // x-y partition only;
|
||||
for (int l=0; l<nsweeps; l++)
|
||||
{
|
||||
// 2. loop through diagonals/steps of each sweep
|
||||
for (int s = 0; s<nsteps; s++)
|
||||
{
|
||||
Array2D<int> subdomains;
|
||||
GetStepSubdomains(l,s,subdomains);
|
||||
int nsubdomains = subdomains.NumRows();
|
||||
|
||||
// 3. Loop through the subdomains on the diagonal
|
||||
Array<int> subdomain_ids;
|
||||
for (int sb=0; sb < nsubdomains; sb++)
|
||||
{
|
||||
Array<int> ijk(dim); ijk = 0;
|
||||
for (int d=0; d<dim; d++) ijk[d] = subdomains[sb][d];
|
||||
int ip = GetSubdomainId(nxyz,ijk);
|
||||
subdomain_ids.Append(ip);
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
|
||||
int n = dmaps->fes[ip]->GetTrueVSize();
|
||||
Vector res_local(2*n); res_local = 0.0;
|
||||
|
||||
if (l==0) { res_local += *f_orig[ip]; }
|
||||
res_local += *f_transf[ip][l];
|
||||
if (res_local.Norml2() < 1e-12)
|
||||
{
|
||||
*subdomain_sol[ip] = 0.0;
|
||||
continue;
|
||||
}
|
||||
PmlMatInv[ip]->Mult(res_local, *subdomain_sol[ip]);
|
||||
}
|
||||
// 4. Transfer solutions to neighbors so that the subdomain
|
||||
// residuals are updated
|
||||
TransferSources(l,subdomain_ids);
|
||||
}
|
||||
// 5. Update the global solution
|
||||
dmaps->SubdomainsToGlobal(subdomain_sol,z);
|
||||
}
|
||||
}
|
||||
|
||||
void ParDST::SetupSubdomainProblems()
|
||||
{
|
||||
sqf.SetSize(nrsubdomains);
|
||||
Optr.SetSize(nrsubdomains);
|
||||
PmlMat.SetSize(nrsubdomains);
|
||||
PmlMatInv.SetSize(nrsubdomains);
|
||||
f_orig.SetSize(nrsubdomains);
|
||||
f_transf.SetSize(nrsubdomains);
|
||||
subdomain_sol.SetSize(nrsubdomains);
|
||||
for (int ip=0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
sqf[ip] = nullptr;
|
||||
f_orig[ip] = nullptr;
|
||||
subdomain_sol[ip] = nullptr;
|
||||
PmlMat[ip] = nullptr;
|
||||
PmlMatInv[ip] = nullptr;
|
||||
Optr[ip] = nullptr;
|
||||
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
subdomain_sol[ip] = new Vector(2*dmaps->fes[ip]->GetTrueVSize());
|
||||
if (prob_kind == 0)
|
||||
{
|
||||
SetHelmholtzPmlSystemMatrix(ip);
|
||||
}
|
||||
else if (prob_kind == 1)
|
||||
{
|
||||
SetMaxwellPmlSystemMatrix(ip);
|
||||
}
|
||||
PmlMat[ip] = Optr[ip]->As<ComplexSparseMatrix>();
|
||||
|
||||
PmlMatInv[ip] = new ComplexUMFPackSolver;
|
||||
PmlMatInv[ip]->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
PmlMatInv[ip]->SetOperator(*PmlMat[ip]);
|
||||
|
||||
// HYPRE_Int rowstarts[2]; rowstarts[0] = 0;
|
||||
// rowstarts[1] = dmaps->fes[ip]->GetTrueVSize();
|
||||
// HypreParMatrix * HypreMat_r =
|
||||
// new HypreParMatrix(MPI_COMM_SELF,rowstarts[1],rowstarts,
|
||||
// &(PmlMat[ip]->real()));
|
||||
// HypreParMatrix * HypreMat_i =
|
||||
// new HypreParMatrix(MPI_COMM_SELF,rowstarts[1],rowstarts,
|
||||
// &(PmlMat[ip]->imag()));
|
||||
// ComplexHypreParMatrix * HypreMat =
|
||||
// new ComplexHypreParMatrix(HypreMat_r,HypreMat_i,true,true);
|
||||
// PmlMatInv[ip] = new ComplexMUMPSSolver;
|
||||
// PmlMatInv[ip]->SetOperator(*HypreMat);
|
||||
// delete HypreMat;
|
||||
int ndofs = dmaps->fes[ip]->GetTrueVSize();
|
||||
f_transf[ip].SetSize(sweeps->nsweeps);
|
||||
for (int i=0;i<sweeps->nsweeps; i++)
|
||||
{
|
||||
f_transf[ip][i] = new Vector(2*ndofs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ParDST::SetHelmholtzPmlSystemMatrix(int ip)
|
||||
{
|
||||
MFEM_VERIFY(part->subdomain_mesh[ip], "Null mesh pointer");
|
||||
Mesh * mesh = part->subdomain_mesh[ip];
|
||||
double h = part->MeshSize;
|
||||
Array2D<double> length(dim,2);
|
||||
length = h*(nrlayers);
|
||||
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(ip,nxyz,ijk);
|
||||
int i = ijk[0];
|
||||
int j = ijk[1];
|
||||
int k = ijk[2];
|
||||
|
||||
if (i == 0 ) length[0][0] = Pmllength[0][0];
|
||||
if (i == nx-1 ) length[0][1] = Pmllength[0][1];
|
||||
if (dim > 1)
|
||||
{
|
||||
if (j == 0 ) length[1][0] = Pmllength[1][0];
|
||||
if (j == ny-1 ) length[1][1] = Pmllength[1][1];
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
if (k == 0 ) length[2][0] = Pmllength[2][0];
|
||||
if (k == nz-1 ) length[2][1] = Pmllength[2][1];
|
||||
}
|
||||
|
||||
CartesianPML pml(mesh, length);
|
||||
pml.SetOmega(omega);
|
||||
|
||||
Array <int> ess_tdof_list;
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
dmaps->fes[ip]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient sigma(-pow(omega, 2));
|
||||
PmlMatrixCoefficient c1_re(dim,pml_detJ_JT_J_inv_Re,&pml);
|
||||
PmlMatrixCoefficient c1_im(dim,pml_detJ_JT_J_inv_Im,&pml);
|
||||
PmlCoefficient detJ_re(pml_detJ_Re,&pml);
|
||||
PmlCoefficient detJ_im(pml_detJ_Im,&pml);
|
||||
ProductCoefficient c2_re0(sigma, detJ_re);
|
||||
ProductCoefficient c2_im0(sigma, detJ_im);
|
||||
ProductCoefficient c2_re(c2_re0, *Q);
|
||||
ProductCoefficient c2_im(c2_im0, *Q);
|
||||
sqf[ip] = new SesquilinearForm (dmaps->fes[ip],bf->GetConvention());
|
||||
|
||||
sqf[ip]->AddDomainIntegrator(new DiffusionIntegrator(c1_re),
|
||||
new DiffusionIntegrator(c1_im));
|
||||
sqf[ip]->AddDomainIntegrator(new MassIntegrator(c2_re),
|
||||
new MassIntegrator(c2_im));
|
||||
sqf[ip]->Assemble(0);
|
||||
|
||||
Optr[ip] = new OperatorPtr;
|
||||
sqf[ip]->FormSystemMatrix(ess_tdof_list,*Optr[ip]);
|
||||
}
|
||||
|
||||
void ParDST::SetMaxwellPmlSystemMatrix(int ip)
|
||||
{
|
||||
MFEM_VERIFY(part->subdomain_mesh[ip], "Null mesh pointer");
|
||||
Mesh * mesh = part->subdomain_mesh[ip];
|
||||
double h = part->MeshSize;
|
||||
Array2D<double> length(dim,2);
|
||||
length = h*(nrlayers);
|
||||
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(ip,nxyz,ijk);
|
||||
int i = ijk[0];
|
||||
int j = ijk[1];
|
||||
int k = ijk[2];
|
||||
|
||||
if (i == 0 ) length[0][0] = Pmllength[0][0];
|
||||
if (i == nx-1 ) length[0][1] = Pmllength[0][1];
|
||||
if (dim > 1)
|
||||
{
|
||||
if (j == 0 ) length[1][0] = Pmllength[1][0];
|
||||
if (j == ny-1 ) length[1][1] = Pmllength[1][1];
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
if (k == 0 ) length[2][0] = Pmllength[2][0];
|
||||
if (k == nz-1 ) length[2][1] = Pmllength[2][1];
|
||||
}
|
||||
|
||||
CartesianPML pml(mesh, length);
|
||||
pml.SetOmega(omega);
|
||||
Array <int> ess_tdof_list;
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
dmaps->fes[ip]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
ConstantCoefficient omeg(-pow(omega, 2));
|
||||
int cdim = (dim == 2) ? 1 : dim;
|
||||
|
||||
PmlMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, &pml);
|
||||
PmlMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, &pml);
|
||||
|
||||
PmlMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,&pml);
|
||||
PmlMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,&pml);
|
||||
ScalarMatrixProductCoefficient c2_Re0(omeg,pml_c2_Re);
|
||||
ScalarMatrixProductCoefficient c2_Im0(omeg,pml_c2_Im);
|
||||
|
||||
MatrixCoefficient * c2_Re=nullptr;
|
||||
MatrixCoefficient * c2_Im=nullptr;
|
||||
|
||||
if (Q)
|
||||
{
|
||||
c2_Re = new ScalarMatrixProductCoefficient(*Q,c2_Re0);
|
||||
c2_Im = new ScalarMatrixProductCoefficient(*Q,c2_Im0);
|
||||
}
|
||||
else if (VQ)
|
||||
{
|
||||
MFEM_ABORT("Vector Coeffiecient not supported ");
|
||||
}
|
||||
else if (MQ)
|
||||
{
|
||||
c2_Re = new MatrixMatrixProductCoefficient(c2_Re0,*MQ);
|
||||
c2_Im = new MatrixMatrixProductCoefficient(c2_Im0,*MQ);
|
||||
}
|
||||
|
||||
sqf[ip] = new SesquilinearForm(dmaps->fes[ip],bf->GetConvention());
|
||||
|
||||
sqf[ip]->AddDomainIntegrator(new CurlCurlIntegrator(pml_c1_Re),
|
||||
new CurlCurlIntegrator(pml_c1_Im));
|
||||
sqf[ip]->AddDomainIntegrator(new VectorFEMassIntegrator(*c2_Re),
|
||||
new VectorFEMassIntegrator(*c2_Im));
|
||||
sqf[ip]->Assemble(0);
|
||||
|
||||
Optr[ip] = new OperatorPtr;
|
||||
sqf[ip]->FormSystemMatrix(ess_tdof_list,*Optr[ip]);
|
||||
delete c2_Re;
|
||||
delete c2_Im;
|
||||
}
|
||||
|
||||
|
||||
void ParDST::MarkSubdomainOverlapDofs(const bool comp)
|
||||
{
|
||||
// First mark the elements
|
||||
// cout<< "Compute Overlap Elements (in each possible direction) " << endl;
|
||||
// Lists of elements
|
||||
// x,y,z = +/- 1 ovlp
|
||||
NovlpElems.resize(nrsubdomains);
|
||||
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
Array<int> ijk;
|
||||
GetSubdomainijk(ip,nxyz,ijk);
|
||||
|
||||
Mesh * mesh = dmaps->fes[ip]->GetMesh();
|
||||
NovlpElems[ip].resize(2*dim);
|
||||
|
||||
Vector pmin, pmax;
|
||||
mesh->GetBoundingBox(pmin,pmax);
|
||||
double h = part->MeshSize;
|
||||
// Loop through elements
|
||||
for (int iel=0; iel<mesh->GetNE(); iel++)
|
||||
{
|
||||
// Get element center
|
||||
Vector center(dim);
|
||||
int geom = mesh->GetElementBaseGeometry(iel);
|
||||
ElementTransformation * tr = mesh->GetElementTransformation(iel);
|
||||
tr->Transform(Geometries.GetCenter(geom),center);
|
||||
|
||||
// Assign elements to the appropriate lists
|
||||
for (int d=0;d<dim; d++)
|
||||
{
|
||||
if (ijk[d]>0)
|
||||
{
|
||||
if (center[d] >= pmin[d]+h*ovlpnrlayers)
|
||||
{
|
||||
NovlpElems[ip][d].Append(iel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NovlpElems[ip][d].Append(iel);
|
||||
}
|
||||
|
||||
if (ijk[d]<nxyz[d]-1)
|
||||
{
|
||||
if (center[d] <= pmax[d]-h*ovlpnrlayers)
|
||||
{
|
||||
NovlpElems[ip][dim+d].Append(iel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NovlpElems[ip][dim+d].Append(iel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark dofs
|
||||
NovlpDofs.resize(nrsubdomains);
|
||||
int mm = (comp) ? 2 : 1; // complex or real valued
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
FiniteElementSpace * fes = dmaps->fes[ip];
|
||||
// Loop through the marked elements
|
||||
NovlpDofs[ip].resize(2*dim);
|
||||
int n = fes->GetTrueVSize();
|
||||
Array<int> marker(n);
|
||||
for (int d=0;d<2*dim; d++)
|
||||
{
|
||||
marker = 0;
|
||||
int m = 0;
|
||||
int melems = NovlpElems[ip][d].Size();
|
||||
for (int iel=0; iel<melems; iel++)
|
||||
{
|
||||
Array<int> ElemDofs;
|
||||
int el = NovlpElems[ip][d][iel];
|
||||
fes->GetElementDofs(el,ElemDofs);
|
||||
int ndof = ElemDofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int eldof = ElemDofs[i];
|
||||
int tdof = (eldof >= 0) ? eldof : abs(eldof) - 1;
|
||||
if (marker[tdof] == 1) continue;
|
||||
marker[tdof] = 1;
|
||||
m++;
|
||||
}
|
||||
}
|
||||
int k = mm*(n-m);
|
||||
NovlpDofs[ip][d].SetSize(k);
|
||||
int l = 0;
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
if (marker[i]==0)
|
||||
{
|
||||
NovlpDofs[ip][d][l] = i; // real dofs
|
||||
if (comp)
|
||||
{
|
||||
NovlpDofs[ip][d][l+k/2] = i+fes->GetTrueVSize();
|
||||
}
|
||||
l++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParDST::GetChiRes(Vector & res, int ip, Array2D<int> direct) const
|
||||
{
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
// negative direction
|
||||
if (direct[d][0]==1) res.SetSubVector(NovlpDofs[ip][d],0.0);
|
||||
// possitive direction
|
||||
if (direct[d][1]==1) res.SetSubVector(NovlpDofs[ip][d+dim],0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ParDST::PlotLocal(Vector & sol, socketstream & sol_sock, int ip) const
|
||||
{
|
||||
FiniteElementSpace * fes = dmaps->fes[ip];
|
||||
Mesh * mesh = fes->GetMesh();
|
||||
GridFunction gf(fes);
|
||||
double * data = sol.GetData();
|
||||
gf.SetData(data);
|
||||
|
||||
string keys;
|
||||
keys = "keys mrRljc\n";
|
||||
sol_sock << "solution\n" << *mesh << gf << keys << flush;
|
||||
}
|
||||
|
||||
|
||||
void ParDST::GetStepSubdomains(const int sweep, const int step, Array2D<int> & subdomains) const
|
||||
{
|
||||
Array<int> aux;
|
||||
switch(dim)
|
||||
{
|
||||
case 2:
|
||||
for (int i=nx-1;i>=0; i--)
|
||||
{
|
||||
int j;
|
||||
switch (sweep)
|
||||
{
|
||||
case 0: j = step-i; break;
|
||||
case 1: j = step-nx+i+1; break;
|
||||
case 2: j = nx+i-step-1; break;
|
||||
default: j = nx+ny-i-step-2; break;
|
||||
}
|
||||
if (j<0 || j>=ny) continue;
|
||||
aux.Append(i); aux.Append(j);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (int i=nx-1;i>=0; i--)
|
||||
{
|
||||
for (int j=ny-1;j>=0; j--)
|
||||
{
|
||||
int k;
|
||||
switch (sweep)
|
||||
{
|
||||
case 0: k = step-i-j; break;
|
||||
case 1: k = step-nx+i+1-j; break;
|
||||
case 2: k = step-ny+j+1-i; break;
|
||||
case 3: k = step-nx-ny+i+j+2; break;
|
||||
case 4: k = i+j+nz-1-step; break;
|
||||
case 5: k = nx+nz-i+j-step-2; break;
|
||||
case 6: k = ny+nz+i-j-step-2; break;
|
||||
default: k = nx+ny+nz-i-j-step-3; break;
|
||||
}
|
||||
if (k<0 || k>=nz) continue;
|
||||
aux.Append(i); aux.Append(j); aux.Append(k);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int nrows = aux.Size()/dim;
|
||||
int ncols = dim;
|
||||
|
||||
subdomains.SetSize(nrows,ncols);
|
||||
for (int r=0;r<nrows; r++)
|
||||
{
|
||||
for (int c=0; c<ncols; c++)
|
||||
{
|
||||
int k = r*ncols + c;
|
||||
subdomains[r][c] = aux[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParDST::TransferSources(int sweep, const Array<int> & subdomain_ids) const
|
||||
{
|
||||
OvlpSol.resize(nrsubdomains);
|
||||
int nrneighbors = pow(3,dim);
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == SubdomainRank[ip])
|
||||
{
|
||||
OvlpSol[ip].resize(nrneighbors);
|
||||
}
|
||||
}
|
||||
int m = subdomain_ids.Size();
|
||||
Array<Vector *> x(m);
|
||||
for (int i = 0; i<m; i++)
|
||||
{
|
||||
x[i] = nullptr;
|
||||
int ip = subdomain_ids[i];
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
x[i] = new Vector(subdomain_sol[ip]->GetData(),subdomain_sol[ip]->Size());
|
||||
}
|
||||
dmaps->TransferToNeighbors(subdomain_ids,x,OvlpSol);
|
||||
for (int i = 0; i<m; i++)
|
||||
{
|
||||
delete x[i]; x[i] = nullptr;
|
||||
}
|
||||
// Update residuals
|
||||
// Find all neighbors of patch ip0
|
||||
for (int is = 0; is<m; is++)
|
||||
{
|
||||
int ip0 = subdomain_ids[is];
|
||||
Array<int> ijk;
|
||||
Array<int> ijk1(3);
|
||||
GetSubdomainijk(ip0,nxyz,ijk);
|
||||
Array<int> directions(3);
|
||||
for (int i=-1; i<2; i++)
|
||||
{
|
||||
int i1 = ijk[0] + i;
|
||||
if (i1 <0 || i1>=nx) continue;
|
||||
directions[0] = i;
|
||||
ijk1[0] = i1;
|
||||
for (int j=-1; j<2; j++)
|
||||
{
|
||||
int j1 = ijk[1] + j;
|
||||
if (j1 <0 || j1>=ny) continue;
|
||||
directions[1] = j;
|
||||
ijk1[1] = j1;
|
||||
int kbeg = (dim == 2) ? 0 : -1;
|
||||
int kend = (dim == 2) ? 1 : 2;
|
||||
for (int k=kbeg; k<kend; k++)
|
||||
{
|
||||
int k1 = ijk[2] + k;
|
||||
if (k1 <0 || k1>=nz) continue;
|
||||
directions[2] = (dim == 3) ? k : -1 ;
|
||||
if (i==0 && j==0 && k==0) continue;
|
||||
|
||||
int l = GetSweepToTransfer(sweep,directions);
|
||||
if (l == -1) continue;
|
||||
ijk1[2] = k1;
|
||||
int ip1 = GetSubdomainId(nxyz,ijk1);
|
||||
|
||||
if (myid != SubdomainRank[ip1]) continue;
|
||||
Array<int>directions1(3); directions1 = -1;
|
||||
for (int i = 0; i<dim; i++) directions1[i] = -directions[i];
|
||||
int dir = GetDirectionId(directions1);
|
||||
int n = dmaps->fes[ip1]->GetTrueVSize();
|
||||
Vector res(2*n);
|
||||
PmlMat[ip1]->Mult(*OvlpSol[ip1][dir],res);
|
||||
|
||||
Array2D<int> direct(dim,2); direct = 0;
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
if (directions[d]==1) direct[d][0] = 1;
|
||||
if (directions[d]==-1) direct[d][1] = 1;
|
||||
}
|
||||
GetChiRes(res,ip1,direct);
|
||||
*f_transf[ip1][l] -= res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
if (myid == SubdomainRank[ip])
|
||||
{
|
||||
for (int i = 0; i<nrneighbors; i++)
|
||||
{
|
||||
if (OvlpSol[ip][i])
|
||||
{
|
||||
delete OvlpSol[ip][i];
|
||||
}
|
||||
}
|
||||
OvlpSol[ip].clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ParDST::GetSweepToTransfer(const int s, Array<int> directions) const
|
||||
{
|
||||
int l1=-1;
|
||||
int nsweeps = sweeps->nsweeps;
|
||||
Array<int> sweep0;
|
||||
sweeps->GetSweep(s,sweep0);
|
||||
switch (dim)
|
||||
{
|
||||
case 2:
|
||||
for (int l=s; l<nsweeps; l++)
|
||||
{
|
||||
// Rule 1: the transfer source direction has to be similar with
|
||||
// the sweep direction
|
||||
Array<int> sweep1;
|
||||
sweeps->GetSweep(l,sweep1);
|
||||
int ddot = 0;
|
||||
for (int d=0; d<dim; d++) ddot+= sweep1[d] * directions[d];
|
||||
if (ddot <= 0) continue;
|
||||
|
||||
// Rule 2: The horizontal or vertical transfer source cannot be used
|
||||
// Case of horizontal or vertical transfer source
|
||||
// (it can't be both 0 cause it's skipped)
|
||||
if (directions[0]==0 || directions[1] == 0)
|
||||
{
|
||||
if (sweep0[0] == -sweep1[0] && sweep0[1] == -sweep1[1]) continue;
|
||||
}
|
||||
l1 = l;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for (int l=s; l<nsweeps; l++)
|
||||
{
|
||||
// Rule 1: (similar directions) the transfer source direction has to be similar with
|
||||
// the sweep direction
|
||||
Array<int> sweep1;
|
||||
sweeps->GetSweep(l,sweep1);
|
||||
int ddot = 0;
|
||||
bool similar = true;
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
if (sweep1[d] * directions[d] < 0) similar = false;
|
||||
ddot+= sweep1[d] * directions[d];
|
||||
}
|
||||
if (!similar || ddot<=0) continue; // not similar
|
||||
|
||||
// Rule 2: (oposite directions) the transfer source direction has to be similar with
|
||||
// the sweep direction
|
||||
//
|
||||
// check any of the projections onto the planes
|
||||
// (xy, xz, yz)
|
||||
|
||||
if ( (directions[0]==0 && directions[1] != 0) ||
|
||||
(directions[0]!=0 && directions[1] == 0) ||
|
||||
(directions[0]==0 && directions[2] != 0) ||
|
||||
(directions[0]!=0 && directions[2] == 0) ||
|
||||
(directions[2]==0 && directions[1] != 0) ||
|
||||
(directions[2]!=0 && directions[1] == 0) )
|
||||
{
|
||||
if (sweep0[0] == -sweep1[0] &&
|
||||
sweep0[1] == -sweep1[1] &&
|
||||
sweep0[2] == -sweep1[2]) continue;
|
||||
}
|
||||
l1 = l;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return l1;
|
||||
}
|
||||
|
||||
void ParDST::CorrectOrientation(int ip,Vector &x) const
|
||||
{
|
||||
FiniteElementSpace * fespace = dmaps->fes[ip];
|
||||
Mesh * mesh = fespace->GetMesh();
|
||||
int nrelems = mesh->GetNE();
|
||||
// GridFunction test;
|
||||
// test.SetFromTrueDofs(x)
|
||||
Array<int> signs(fespace->GetTrueVSize()); signs = 0;
|
||||
for (int iel=0; iel<nrelems; iel++)
|
||||
{
|
||||
Array<int> ElemDofs;
|
||||
fespace->GetElementDofs(iel,ElemDofs);
|
||||
int ndofs = ElemDofs.Size();
|
||||
ElemDofs.Print();
|
||||
for (int i = 0; i< ndofs; i++)
|
||||
{
|
||||
int pdof_ = ElemDofs[i];
|
||||
if (pdof_ < 0)
|
||||
{
|
||||
signs[abs(pdof_)-1] += 1.0 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
signs[pdof_] -= 1.0 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << "signs = " ; signs.Print();
|
||||
for (int i = 0; i<fespace->GetTrueVSize(); i++)
|
||||
{
|
||||
if (signs[i]<0)
|
||||
{
|
||||
x(i) *= -1.0;
|
||||
x(i+fespace->GetTrueVSize()) *= -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ParDST::~ParDST()
|
||||
{
|
||||
|
||||
for (int ip=0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
delete Optr[ip];
|
||||
delete subdomain_sol[ip];
|
||||
delete PmlMatInv[ip];
|
||||
delete sqf[ip];
|
||||
if (myid != SubdomainRank[ip]) continue;
|
||||
for (int i=0;i<sweeps->nsweeps; i++)
|
||||
{
|
||||
delete f_transf[ip][i];
|
||||
}
|
||||
delete f_orig[ip];
|
||||
}
|
||||
f_orig.DeleteAll();
|
||||
delete dmaps;
|
||||
delete sweeps;
|
||||
delete part;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include "../common/Utilities.hpp"
|
||||
#include "../common/PML.hpp"
|
||||
#include "DofMapsDST.hpp"
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
class ParDST : public Solver//
|
||||
{
|
||||
private:
|
||||
MPI_Comm comm = MPI_COMM_WORLD;
|
||||
int num_procs, myid;
|
||||
// Constructor inputs
|
||||
int prob_kind;
|
||||
ParSesquilinearForm *bf=nullptr;
|
||||
ParFiniteElementSpace * pfes = nullptr;
|
||||
ParMesh * pmesh = nullptr;
|
||||
ParMeshPartition * part = nullptr;
|
||||
Array<int> SubdomainRank;
|
||||
Array<int> RankSubdomains;
|
||||
const FiniteElementCollection * fec = nullptr;
|
||||
Array2D<double> Pmllength;
|
||||
int dim = 2;
|
||||
double omega = 0.5;
|
||||
Coefficient * Q=nullptr;
|
||||
VectorCoefficient * VQ=nullptr;
|
||||
MatrixCoefficient * MQ=nullptr;
|
||||
int nrlayers;
|
||||
int ovlpnrlayers;
|
||||
int nrsubdomains = 0;
|
||||
int nx,ny,nz;
|
||||
Array<int> nxyz;
|
||||
Sweep * sweeps = nullptr;
|
||||
DofMaps * dmaps = nullptr;
|
||||
Array< SesquilinearForm * > sqf;
|
||||
Array< OperatorPtr * > Optr;
|
||||
Array<ComplexSparseMatrix *> PmlMat;
|
||||
Array<ComplexUMFPackSolver *> PmlMatInv;
|
||||
// Array<ComplexMUMPSSolver *> PmlMatInv;
|
||||
mutable Array<Vector *> f_orig;
|
||||
mutable Array<Array<Vector * >> f_transf;
|
||||
mutable Array<Vector * > subdomain_sol;
|
||||
mutable std::vector<std::vector<Vector * >> OvlpSol;
|
||||
void SetupSubdomainProblems();
|
||||
std::vector<std::vector<Array<int>>> NovlpElems;
|
||||
std::vector<std::vector<Array<int>>> NovlpDofs;
|
||||
void MarkSubdomainOverlapDofs(const bool comp = false);
|
||||
void SetHelmholtzPmlSystemMatrix(int ip);
|
||||
void SetMaxwellPmlSystemMatrix(int ip);
|
||||
void GetChiRes(Vector & res, int ip, Array2D<int> direct) const;
|
||||
void PlotLocal(Vector & sol, socketstream & sol_sock, int ip) const;
|
||||
void GetStepSubdomains(const int sweep, const int step, Array2D<int> & subdomains) const;
|
||||
void TransferSources(int sweep, const Array<int> & subdomain_ids) const;
|
||||
int GetSweepToTransfer(const int s, Array<int> directions) const;
|
||||
void CorrectOrientation(int ip, Vector & x) const;
|
||||
void Init();
|
||||
public:
|
||||
ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
|
||||
double omega_, Coefficient * Q_, int nrlayers_, int nx_=2, int ny_=2, int nz_=2);
|
||||
ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
|
||||
double omega_, VectorCoefficient * VQ_, int nrlayers_, int nx_=2, int ny_=2, int nz_=2);
|
||||
ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
|
||||
double omega_, MatrixCoefficient * MQ_, int nrlayers_, int nx_=2, int ny_=2, int nz_=2);
|
||||
virtual void SetOperator(const Operator &op) {}
|
||||
virtual void Mult(const Vector &r, Vector &z) const;
|
||||
virtual ~ParDST();
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
#pragma once
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
struct UniqueIndexGenerator
|
||||
{
|
||||
int counter = 0;
|
||||
std::unordered_map<int,int> idx;
|
||||
int Get(int i)
|
||||
{
|
||||
std::unordered_map<int,int>::iterator f = idx.find(i);
|
||||
if (f == idx.end())
|
||||
{
|
||||
idx[i] = counter;
|
||||
return counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*f).second;
|
||||
}
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
counter = 0;
|
||||
idx.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
double GetUniformMeshElementSize(Mesh * mesh);
|
||||
Mesh * ExtendMesh(Mesh * mesh, const Array<int> & directions);
|
||||
|
||||
class CartesianMeshPartition
|
||||
{
|
||||
private:
|
||||
Mesh *mesh=nullptr;
|
||||
public:
|
||||
int nrpatch;
|
||||
int nxyz[3];
|
||||
double MeshSize;
|
||||
std::vector<Array<int>> element_map;
|
||||
Array3D<int>subdomains;
|
||||
// constructor
|
||||
CartesianMeshPartition(Mesh * mesh_,int & nx, int & ny, int & nz);
|
||||
~CartesianMeshPartition() {};
|
||||
};
|
||||
|
||||
class OverlappingCartesianMeshPartition
|
||||
{
|
||||
private:
|
||||
Mesh *mesh=nullptr;
|
||||
public:
|
||||
int nrpatch;
|
||||
double MeshSize;
|
||||
int nxyz[3];
|
||||
std::vector<Array<int>> element_map;
|
||||
Array3D<int> subdomains;
|
||||
// constructor
|
||||
OverlappingCartesianMeshPartition(Mesh * mesh_,int & nx, int & ny, int & nz);
|
||||
OverlappingCartesianMeshPartition(Mesh * mesh_,int & nx, int & ny, int & nz, int ovlp_nlayers);
|
||||
~OverlappingCartesianMeshPartition() {};
|
||||
};
|
||||
|
||||
class STPOverlappingCartesianMeshPartition // Special layered partition for STP
|
||||
{
|
||||
private:
|
||||
Mesh *mesh=nullptr;
|
||||
public:
|
||||
int nrpatch;
|
||||
int nx, ny, nz;
|
||||
std::vector<Array<int>> element_map;
|
||||
// constructor
|
||||
STPOverlappingCartesianMeshPartition(Mesh * mesh_);
|
||||
~STPOverlappingCartesianMeshPartition() {};
|
||||
};
|
||||
|
||||
class MeshPartition
|
||||
{
|
||||
private:
|
||||
Mesh *mesh=nullptr;
|
||||
void AddElementToMesh(Mesh * mesh,mfem::Element::Type elem_type,int * ind);
|
||||
void GetNumVertices(int type, mfem::Element::Type & elem_type, int & nrvert);
|
||||
void PrintElementMap();
|
||||
public:
|
||||
int nrpatch;
|
||||
double MeshSize;
|
||||
std::vector<Array<int>> element_map;
|
||||
Array3D<int> subdomains;
|
||||
Array<Mesh *> patch_mesh;
|
||||
int partition_kind;
|
||||
int nxyz[3];
|
||||
// constructor
|
||||
MeshPartition(Mesh * mesh_, int part, int mx=1, int my=1, int mz=1, int ovl_nlayers=0);
|
||||
~MeshPartition();
|
||||
};
|
||||
|
||||
void SaveMeshPartition(Array<Mesh * > meshes,
|
||||
string mfilename="output/mesh.",
|
||||
string sfilename="output/sol.");
|
||||
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
class CartesianParMeshPartition
|
||||
{
|
||||
private:
|
||||
ParMesh *pmesh=nullptr;
|
||||
public:
|
||||
int nrsubdomains;
|
||||
int nxyz[3];
|
||||
double MeshSize;
|
||||
std::vector<Array<int>> local_element_map;
|
||||
Array<int> subdomain_rank;
|
||||
Array3D<int>subdomains;
|
||||
// constructor
|
||||
CartesianParMeshPartition(ParMesh * pmesh_,int & nx, int & ny, int & nz,
|
||||
int ovlp_nlayers);
|
||||
~CartesianParMeshPartition() {};
|
||||
};
|
||||
|
||||
class ParMeshPartition
|
||||
{
|
||||
private:
|
||||
MPI_Comm comm;
|
||||
ParMesh *pmesh=nullptr;
|
||||
void AddElementToMesh(Mesh * mesh,mfem::Element::Type elem_type,int * ind);
|
||||
void GetNumVertices(int type, mfem::Element::Type & elem_type, int & nrvert);
|
||||
void PrintElementMap();
|
||||
public:
|
||||
int nrsubdomains;
|
||||
int OvlpNlayers;
|
||||
int myelem_offset = 0;
|
||||
double MeshSize;
|
||||
std::vector<Array<int>> element_map;
|
||||
std::vector<Array<int>> local_element_map;
|
||||
Array3D<int> subdomains;
|
||||
Array<Mesh *> subdomain_mesh;
|
||||
Array<int> subdomain_rank;
|
||||
int partition_kind;
|
||||
int nxyz[3];
|
||||
// constructor
|
||||
ParMeshPartition(ParMesh * pmesh_, int mx=1, int my=1, int mz=1, int ovl_nlayers=0);
|
||||
void SaveMeshPartition();
|
||||
~ParMeshPartition();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,570 @@
|
||||
#include "PML.hpp"
|
||||
|
||||
CartesianPML::CartesianPML(Mesh *mesh_, Array2D<double> length_)
|
||||
: mesh(mesh_), length(length_)
|
||||
{
|
||||
dim = mesh->Dimension();
|
||||
SetBoundaries();
|
||||
}
|
||||
|
||||
void CartesianPML::SetBoundaries()
|
||||
{
|
||||
comp_dom_bdr.SetSize(dim, 2);
|
||||
dom_bdr.SetSize(dim, 2);
|
||||
// initialize
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
dom_bdr(i, 0) = infinity();
|
||||
dom_bdr(i, 1) = -infinity();
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
Array<int> bdr_vertices;
|
||||
mesh->GetBdrElementVertices(i, bdr_vertices);
|
||||
for (int j = 0; j < bdr_vertices.Size(); j++)
|
||||
{
|
||||
for (int k = 0; k < dim; k++)
|
||||
{
|
||||
dom_bdr(k, 0) = min(dom_bdr(k, 0), mesh->GetVertex(bdr_vertices[j])[k]);
|
||||
dom_bdr(k, 1) = max(dom_bdr(k, 1), mesh->GetVertex(bdr_vertices[j])[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh * pmesh = dynamic_cast<ParMesh *>(mesh);
|
||||
if (pmesh)
|
||||
{
|
||||
for (int d=0; d<dim; d++)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE,&dom_bdr(d,0),1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE,&dom_bdr(d,1),1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
comp_dom_bdr(i, 0) = dom_bdr(i, 0) + length(i, 0);
|
||||
comp_dom_bdr(i, 1) = dom_bdr(i, 1) - length(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void CartesianPML::SetAttributes(Mesh *mesh_)
|
||||
{
|
||||
int nrelem = mesh_->GetNE();
|
||||
elems.SetSize(nrelem);
|
||||
|
||||
for (int i = 0; i < nrelem; ++i)
|
||||
{
|
||||
elems[i] = 1;
|
||||
bool in_pml = false;
|
||||
Element *el = mesh_->GetElement(i);
|
||||
Array<int> vertices;
|
||||
// Initialize Attribute
|
||||
el->SetAttribute(1);
|
||||
el->GetVertices(vertices);
|
||||
int nrvert = vertices.Size();
|
||||
// Check if any vertex is in the pml
|
||||
for (int iv = 0; iv < nrvert; ++iv)
|
||||
{
|
||||
int vert_idx = vertices[iv];
|
||||
double *coords = mesh_->GetVertex(vert_idx);
|
||||
for (int comp = 0; comp < dim; ++comp)
|
||||
{
|
||||
if (coords[comp] > comp_dom_bdr(comp, 1) ||
|
||||
coords[comp] < comp_dom_bdr(comp, 0))
|
||||
{
|
||||
in_pml = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (in_pml)
|
||||
{
|
||||
elems[i] = 0;
|
||||
el->SetAttribute(2);
|
||||
}
|
||||
}
|
||||
mesh_->SetAttributes();
|
||||
}
|
||||
|
||||
void CartesianPML::StretchFunction(const Vector &x,
|
||||
vector<complex<double>> &dxs, double omega)
|
||||
{
|
||||
complex<double> zi = complex<double>(0., 1.);
|
||||
|
||||
double n = 2.0;
|
||||
double c = 10.0;
|
||||
// double c = log(omega);
|
||||
double coeff;
|
||||
// Stretch in each direction independently
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
dxs[i] = 1.0;
|
||||
if (x(i) >= comp_dom_bdr(i, 1))
|
||||
{
|
||||
coeff = n * c / omega / pow(length(i, 1), n);
|
||||
dxs[i] = 1.0 + zi * coeff * abs(pow(x(i) - comp_dom_bdr(i, 1), n - 1.0));
|
||||
}
|
||||
if (x(i) <= comp_dom_bdr(i, 0))
|
||||
{
|
||||
coeff = n * c / omega / pow(length(i, 0), n);
|
||||
dxs[i] = 1.0 + zi * coeff * abs(pow(x(i) - comp_dom_bdr(i, 0), n - 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToroidPML::ToroidPML(Mesh *mesh_)
|
||||
: mesh(mesh_)
|
||||
{
|
||||
dim = mesh->Dimension();
|
||||
zlim.SetSize(2);
|
||||
rlim.SetSize(2);
|
||||
alim.SetSize(2);
|
||||
zpml_thickness.SetSize(2);
|
||||
rpml_thickness.SetSize(2);
|
||||
apml_thickness.SetSize(2);
|
||||
SetBoundaries();
|
||||
}
|
||||
|
||||
void ToroidPML::SetBoundaries()
|
||||
{
|
||||
mesh->EnsureNodes();
|
||||
int nrnodes = mesh->GetNodalFESpace()->GetTrueVSize()/dim;
|
||||
double zmin = infinity();
|
||||
double zmax = -infinity();
|
||||
double rmin = infinity();
|
||||
double rmax = -infinity();
|
||||
double amin = infinity(); // in degrees
|
||||
double amax = -infinity(); // in degrees
|
||||
for (int i = 0; i<nrnodes; i++)
|
||||
{
|
||||
Vector coord(dim);
|
||||
mesh->GetNode(i,coord);
|
||||
for (int d = 0; d<dim; d++)
|
||||
{
|
||||
if (abs(coord[d])<1e-13) coord[d] = 0.0;
|
||||
}
|
||||
// Find r and a for this point
|
||||
double x = coord[0];
|
||||
double y = coord[1];
|
||||
double z = 0.0;
|
||||
if (dim == 3) z = coord[2];
|
||||
double a = GetAngle(x,y);
|
||||
double r = sqrt(x*x + y*y);
|
||||
|
||||
zmin = min(zmin,z);
|
||||
zmax = max(zmax,z);
|
||||
rmin = min(rmin,r);
|
||||
rmax = max(rmax,r);
|
||||
amin = min(amin,a);
|
||||
amax = max(amax,a);
|
||||
}
|
||||
|
||||
zlim[0] = zmin;
|
||||
zlim[1] = zmax;
|
||||
rlim[0] = rmin;
|
||||
rlim[1] = rmax;
|
||||
alim[0] = amin;
|
||||
alim[1] = amax;
|
||||
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh * pmesh = dynamic_cast<ParMesh *>(mesh);
|
||||
if (pmesh)
|
||||
{
|
||||
MPI_Allreduce(MPI_IN_PLACE,&zlim[0],1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE,&zlim[1],1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE,&rlim[0],1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE,&rlim[1],1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE,&alim[0],1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
|
||||
MPI_Allreduce(MPI_IN_PLACE,&alim[1],1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ToroidPML::SetAttributes(Mesh *mesh_)
|
||||
{
|
||||
int nrelem = mesh_->GetNE();
|
||||
elems.SetSize(nrelem);
|
||||
|
||||
// Loop through the elements and identify which of them are in the PML
|
||||
for (int i = 0; i < nrelem; ++i)
|
||||
{
|
||||
// initialize with 1
|
||||
elems[i] = 1;
|
||||
Element *el = mesh_->GetElement(i);
|
||||
// Initialize attribute
|
||||
el->SetAttribute(1);
|
||||
|
||||
Array<int> vertices;
|
||||
el->GetVertices(vertices);
|
||||
int nrvert = vertices.Size();
|
||||
// Check if any vertex is in the pml
|
||||
bool in_pml = false;
|
||||
for (int iv = 0; iv < nrvert; ++iv)
|
||||
{
|
||||
int vert_idx = vertices[iv];
|
||||
double *coords = mesh_->GetVertex(vert_idx);
|
||||
double x = coords[0];
|
||||
double y = coords[1];
|
||||
double a = GetAngle(x,y);
|
||||
double r = sqrt(x*x + y*y);
|
||||
|
||||
if (astretch)
|
||||
{
|
||||
if ( (a <= alim[0]+apml_thickness[0]) ||
|
||||
(a >= alim[1]-apml_thickness[1]) )
|
||||
{
|
||||
in_pml = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rstretch)
|
||||
{
|
||||
if ( (r <= rlim[0]+rpml_thickness[0]) ||
|
||||
(r >= rlim[1]-rpml_thickness[1]) )
|
||||
{
|
||||
in_pml = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (in_pml)
|
||||
{
|
||||
elems[i] = 0;
|
||||
el->SetAttribute(2);
|
||||
}
|
||||
|
||||
// Vector center;
|
||||
// mesh_->GetElementCenter(i,center);
|
||||
// double x = center[0];
|
||||
// double y = center[1];
|
||||
// double a = GetAngle(x,y);
|
||||
// double r = sqrt(x*x + y*y);
|
||||
// // check upper and lower bound
|
||||
// if (astretch)
|
||||
// {
|
||||
// if ( (a <= alim[0]+apml_thickness[0]) ||
|
||||
// (a >= alim[1]-apml_thickness[1]) )
|
||||
// {
|
||||
// elems[i] = 0;
|
||||
// el->SetAttribute(2);
|
||||
// }
|
||||
// }
|
||||
// if (rstretch)
|
||||
// {
|
||||
// if ( (r <= rlim[0]+rpml_thickness[0]) ||
|
||||
// (r >= rlim[1]-rpml_thickness[1]) )
|
||||
// {
|
||||
// elems[i] = 0;
|
||||
// el->SetAttribute(2);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
mesh_->SetAttributes();
|
||||
}
|
||||
|
||||
|
||||
double ToroidPML::GetAngle(const double x, const double y)
|
||||
{
|
||||
// Find r and a for this point
|
||||
double arad;
|
||||
if (x == 0.0)
|
||||
{
|
||||
arad = (y > 0.0)? M_PI/2.0 : 3.0 * M_PI/2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
arad = atan(y/x);
|
||||
int k = 0;
|
||||
if (x<0)
|
||||
{
|
||||
k = 1;
|
||||
}
|
||||
else if (y<0)
|
||||
{
|
||||
k = 2;
|
||||
}
|
||||
arad += k*M_PI;
|
||||
}
|
||||
return arad * 180.0/M_PI;
|
||||
}
|
||||
|
||||
// void ToroidPML::StretchFunction(const Vector &X,
|
||||
// vector<complex<double>> &dxs, double omega)
|
||||
void ToroidPML::StretchFunction(const Vector &X, ComplexDenseMatrix & J, double omega)
|
||||
|
||||
{
|
||||
complex<double> zi = complex<double>(0., 1.);
|
||||
|
||||
double n = 2.0;
|
||||
double c = 10.0;
|
||||
// double c = log(omega);
|
||||
// Stretch in the azimuthal direction
|
||||
double x = X[0];
|
||||
double y = X[1];
|
||||
if (abs(x) < 1e-12) x = 0.0;
|
||||
if (abs(y) < 1e-12) y = 0.0;
|
||||
double a = GetAngle(x,y);
|
||||
double r = sqrt(x*x + y*y);
|
||||
// dxs[0] = 1.0;
|
||||
// dxs[1] = 1.0;
|
||||
J = 0.0;
|
||||
J(0,0) = 1.0;
|
||||
J(1,1) = 1.0;
|
||||
if (dim == 3) J(2,2) = 1.0;
|
||||
|
||||
if (astretch)
|
||||
{
|
||||
double th = a * M_PI/180.0;
|
||||
double thl, thL, thH;
|
||||
bool in_pml = false;
|
||||
// negative direction
|
||||
if (a <= alim[0]+apml_thickness[0])
|
||||
{
|
||||
in_pml = true;
|
||||
thL = alim[1] * M_PI/180.0;
|
||||
thH = apml_thickness[1] * M_PI/180.0;
|
||||
thl = thL + thH;
|
||||
}
|
||||
// positive direction
|
||||
if (a >= alim[1]-apml_thickness[1])
|
||||
{
|
||||
in_pml = true;
|
||||
thL = alim[1] * M_PI/180.0;
|
||||
thH = apml_thickness[1] * M_PI/180.0;
|
||||
thl = thL - thH;
|
||||
}
|
||||
// double c1 = min(20.0*M_PI/180.0,thH);
|
||||
if (in_pml)
|
||||
{
|
||||
double c1 = thH;
|
||||
double coeff = n * c / omega / pow(c1,n);
|
||||
double f_th = pow(th - thl,n-1);
|
||||
double th_x = - y / (r * r);
|
||||
double th_y = x / (r * r);
|
||||
|
||||
J(0,0) = 1.0 + zi * coeff * abs(f_th * th_x);
|
||||
J(0,1) = zi * f_th * th_y;
|
||||
J(1,0) = zi * f_th * th_x;
|
||||
J(1,1) = 1.0 + zi * coeff * abs(f_th * th_y);
|
||||
}
|
||||
}
|
||||
// Stretch in the radial direction
|
||||
if (rstretch)
|
||||
{ // negative
|
||||
double rl, rL, rH;
|
||||
bool in_pml = false;
|
||||
if (r <= rlim[0]+rpml_thickness[0])
|
||||
{
|
||||
in_pml = true;
|
||||
rL = rlim[0];
|
||||
rH = rpml_thickness[0];
|
||||
rl = rL + rH;
|
||||
}
|
||||
// positive direction
|
||||
if (r >= rlim[1]-rpml_thickness[1])
|
||||
{
|
||||
in_pml = true;
|
||||
rL = rlim[1];
|
||||
rH = rpml_thickness[1];
|
||||
rl = rL - rH;
|
||||
}
|
||||
|
||||
if (in_pml)
|
||||
{
|
||||
double coeff = n * c / omega / pow (rH,n);
|
||||
double f_r = pow(r-rl,n-1.0);
|
||||
double r_x = x / r;
|
||||
double r_y = y / r;
|
||||
|
||||
J(0,0) = 1.0 + zi * coeff * abs(f_r*r_x);
|
||||
J(0,1) = zi * f_r * r_y;
|
||||
J(1,0) = zi * f_r * r_x;
|
||||
J(1,1) = 1.0 + zi * coeff * abs(f_r*r_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double pml_detJ_Re(const Vector & x, CartesianPML * pml)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
std::vector<std::complex<double>> dxs(dim);
|
||||
complex<double> det(1.0,0.0);
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
for (int i=0; i<dim; ++i) det *= dxs[i];
|
||||
return det.real();
|
||||
}
|
||||
|
||||
double pml_detJ_Im(const Vector & x, CartesianPML * pml)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
std::vector<std::complex<double>> dxs(dim);
|
||||
complex<double> det(1.0,0.0);
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
for (int i=0; i<dim; ++i) det *= dxs[i];
|
||||
return det.imag();
|
||||
}
|
||||
|
||||
void pml_detJ_JT_J_inv_Re(const Vector & x, CartesianPML * pml , DenseMatrix & M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
std::vector<std::complex<double>> dxs(dim);
|
||||
complex<double> det(1.0,0.0);
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i<dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M=0.0;
|
||||
for (int i = 0; i<dim; ++i)
|
||||
{
|
||||
M(i,i) = (det / pow(dxs[i],2)).real();
|
||||
}
|
||||
}
|
||||
|
||||
void pml_detJ_JT_J_inv_Im(const Vector & x, CartesianPML * pml , DenseMatrix & M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
|
||||
std::vector<std::complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i<dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M=0.0;
|
||||
for (int i = 0; i<dim; ++i)
|
||||
{
|
||||
M(i,i) = (det / pow(dxs[i],2)).imag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det(1.0, 0.0);
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
M(i, i) = (det / pow(dxs[i], 2)).real();
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
M(i, i) = (det / pow(dxs[i], 2)).imag();
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
M(i, i) = abs(det / pow(dxs[i], 2));
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det(1.0, 0.0);
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
// in the 2D case the coefficient is scalar 1/det(J)
|
||||
if (dim == 2)
|
||||
{
|
||||
M = (1.0 / det).real();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
M(i, i) = (pow(dxs[i], 2) / det).real();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
|
||||
{
|
||||
int dim = pml->dim;
|
||||
double omega = pml->omega;
|
||||
vector<complex<double>> dxs(dim);
|
||||
complex<double> det = 1.0;
|
||||
pml->StretchFunction(x, dxs, omega);
|
||||
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
det *= dxs[i];
|
||||
}
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
M = (1.0 / det).imag();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = 0.0;
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
M(i, i) = (pow(dxs[i], 2) / det).imag();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
#include "mfem.hpp"
|
||||
#include "complex_linalg.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
// Class for setting up a simple Cartesian PML region
|
||||
class CartesianPML
|
||||
{
|
||||
private:
|
||||
Mesh *mesh;
|
||||
|
||||
// Length of the PML Region in each direction
|
||||
Array2D<double> length;
|
||||
|
||||
// Computational Domain Boundary
|
||||
Array2D<double> comp_dom_bdr;
|
||||
|
||||
// Domain Boundary
|
||||
Array2D<double> dom_bdr;
|
||||
|
||||
// Integer Array identifying elements in the pml
|
||||
// 0: in the pml, 1: not in the pml
|
||||
Array<int> elems;
|
||||
|
||||
// Compute Domain and Computational Domain Boundaries
|
||||
void SetBoundaries();
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CartesianPML(Mesh *mesh_,Array2D<double> length_);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
// Return Computational Domain Boundary
|
||||
Array2D<double> GetCompDomainBdr() {return comp_dom_bdr;}
|
||||
|
||||
// Return Domain Boundary
|
||||
Array2D<double> GetDomainBdr() {return dom_bdr;}
|
||||
|
||||
// Return Marker list for elements
|
||||
Array<int> * GetMarkedPMLElements() {return &elems;}
|
||||
|
||||
// Mark element in the PML region
|
||||
void SetAttributes(Mesh *mesh_);
|
||||
|
||||
void SetOmega(double omega_) {omega = omega_;}
|
||||
|
||||
// PML complex stretching function
|
||||
void StretchFunction(const Vector &x, vector<complex<double>> &dxs, double omega);
|
||||
};
|
||||
|
||||
class ToroidPML
|
||||
{
|
||||
private:
|
||||
Mesh *mesh;
|
||||
|
||||
Vector zlim, zpml_thickness; // range in axial direction
|
||||
Vector rlim, rpml_thickness; // range in radial direction
|
||||
Vector alim, apml_thickness; // range in azimuthal direction
|
||||
|
||||
// Integer Array identifying elements in the pml
|
||||
// 0: in the pml, 1: not in the pml
|
||||
Array<int> elems;
|
||||
|
||||
double GetAngle(const double x, const double y);
|
||||
|
||||
// Compute Domain and Computational Domain Boundaries
|
||||
void SetBoundaries();
|
||||
|
||||
bool zstretch = false;
|
||||
bool rstretch = false;
|
||||
bool astretch = false;
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
ToroidPML(Mesh *mesh_);
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
// Return Computational Domain Boundary
|
||||
|
||||
// Return Domain Boundary
|
||||
void GetDomainBdrs(Vector & zlim_, Vector & rlim_, Vector & alim_)
|
||||
{
|
||||
zlim_.SetSize(2); zlim_ = zlim;
|
||||
rlim_.SetSize(2); rlim_ = rlim;
|
||||
alim_.SetSize(2); alim_ = alim;
|
||||
}
|
||||
|
||||
void SetPmlWidth(const Vector & zpml, const Vector & rpml, const Vector & apml)
|
||||
{
|
||||
MFEM_VERIFY(zpml.Size() == 2 , "Check zpml size");
|
||||
MFEM_VERIFY(rpml.Size() == 2 , "Check rpml size");
|
||||
MFEM_VERIFY(apml.Size() == 2 , "Check apml size");
|
||||
zpml_thickness = zpml;
|
||||
rpml_thickness = rpml;
|
||||
apml_thickness = apml;
|
||||
}
|
||||
|
||||
void SetPmlAxes(const bool zstretch_,
|
||||
const bool rstretch_,
|
||||
const bool astretch_ )
|
||||
{
|
||||
zstretch = zstretch_;
|
||||
rstretch = rstretch_;
|
||||
astretch = astretch_;
|
||||
}
|
||||
|
||||
// // Return Marker list for elements
|
||||
Array<int> * GetMarkedPMLElements() {return &elems;}
|
||||
|
||||
// Mark element in the PML region
|
||||
void SetAttributes(Mesh *mesh_);
|
||||
|
||||
void SetOmega(double omega_) {omega = omega_;}
|
||||
|
||||
// PML complex stretching function
|
||||
// void StretchFunction(const Vector &X, vector<complex<double>> &dxs, double omega);
|
||||
void StretchFunction(const Vector &X, ComplexDenseMatrix & J, double omega);
|
||||
};
|
||||
|
||||
class PmlCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
CartesianPML * pml = nullptr;
|
||||
double (*Function)(const Vector &, CartesianPML * );
|
||||
public:
|
||||
PmlCoefficient(double (*F)(const Vector &, CartesianPML *), CartesianPML * pml_)
|
||||
: pml(pml_), Function(F)
|
||||
{}
|
||||
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{
|
||||
double x[3];
|
||||
Vector transip(x, 3);
|
||||
T.Transform(ip, transip);
|
||||
return ((*Function)(transip, pml));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// This includes scalar coefficients
|
||||
class PmlMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
CartesianPML * pml = nullptr;
|
||||
void (*Function)(const Vector &, CartesianPML * , DenseMatrix &);
|
||||
public:
|
||||
PmlMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
|
||||
DenseMatrix &),
|
||||
CartesianPML * pml_)
|
||||
: MatrixCoefficient(dim), pml(pml_), Function(F)
|
||||
{}
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double x[3];
|
||||
Vector transip(x, 3);
|
||||
T.Transform(ip, transip);
|
||||
K.SetSize(height, width);
|
||||
(*Function)(transip, pml, K);
|
||||
}
|
||||
};
|
||||
|
||||
// Helmholtz pml Functions
|
||||
double pml_detJ_Re(const Vector & x, CartesianPML * pml);
|
||||
double pml_detJ_Im(const Vector & x, CartesianPML * pml);
|
||||
void pml_detJ_JT_J_inv_Re(const Vector & x, CartesianPML * pml , DenseMatrix & M);
|
||||
void pml_detJ_JT_J_inv_Im(const Vector & x, CartesianPML * pml , DenseMatrix & M);
|
||||
|
||||
// Maxwell Pml functions
|
||||
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
|
||||
@@ -0,0 +1,619 @@
|
||||
#include "Utilities.hpp"
|
||||
|
||||
Sweep::Sweep(int dim_) : dim(dim_)
|
||||
{
|
||||
nsweeps = pow(2,dim);
|
||||
sweeps.resize(nsweeps);
|
||||
|
||||
for (int is = 0; is<nsweeps; is++)
|
||||
{
|
||||
sweeps[is].SetSize(dim);
|
||||
}
|
||||
|
||||
switch(dim)
|
||||
{
|
||||
case 1:
|
||||
sweeps[0][0] = 1;
|
||||
sweeps[1][0] = -1;
|
||||
break;
|
||||
case 2:
|
||||
sweeps[0][0] = 1; sweeps[0][1] = 1;
|
||||
sweeps[1][0] = -1; sweeps[1][1] = 1;
|
||||
sweeps[2][0] = 1; sweeps[2][1] = -1;
|
||||
sweeps[3][0] = -1; sweeps[3][1] = -1;
|
||||
break;
|
||||
default:
|
||||
sweeps[0][0] = 1; sweeps[0][1] = 1; sweeps[0][2] = 1;
|
||||
sweeps[1][0] = -1; sweeps[1][1] = 1; sweeps[1][2] = 1;
|
||||
sweeps[2][0] = 1; sweeps[2][1] = -1; sweeps[2][2] = 1;
|
||||
sweeps[3][0] = -1; sweeps[3][1] = -1; sweeps[3][2] = 1;
|
||||
sweeps[4][0] = 1; sweeps[4][1] = 1; sweeps[4][2] = -1;
|
||||
sweeps[5][0] = -1; sweeps[5][1] = 1; sweeps[5][2] = -1;
|
||||
sweeps[6][0] = 1; sweeps[6][1] = -1; sweeps[6][2] = -1;
|
||||
sweeps[7][0] = -1; sweeps[7][1] = -1; sweeps[7][2] = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Sweep::~Sweep()
|
||||
{
|
||||
for (int i = 0; i<nsweeps; i++)
|
||||
{
|
||||
sweeps[i].DeleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double CutOffFncn(const Vector &x, const Vector & pmin, const Vector & pmax, const Array2D<double> & h_)
|
||||
{
|
||||
int dim = pmin.Size();
|
||||
Vector h0(dim);
|
||||
Vector h1(dim);
|
||||
for (int i=0; i<dim; i++)
|
||||
{
|
||||
h0(i) = h_[i][0];
|
||||
h1(i) = h_[i][1];
|
||||
}
|
||||
Vector x0(dim);
|
||||
Vector x1(dim);
|
||||
x0 = pmin; x0+=h0;
|
||||
x1 = pmax; x1-=h1;
|
||||
|
||||
double f = 1.0;
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
double val = 1.0;
|
||||
if( x(i) >= pmax(i) || x(i) <= pmin(i))
|
||||
{
|
||||
val = 0.0;
|
||||
}
|
||||
else if (x(i) < pmax(i) && x(i) >= x1(i))
|
||||
{
|
||||
if(h1(i) != 0.0)
|
||||
// val = (x(i)-pmax(i))/(x1(i)-pmax(i));
|
||||
val = pow((x(i)-pmax(i))/(x1(i)-pmax(i)),1.0);
|
||||
}
|
||||
else if (x(i) > pmin(i) && x(i) <= x0(i))
|
||||
{
|
||||
if (h0(i) != 0.0)
|
||||
// val = (x(i)-pmin(i))/(x0(i)-pmin(i));
|
||||
val = pow((x(i)-pmin(i))/(x0(i)-pmin(i)),1.0);
|
||||
}
|
||||
|
||||
if (h0(i) == 0 && x(i) <= x1(i))
|
||||
{
|
||||
val = 1.0;
|
||||
}
|
||||
if (h1(i) == 0 && x(i) >= x0(i))
|
||||
{
|
||||
val = 1.0;
|
||||
}
|
||||
f *= val;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
double ChiFncn(const Vector &x, const Vector & pmin, const Vector & pmax, const Array2D<double> & h_)
|
||||
{
|
||||
int dim = pmin.Size();
|
||||
Vector h0(dim);
|
||||
Vector h1(dim);
|
||||
for (int i=0; i<dim; i++)
|
||||
{
|
||||
h0(i) = h_[i][0];
|
||||
h1(i) = h_[i][1];
|
||||
}
|
||||
Vector x0(dim);
|
||||
Vector x1(dim);
|
||||
x0 = pmin; x0+=h0;
|
||||
x1 = pmax; x1-=h1;
|
||||
|
||||
double f = 1.0;
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
double val = 1.0;
|
||||
if( x(i) >= pmax(i) || x(i) <= pmin(i))
|
||||
{
|
||||
val = 0.0;
|
||||
}
|
||||
else if (x(i) < pmax(i) && x(i) >= x1(i))
|
||||
{
|
||||
if(h1(i) != 0.0)
|
||||
val = (x(i)-pmax(i))/(x1(i)-pmax(i));
|
||||
// This function has to be changed to smth more reasonable
|
||||
// val = pow((x(i)-pmax(i))/(x1(i)-pmax(i)),100.0);
|
||||
}
|
||||
else if (x(i) > pmin(i) && x(i) <= x0(i))
|
||||
{
|
||||
if (h0(i) != 0.0)
|
||||
val = (x(i)-pmin(i))/(x0(i)-pmin(i));
|
||||
// val = pow((x(i)-pmin(i))/(x0(i)-pmin(i)),100.0);
|
||||
}
|
||||
|
||||
if (h0(i) == 0 && x(i) <= x1(i))
|
||||
{
|
||||
val = 1.0;
|
||||
}
|
||||
if (h1(i) == 0 && x(i) >= x0(i))
|
||||
{
|
||||
val = 1.0;
|
||||
}
|
||||
f *= val;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
DofMap::DofMap(FiniteElementSpace * fes , MeshPartition * partition)
|
||||
{
|
||||
const FiniteElementCollection * fec = fes->FEColl();
|
||||
nrpatch = partition->nrpatch;
|
||||
|
||||
fespaces.SetSize(nrpatch);
|
||||
|
||||
Dof2GlobalDof.resize(nrpatch);
|
||||
|
||||
for (int ip=0; ip<nrpatch; ++ip)
|
||||
{
|
||||
// create finite element spaces for each patch
|
||||
fespaces[ip] = new FiniteElementSpace(partition->patch_mesh[ip],fec);
|
||||
|
||||
// construct the patch tdof to global tdof map
|
||||
int nrdof = fespaces[ip]->GetTrueVSize();
|
||||
Dof2GlobalDof[ip].SetSize(2*nrdof);
|
||||
|
||||
// loop through the elements in the patch
|
||||
for (int iel = 0; iel<partition->element_map[ip].Size(); ++iel)
|
||||
{
|
||||
// index in the global mesh
|
||||
int iel_idx = partition->element_map[ip][iel];
|
||||
// get the dofs of this element
|
||||
Array<int> ElemDofs;
|
||||
Array<int> GlobalElemDofs;
|
||||
fespaces[ip]->GetElementDofs(iel,ElemDofs);
|
||||
fes->GetElementDofs(iel_idx,GlobalElemDofs);
|
||||
// the sizes have to match
|
||||
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
|
||||
"Size inconsistency");
|
||||
// loop through the dofs and take into account the signs;
|
||||
int ndof = ElemDofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int pdof_ = ElemDofs[i];
|
||||
int gdof_ = GlobalElemDofs[i];
|
||||
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
|
||||
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
|
||||
Dof2GlobalDof[ip][pdof] = gdof;
|
||||
Dof2GlobalDof[ip][pdof+nrdof] = gdof+fes->GetTrueVSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DofMap::DofMap(FiniteElementSpace * fes , MeshPartition * partition, int nrlayers)
|
||||
{
|
||||
|
||||
nx = partition->nxyz[0];
|
||||
ny = partition->nxyz[1];
|
||||
nz = partition->nxyz[2];
|
||||
|
||||
int partition_kind = partition->partition_kind;
|
||||
// Mesh * mesh = fespace->GetMesh();
|
||||
const FiniteElementCollection * fec = fes->FEColl();
|
||||
nrpatch = partition->nrpatch;
|
||||
|
||||
fespaces.SetSize(nrpatch);
|
||||
PmlMeshes.SetSize(nrpatch);
|
||||
// Extend patch meshes to include pml
|
||||
|
||||
for (int ip = 0; ip<nrpatch; ip++)
|
||||
{
|
||||
int k = ip/(nx*ny);
|
||||
int j = (ip-k*nx*ny)/nx;
|
||||
int i = (ip-k*nx*ny)%nx;
|
||||
|
||||
Array<int> directions;
|
||||
if (i > 0)
|
||||
{
|
||||
for (int i=0; i<nrlayers; i++)
|
||||
{
|
||||
directions.Append(-1);
|
||||
}
|
||||
}
|
||||
if (j > 0)
|
||||
{
|
||||
for (int i=0; i<nrlayers; i++)
|
||||
{
|
||||
directions.Append(-2);
|
||||
}
|
||||
}
|
||||
if (k > 0)
|
||||
{
|
||||
for (int i=0; i<nrlayers; i++)
|
||||
{
|
||||
directions.Append(-3);
|
||||
}
|
||||
}
|
||||
if (i < nx-1)
|
||||
{
|
||||
for (int i=0; i<nrlayers; i++)
|
||||
{
|
||||
if (partition_kind == 3 || partition_kind == 2) directions.Append(1);
|
||||
}
|
||||
}
|
||||
if (j < ny-1)
|
||||
{
|
||||
for (int i=0; i<nrlayers; i++)
|
||||
{
|
||||
if (partition_kind == 3 || partition_kind == 2) directions.Append(2);
|
||||
}
|
||||
}
|
||||
if (k < nz-1)
|
||||
{
|
||||
for (int i=0; i<nrlayers; i++)
|
||||
{
|
||||
if (partition_kind == 3 || partition_kind == 2) directions.Append(1);
|
||||
}
|
||||
}
|
||||
PmlMeshes[ip] = ExtendMesh(partition->patch_mesh[ip],directions);
|
||||
}
|
||||
|
||||
// Save PML_meshes
|
||||
string meshpath;
|
||||
string solpath;
|
||||
if (partition_kind == 3 || partition_kind == 2)
|
||||
{
|
||||
meshpath = "output/mesh_ovlp_pml.";
|
||||
solpath = "output/sol_ovlp_pml.";
|
||||
}
|
||||
else if (partition_kind == 4)
|
||||
{
|
||||
meshpath = "output/mesh_novlp_pml.";
|
||||
solpath = "output/sol_novlp_pml.";
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("This partition kind not supported yet");
|
||||
}
|
||||
|
||||
// SaveMeshPartition(PmlMeshes, meshpath, solpath);
|
||||
|
||||
PmlFespaces.SetSize(nrpatch);
|
||||
Dof2GlobalDof.resize(nrpatch);
|
||||
Dof2PmlDof.resize(nrpatch);
|
||||
|
||||
for (int ip=0; ip<nrpatch; ++ip)
|
||||
{
|
||||
// create finite element spaces for each patch
|
||||
fespaces[ip] = new FiniteElementSpace(partition->patch_mesh[ip],fec);
|
||||
PmlFespaces[ip] = new FiniteElementSpace(PmlMeshes[ip],fec);
|
||||
|
||||
// construct the patch tdof to global tdof map
|
||||
int nrdof = fespaces[ip]->GetTrueVSize();
|
||||
Dof2GlobalDof[ip].SetSize(2*nrdof);
|
||||
Dof2PmlDof[ip].SetSize(2*nrdof);
|
||||
|
||||
// build dof maps between patch and extended patch
|
||||
// loop through the patch elements and constract the dof map
|
||||
// The same elements in the extended mesh have the same ordering (but not the dofs)
|
||||
|
||||
// loop through the elements in the patch
|
||||
for (int iel = 0; iel<partition->element_map[ip].Size(); ++iel)
|
||||
{
|
||||
// index in the global mesh
|
||||
int iel_idx = partition->element_map[ip][iel];
|
||||
// get the dofs of this element
|
||||
Array<int> ElemDofs;
|
||||
Array<int> PmlElemDofs;
|
||||
Array<int> GlobalElemDofs;
|
||||
fespaces[ip]->GetElementDofs(iel,ElemDofs);
|
||||
PmlFespaces[ip]->GetElementDofs(iel,PmlElemDofs);
|
||||
fes->GetElementDofs(iel_idx,GlobalElemDofs);
|
||||
// the sizes have to match
|
||||
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
|
||||
"Size inconsistency");
|
||||
MFEM_VERIFY(ElemDofs.Size() == PmlElemDofs.Size(),
|
||||
"Size inconsistency");
|
||||
// loop through the dofs and take into account the signs;
|
||||
int ndof = ElemDofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int pdof_ = ElemDofs[i];
|
||||
int gdof_ = GlobalElemDofs[i];
|
||||
int pmldof_ = PmlElemDofs[i];
|
||||
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
|
||||
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
|
||||
int pmldof = (pmldof_ >= 0) ? pmldof_ : abs(pmldof_) - 1;
|
||||
|
||||
Dof2GlobalDof[ip][pdof] = gdof;
|
||||
Dof2GlobalDof[ip][pdof+nrdof] = gdof+fes->GetTrueVSize();
|
||||
Dof2PmlDof[ip][pdof] = pmldof;
|
||||
Dof2PmlDof[ip][pdof+nrdof] = pmldof+PmlFespaces[ip]->GetTrueVSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LocalDofMap::LocalDofMap(const FiniteElementCollection * fec_, MeshPartition * part1_,
|
||||
MeshPartition * part2_):fec(fec_), part1(part1_), part2(part2_)
|
||||
{
|
||||
// Each overlapping patch has 2 non-overlapping subdomains
|
||||
// Thre are n non-overlapping and and n-1 overlapping subdomains
|
||||
int nrpatch = part2->nrpatch;
|
||||
MFEM_VERIFY(part1->nrpatch-1 == part2->nrpatch, "Check number of subdomains");
|
||||
|
||||
cout << "Constructing local dof maps" << endl;
|
||||
map1.resize(nrpatch);
|
||||
map2.resize(nrpatch);
|
||||
for (int ip=0; ip<nrpatch; ip++)
|
||||
{
|
||||
// Get the 3 meshes involved
|
||||
Mesh * mesh = part2->patch_mesh[ip];
|
||||
Mesh * mesh1 = part1->patch_mesh[ip];
|
||||
Mesh * mesh2 = part1->patch_mesh[ip+1];
|
||||
|
||||
// Define the fespaces
|
||||
FiniteElementSpace fespace(mesh, fec);
|
||||
FiniteElementSpace fespace1(mesh1, fec);
|
||||
FiniteElementSpace fespace2(mesh2, fec);
|
||||
|
||||
int ndof1 = fespace1.GetTrueVSize();
|
||||
int ndof2 = fespace2.GetTrueVSize();
|
||||
|
||||
map1[ip].SetSize(2*ndof1); // times 2 because it's complex
|
||||
map2[ip].SetSize(2*ndof2); // times 2 because it's complex
|
||||
|
||||
// loop through the elements in the patches
|
||||
// map 1 is constructed by the first half of elements
|
||||
// map 2 is constructed by the second half of elements
|
||||
|
||||
for (int iel = 0; iel<part1->element_map[ip].Size(); ++iel)
|
||||
{
|
||||
// index in the overlapping mesh
|
||||
int iel_idx = iel;
|
||||
Array<int> ElemDofs;
|
||||
Array<int> GlobalElemDofs;
|
||||
fespace1.GetElementDofs(iel,ElemDofs);
|
||||
fespace.GetElementDofs(iel_idx,GlobalElemDofs);
|
||||
// the sizes have to match
|
||||
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
|
||||
"Size inconsistency");
|
||||
// loop through the dofs and take into account the signs;
|
||||
int ndof = ElemDofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int pdof_ = ElemDofs[i];
|
||||
int gdof_ = GlobalElemDofs[i];
|
||||
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
|
||||
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
|
||||
map1[ip][pdof] = gdof;
|
||||
map1[ip][pdof+ndof1] = gdof+fespace.GetTrueVSize();
|
||||
}
|
||||
}
|
||||
for (int iel = 0; iel<part1->element_map[ip+1].Size(); ++iel)
|
||||
{
|
||||
// index in the overlapping mesh
|
||||
int k = part1->element_map[ip].Size();
|
||||
int iel_idx = iel+k;
|
||||
Array<int> ElemDofs;
|
||||
Array<int> GlobalElemDofs;
|
||||
fespace2.GetElementDofs(iel,ElemDofs);
|
||||
fespace.GetElementDofs(iel_idx,GlobalElemDofs);
|
||||
// the sizes have to match
|
||||
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
|
||||
"Size inconsistency");
|
||||
// loop through the dofs and take into account the signs;
|
||||
int ndof = ElemDofs.Size();
|
||||
for (int i = 0; i<ndof; ++i)
|
||||
{
|
||||
int pdof_ = ElemDofs[i];
|
||||
int gdof_ = GlobalElemDofs[i];
|
||||
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
|
||||
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
|
||||
map2[ip][pdof] = gdof;
|
||||
map2[ip][pdof+ndof2] = gdof+fespace.GetTrueVSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
NeighborDofMaps::NeighborDofMaps(MeshPartition * part_, FiniteElementSpace * fes_,
|
||||
DofMap * dmap_,
|
||||
int ovlp_layers_) : part(part_), fes(fes_),
|
||||
dmap(dmap_),
|
||||
ovlp_layers(ovlp_layers_)
|
||||
{
|
||||
|
||||
nrsubdomains = part->nrpatch;
|
||||
nxyz.SetSize(3);
|
||||
mesh = fes->GetMesh();
|
||||
dim = mesh->Dimension();
|
||||
for (int d=0; d<3; d++) nxyz[d] = part->nxyz[d];
|
||||
MarkOvlpElements();
|
||||
ComputeNeighborDofMaps();
|
||||
}
|
||||
|
||||
void NeighborDofMaps::MarkOvlpElements()
|
||||
{
|
||||
// Lists of elements
|
||||
// x,y,z = +/- 1 ovlp
|
||||
OvlpElems.resize(nrsubdomains);
|
||||
|
||||
for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
{
|
||||
int i0,j0,k0;
|
||||
Getijk(ip,i0,j0,k0);
|
||||
int ijk[dim]; ijk[0] = i0; ijk[1]=j0;
|
||||
if (dim==3) ijk[2] = k0;
|
||||
|
||||
FiniteElementSpace * sub_fes = dmap->fespaces[ip];
|
||||
Mesh * sub_mesh = sub_fes->GetMesh();
|
||||
// OvlpElems[ip].resize(2*dim);
|
||||
OvlpElems[ip].resize(pow(3,dim));
|
||||
|
||||
Vector pmin, pmax;
|
||||
sub_mesh->GetBoundingBox(pmin,pmax);
|
||||
double h = part->MeshSize;
|
||||
// Loop through elements
|
||||
for (int iel=0; iel<sub_mesh->GetNE(); iel++)
|
||||
{
|
||||
// Get element center
|
||||
Vector center(dim);
|
||||
int geom = sub_mesh->GetElementBaseGeometry(iel);
|
||||
ElementTransformation * tr = sub_mesh->GetElementTransformation(iel);
|
||||
tr->Transform(Geometries.GetCenter(geom),center);
|
||||
|
||||
// loop through dimensions
|
||||
Array<bool> pos(dim); pos = 0;
|
||||
Array<bool> neg(dim); neg = 0;
|
||||
|
||||
for (int d=0;d<dim; d++)
|
||||
{
|
||||
if (ijk[d]>0 && center[d] < pmin[d]+2.0*h*ovlp_layers)
|
||||
{
|
||||
neg[d] = true;
|
||||
}
|
||||
|
||||
if (ijk[d]<nxyz[d]-1 && center[d] > pmax[d]-2.0*h*ovlp_layers)
|
||||
{
|
||||
pos[d] = true;
|
||||
}
|
||||
}
|
||||
SetElementToOverlap(ip,iel,neg,pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NeighborDofMaps::ComputeNeighborDofMaps()
|
||||
{
|
||||
OvlpDofMaps.resize(nrsubdomains);
|
||||
|
||||
// Array<UniqueIndexGen * > Gen(nrsubdomains);
|
||||
// // construct unique number generator for the elements of a patch
|
||||
// for (int ip = 0; ip<nrsubdomains; ip++)
|
||||
// {
|
||||
// Gen[ip] = new UniqueIndexGen;
|
||||
// // register the elements
|
||||
// int nel = part->element_map[ip].Size();
|
||||
// for (int iel=0; iel<nel; iel++)
|
||||
// {
|
||||
// int iel_idx = part->element_map[ip][iel];
|
||||
// Gen[ip]->Set(iel_idx);
|
||||
// }
|
||||
// }
|
||||
|
||||
// construct dof maps
|
||||
int nrneighbors = pow(3,dim); // including its self
|
||||
|
||||
for (int ip0 = 0; ip0<nrsubdomains; ip0++)
|
||||
{
|
||||
OvlpDofMaps[ip0].resize(nrneighbors);
|
||||
|
||||
FiniteElementSpace * fes0 = dmap->fespaces[ip0];
|
||||
int tdofs0 = fes0->GetTrueVSize();
|
||||
Array<int> marker0(tdofs0); marker0 = 0;
|
||||
int i0, j0, k0;
|
||||
Array<int> ijk(dim);
|
||||
Getijk(ip0, i0,j0,k0);
|
||||
|
||||
int kbeg = (dim == 2) ? 0 : -1;
|
||||
int kend = (dim == 2) ? 1 : 2;
|
||||
for (int k=kbeg; k<kend; k++)
|
||||
{
|
||||
int k1 = k0 + k;
|
||||
if (k1 <0 || k1>=nxyz[2]) continue;
|
||||
int kk = (dim == 2) ? -1 : k;
|
||||
for (int j=-1; j<2; j++)
|
||||
{
|
||||
int j1 = j0 + j;
|
||||
if (j1 <0 || j1>=nxyz[1]) continue;
|
||||
for (int i=-1; i<2; i++)
|
||||
{
|
||||
int i1 = i0 + i;
|
||||
if (i1 <0 || i1>=nxyz[0]) continue;
|
||||
|
||||
Array<int> ip0list; marker0 = 0;
|
||||
int directionId = GetDirectionId(i,j,kk);
|
||||
|
||||
Array<int> Elems = OvlpElems[ip0][directionId];
|
||||
int nel = Elems.Size();
|
||||
|
||||
for (int iel = 0; iel<nel; ++iel)
|
||||
{
|
||||
int iel0 = Elems[iel];
|
||||
Array<int> ElemDofs0;
|
||||
|
||||
fes0->GetElementDofs(iel0,ElemDofs0);
|
||||
int ndof = ElemDofs0.Size();
|
||||
// since the elements are added to the subdomain meshes
|
||||
// in the same ordered fashion (as they come from the
|
||||
// original mesh) then the ordering of elements in each
|
||||
// subdomain is the same. Hence the dof ovlp lists
|
||||
// can be computed for each subdomain independendly
|
||||
for (int l = 0; l<ndof; ++l)
|
||||
{
|
||||
int dof0_ = ElemDofs0[l];
|
||||
int dof0 = (dof0_ >= 0) ? dof0_ : abs(dof0_) - 1;
|
||||
if (!marker0[dof0])
|
||||
{
|
||||
ip0list.Append(dof0); // dofs of ip0 in ovlp
|
||||
marker0[dof0] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OvlpDofMaps[ip0][directionId].Append(ip0list);
|
||||
int tsize = fes0->GetTrueVSize();
|
||||
// Imaginary part
|
||||
for (int l=0;l<ip0list.Size(); l++) { ip0list[l] += tsize; }
|
||||
OvlpDofMaps[ip0][directionId].Append(ip0list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NeighborDofMaps::GetNeighborDofMap(const int ip,
|
||||
const Array<int> & directions,
|
||||
Array<int> & dofmap)
|
||||
{
|
||||
int k = (dim == 2) ? -1 : directions[2];
|
||||
int directionid = GetDirectionId(directions[0],directions[1],k);
|
||||
dofmap = OvlpDofMaps[ip][directionid];
|
||||
}
|
||||
|
||||
|
||||
void NeighborDofMaps::SetElementToOverlap(int ip, int iel,
|
||||
const Array<bool> & neg,
|
||||
const Array<bool> & pos)
|
||||
{
|
||||
int kbeg = (dim == 2) ? 0 : -1;
|
||||
int kend = (dim == 2) ? 0 : 1;
|
||||
for (int k = kbeg; k<=kend; k++)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
if (k == -1 && !neg[2]) continue;
|
||||
if (k == 1 && !pos[2]) continue;
|
||||
}
|
||||
for (int j = -1; j<=1; j++)
|
||||
{
|
||||
if (j== -1 && !neg[1]) continue;
|
||||
if (j== 1 && !pos[1]) continue;
|
||||
for (int i = -1; i<=1; i++)
|
||||
{
|
||||
// cases to skip
|
||||
if (i==-1 && !neg[0]) continue;
|
||||
if (i== 1 && !pos[0]) continue;
|
||||
|
||||
if (i==0 && j==0 && k == 0) continue;
|
||||
int kk = (dim==2)?-1 : k;
|
||||
int DirId = GetDirectionId(i,j,kk);
|
||||
OvlpElems[ip][DirId].Append(iel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
#include "MeshPartition.hpp"
|
||||
#include "complex_linalg.hpp"
|
||||
|
||||
struct UniqueIndexGen
|
||||
{
|
||||
int counter = 0;
|
||||
std::unordered_map<int,int> idx;
|
||||
|
||||
void Set(int i)
|
||||
{
|
||||
std::unordered_map<int,int>::iterator f = idx.find(i);
|
||||
if (f == idx.end())
|
||||
{
|
||||
idx[i] = counter;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
int Get(int i)
|
||||
{
|
||||
std::unordered_map<int,int>::iterator f = idx.find(i);
|
||||
if (f == idx.end())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*f).second;
|
||||
}
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
counter = 0;
|
||||
idx.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Sweep
|
||||
{
|
||||
private:
|
||||
int dim;
|
||||
std::vector<Array<int>> sweeps;
|
||||
public:
|
||||
int nsweeps;
|
||||
Sweep(int dim_);
|
||||
~Sweep();
|
||||
void GetSweep(const int i, Array<int> & sweep)
|
||||
{
|
||||
MFEM_VERIFY(i<nsweeps, "Sweep number out of bounds");
|
||||
sweep.SetSize(dim);
|
||||
sweep = sweeps[i];
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Function coefficient that takes the bounding box of the mesh as an input
|
||||
class CutOffFnCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
double (*Function)(const Vector &, const Vector &, const Vector &, const Array2D<double> &);
|
||||
Vector pmin, pmax;
|
||||
Array2D<double> h; // specify the with of the cutoff function (h in each direction)
|
||||
|
||||
|
||||
public:
|
||||
CutOffFnCoefficient(double (*F)(const Vector &, const Vector &, const Vector &, const Array2D<double> &),
|
||||
const Vector & pmin_, const Vector & pmax_, Array2D<double> & h_)
|
||||
: Function(F), pmin(pmin_), pmax(pmax_), h(h_)
|
||||
{}
|
||||
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{
|
||||
double x[3];
|
||||
Vector transip(x, 3);
|
||||
T.Transform(ip, transip);
|
||||
return ((*Function)(transip, pmin, pmax, h));
|
||||
}
|
||||
};
|
||||
|
||||
double CutOffFncn(const Vector &x, const Vector & pmin,
|
||||
const Vector & pmax, const Array2D<double> & h_);
|
||||
double ChiFncn(const Vector &x, const Vector & pmin,
|
||||
const Vector & pmax, const Array2D<double> & h_);
|
||||
|
||||
class DofMap // Constructs dof maps for a given partition
|
||||
{
|
||||
public:
|
||||
int nrpatch, nx, ny, nz;
|
||||
vector<Array<int>> Dof2GlobalDof;
|
||||
vector<Array<int>> Dof2PmlDof;
|
||||
Array<Mesh *> PmlMeshes;
|
||||
Array<FiniteElementSpace *> fespaces;
|
||||
Array<FiniteElementSpace *> PmlFespaces;
|
||||
// constructor
|
||||
// Non PML constructor dof map
|
||||
DofMap(FiniteElementSpace * fes, MeshPartition * partition);
|
||||
// PML
|
||||
DofMap(FiniteElementSpace * fes , MeshPartition * partition, int nrlayers);
|
||||
~DofMap(){};
|
||||
};
|
||||
|
||||
|
||||
|
||||
class LocalDofMap // Constructs dof mapbetween two partitions
|
||||
{
|
||||
const FiniteElementCollection *fec=nullptr;
|
||||
MeshPartition * part1=nullptr;
|
||||
MeshPartition * part2=nullptr;
|
||||
public:
|
||||
int nrpatch, nx, ny, nz;
|
||||
vector<Array<int>> map1;
|
||||
vector<Array<int>> map2;
|
||||
// constructor
|
||||
LocalDofMap(const FiniteElementCollection * fec_, MeshPartition * part1_,
|
||||
MeshPartition * part2_);
|
||||
~LocalDofMap();
|
||||
};
|
||||
|
||||
|
||||
struct NeighborDofMaps
|
||||
{
|
||||
private:
|
||||
int dim;
|
||||
MeshPartition * part = nullptr;
|
||||
FiniteElementSpace * fes = nullptr;
|
||||
Mesh * mesh = nullptr;
|
||||
std::vector<std::vector<Array<int>>> OvlpElems;
|
||||
std::vector<std::vector<Array<int>>> OvlpDofMaps;
|
||||
|
||||
DofMap * dmap = nullptr;
|
||||
int nrsubdomains = 0;
|
||||
int ovlp_layers = 0;
|
||||
Array<int> nxyz;
|
||||
void SetElementToOverlap(int ip, int iel,
|
||||
const Array<bool> & neg,
|
||||
const Array<bool> & pos);
|
||||
|
||||
void MarkOvlpElements();
|
||||
void ComputeNeighborDofMaps();
|
||||
|
||||
void Getijk(int ip, int & i, int & j, int & k) const
|
||||
{
|
||||
k = ip/(nxyz[0]*nxyz[1]);
|
||||
j = (ip-k*nxyz[0]*nxyz[1])/nxyz[0];
|
||||
i = (ip-k*nxyz[0]*nxyz[1])%nxyz[0];
|
||||
}
|
||||
|
||||
int GetPatchId(const Array<int> & ijk) const
|
||||
{
|
||||
int d=ijk.Size();
|
||||
int z = (d==2)? 0 : ijk[2];
|
||||
return part->subdomains(ijk[0],ijk[1],z);
|
||||
}
|
||||
int GetDirectionId(int i, int j, int k=-1)
|
||||
{
|
||||
int n = 3;
|
||||
return (k+1)*n*n + (j+1)*n + i+1;
|
||||
}
|
||||
void GetDirections(const int id, int & i, int & j, int & k)
|
||||
{
|
||||
int n = 3;
|
||||
k = id/(n*n) - 1;
|
||||
j = (id-(k+1)*n*n)/n - 1;
|
||||
i = (id-(k+1)*n*n)%n - 1;
|
||||
}
|
||||
|
||||
public:
|
||||
NeighborDofMaps(MeshPartition * part_,
|
||||
FiniteElementSpace * fes_,
|
||||
DofMap * dmap_,
|
||||
int ovlp_layers_);
|
||||
|
||||
void GetNeighborDofMap(const int ip, const Array<int> & directions,
|
||||
Array<int> & dofmap);
|
||||
};
|
||||
@@ -0,0 +1,358 @@
|
||||
|
||||
#include "../../../linalg/kernels.hpp"
|
||||
#include "complex_linalg.hpp"
|
||||
|
||||
|
||||
ComplexDenseMatrix::ComplexDenseMatrix(){}
|
||||
|
||||
ComplexDenseMatrix::ComplexDenseMatrix(int s)
|
||||
{
|
||||
MFEM_ASSERT(s >= 0, "invalid ComplexDenseMatrix size: " << s);
|
||||
height = s;
|
||||
width = s;
|
||||
if (s > 0)
|
||||
{
|
||||
data = new complex<double>[s*s];
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ComplexDenseMatrix::ComplexDenseMatrix(int m, int n)
|
||||
{
|
||||
MFEM_VERIFY(m >= 0 && n >= 0,
|
||||
"invalid DenseMatrix size: " << m << " x " << n);
|
||||
const int s = m*n;
|
||||
height = m;
|
||||
width = n;
|
||||
if (s > 0)
|
||||
{
|
||||
data = new complex<double>[s];
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexDenseMatrix::SetSize(int h, int w)
|
||||
{
|
||||
MFEM_VERIFY(h >= 0 && w >= 0,
|
||||
"invalid ComplexDenseMatrix size: " << h << " x " << w);
|
||||
if (Height() == h && Width() == w)
|
||||
{
|
||||
return;
|
||||
}
|
||||
height = h;
|
||||
width = w;
|
||||
const int hw = h*w;
|
||||
delete data;
|
||||
data = new complex<double>[hw];
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator=(double c)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator=(complex<double> c)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
std::complex<double> ComplexDenseMatrix::Det() const
|
||||
{
|
||||
MFEM_ASSERT(Height() == Width() && Height() > 0,
|
||||
"The matrix must be square and "
|
||||
<< "sized larger than zero to compute the determinant."
|
||||
<< " Height() = " << Height()
|
||||
<< ", Width() = " << Width());
|
||||
|
||||
switch (Height())
|
||||
{
|
||||
case 1:
|
||||
return data[0];
|
||||
|
||||
case 2:
|
||||
return data[0] * data[3] - data[1] * data[2];
|
||||
|
||||
case 3:
|
||||
{
|
||||
const complex<double> *d = data;
|
||||
return
|
||||
d[0] * (d[4] * d[8] - d[5] * d[7]) +
|
||||
d[3] * (d[2] * d[7] - d[1] * d[8]) +
|
||||
d[6] * (d[1] * d[5] - d[2] * d[4]);
|
||||
}
|
||||
default:
|
||||
{
|
||||
MFEM_ABORT("dim>3 not supported yet");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix * ComplexDenseMatrix::real() const
|
||||
{
|
||||
DenseMatrix * Ar = new DenseMatrix(height,width);
|
||||
double * data = Ar->Data();
|
||||
complex<double> * zdata = this->data;
|
||||
for (int s = 0; s<height*width; s++)
|
||||
{
|
||||
data[s] = zdata[s].real();
|
||||
}
|
||||
return Ar;
|
||||
}
|
||||
DenseMatrix * ComplexDenseMatrix::imag() const
|
||||
{
|
||||
DenseMatrix * Ai = new DenseMatrix(height,width);
|
||||
double * data = Ai->Data();
|
||||
complex<double> * zdata = this->data;
|
||||
for (int s = 0; s<height*width; s++)
|
||||
{
|
||||
data[s] = zdata[s].imag();
|
||||
}
|
||||
return Ai;
|
||||
}
|
||||
|
||||
void ComplexDenseMatrix::GetReal(DenseMatrix & Ar)
|
||||
{
|
||||
MFEM_ASSERT(Ar.Height() == height && Ar.Width() == width, "Incompatible dimensions");
|
||||
double * data = Ar.Data();
|
||||
complex<double> * zdata = this->data;
|
||||
for (int s = 0; s<height*width; s++)
|
||||
{
|
||||
data[s] = zdata[s].real();
|
||||
}
|
||||
}
|
||||
|
||||
void ComplexDenseMatrix::GetImag(DenseMatrix & Ai)
|
||||
{
|
||||
double * data = Ai.Data();
|
||||
complex<double> * zdata = this->data;
|
||||
for (int s = 0; s<height*width; s++)
|
||||
{
|
||||
data[s] = zdata[s].imag();
|
||||
}
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator=(const ComplexDenseMatrix &m)
|
||||
{
|
||||
SetSize(m.height, m.width);
|
||||
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = m.data[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator+=(const complex<double> *m)
|
||||
{
|
||||
const int hw = Height()*Width();
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] += m[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator+=(const ComplexDenseMatrix &m)
|
||||
{
|
||||
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
|
||||
"incompatible matrix sizes.");
|
||||
return *this += m.GetData();
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator-=(const ComplexDenseMatrix &m)
|
||||
{
|
||||
int s = Height()*Width();
|
||||
complex<double> * mdata = m.GetData();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] -= mdata[s];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComplexDenseMatrix &ComplexDenseMatrix::operator*=(complex<double> c)
|
||||
{
|
||||
int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] *= c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void ComplexDenseMatrix::Print(std::ostream &out, int width_) const
|
||||
{
|
||||
// save current output flags
|
||||
ios::fmtflags old_flags = out.flags();
|
||||
// output flags = scientific + show sign
|
||||
out << setiosflags(ios::scientific | ios::showpos);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
out << "[row " << i << "]\n";
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
out << (*this)(i,j);
|
||||
if (j+1 == width || (j+1) % width_ == 0)
|
||||
{
|
||||
out << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
out << ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
// reset output flags to original values
|
||||
out.flags(old_flags);
|
||||
}
|
||||
|
||||
void ComplexDenseMatrix::PrintMatlab(std::ostream &out) const
|
||||
{
|
||||
// save current output flags
|
||||
// ios::fmtflags old_flags = out.flags();
|
||||
// output flags = scientific + show sign
|
||||
// out << setiosflags(ios::scientific | ios::showpos);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
out << (*this)(i,j);
|
||||
out << ' ';
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
// reset output flags to original values
|
||||
// out.flags(old_flags);
|
||||
}
|
||||
|
||||
ComplexDenseMatrixInverse::ComplexDenseMatrixInverse(const ComplexDenseMatrix & A) : ComplexDenseMatrix(A.Height())
|
||||
{
|
||||
MFEM_VERIFY(A.Height() == A.Width(), "The matrix is not square");
|
||||
MFEM_VERIFY(A.Height() < 4, "dim > 3 is not supported yet");
|
||||
|
||||
std::complex<double> detA = A.Det();
|
||||
MFEM_VERIFY(abs(A.Det())>1e-14, "The given matrix is singular");
|
||||
|
||||
std::complex<double> * d = this->Data();
|
||||
std::complex<double> *dA = A.GetData();
|
||||
switch (A.Height())
|
||||
{
|
||||
case 1:
|
||||
d[0] = 1.0/dA[0];
|
||||
break;
|
||||
case 2:
|
||||
d[0] = 1.0/detA * dA[3];
|
||||
d[1] = -1.0/detA * dA[1];
|
||||
d[2] = -1.0/detA * dA[2];
|
||||
d[3] = 1.0/detA * dA[0];
|
||||
break;
|
||||
case 3:
|
||||
d[0] = 1.0/detA*(dA[4]*dA[8] - dA[5]*dA[7]);
|
||||
d[1] = -1.0/detA*(dA[1]*dA[8] - dA[2]*dA[7]);
|
||||
d[2] = 1.0/detA*(dA[1]*dA[5] - dA[2]*dA[4]);
|
||||
d[3] = -1.0/detA*(dA[3]*dA[8] - dA[5]*dA[6]);
|
||||
d[4] = 1.0/detA*(dA[0]*dA[8] - dA[2]*dA[6]);
|
||||
d[5] = -1.0/detA*(dA[0]*dA[5] - dA[2]*dA[3]);
|
||||
d[6] = 1.0/detA*(dA[3]*dA[7] - dA[4]*dA[6]);
|
||||
d[7] = -1.0/detA*(dA[0]*dA[7] - dA[1]*dA[6]);
|
||||
d[8] = 1.0/detA*(dA[0]*dA[4] - dA[1]*dA[3]);
|
||||
break;
|
||||
default:
|
||||
// Should be unreachable
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Matrix matrix multiplication. A = B * C.
|
||||
void Mult(const ComplexDenseMatrix &b, const ComplexDenseMatrix &c, ComplexDenseMatrix &a)
|
||||
{
|
||||
MFEM_ASSERT(a.Height() == b.Height() && a.Width() == c.Width() &&
|
||||
b.Width() == c.Height(), "incompatible dimensions");
|
||||
|
||||
const int ah = a.Height();
|
||||
const int aw = a.Width();
|
||||
const int bw = b.Width();
|
||||
complex<double> *ad = a.Data();
|
||||
const complex<double> *bd = b.Data();
|
||||
const complex<double> *cd = c.Data();
|
||||
kernels::Mult(ah,aw,bw,bd,cd,ad);
|
||||
}
|
||||
|
||||
/// Multiply the transpose of a matrix A with a matrix B: At*B
|
||||
void MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB)
|
||||
{
|
||||
MFEM_ASSERT(A.Width() == AtB.Height() && B.Width() == AtB.Width() &&
|
||||
A.Height() == B.Height(), "incompatible dimensions");
|
||||
const int ah = A.Height();
|
||||
const int aw = A.Width();
|
||||
const int bw = B.Width();
|
||||
const complex<double> *ad = A.Data();
|
||||
const complex<double> *bd = B.Data();
|
||||
complex<double> *cd = AtB.Data();
|
||||
|
||||
for (int j = 0; j < bw; j++)
|
||||
{
|
||||
const complex<double> *ap = ad;
|
||||
for (int i = 0; i < aw; i++)
|
||||
{
|
||||
complex<double> d = 0.0;
|
||||
for (int k = 0; k < ah; k++)
|
||||
{
|
||||
d += ap[k] * bd[k];
|
||||
}
|
||||
*(cd++) = d;
|
||||
ap += ah;
|
||||
}
|
||||
bd += ah;
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply the conjugate transpose of a matrix A with a matrix B: At*B
|
||||
void MultAhB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB)
|
||||
{
|
||||
MFEM_ASSERT(A.Width() == AtB.Height() && B.Width() == AtB.Width() &&
|
||||
A.Height() == B.Height(), "incompatible dimensions");
|
||||
MFEM_ASSERT(A.Width() == AtB.Height() && B.Width() == AtB.Width() &&
|
||||
A.Height() == B.Height(), "incompatible dimensions");
|
||||
const int ah = A.Height();
|
||||
const int aw = A.Width();
|
||||
const int bw = B.Width();
|
||||
const complex<double> *ad = A.Data();
|
||||
const complex<double> *bd = B.Data();
|
||||
complex<double> *cd = AtB.Data();
|
||||
|
||||
for (int j = 0; j < bw; j++)
|
||||
{
|
||||
const complex<double> *ap = ad;
|
||||
for (int i = 0; i < aw; i++)
|
||||
{
|
||||
complex<double> d = 0.0;
|
||||
for (int k = 0; k < ah; k++)
|
||||
{
|
||||
d += conj(ap[k]) * bd[k];
|
||||
}
|
||||
*(cd++) = d;
|
||||
ap += ah;
|
||||
}
|
||||
bd += ah;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ComplexDenseMatrix
|
||||
{
|
||||
private:
|
||||
std::complex<double> * data = nullptr;
|
||||
int height = 0;
|
||||
int width = 0;
|
||||
public:
|
||||
ComplexDenseMatrix();
|
||||
|
||||
/// Creates square matrix of size s.
|
||||
explicit ComplexDenseMatrix(int s);
|
||||
|
||||
/// Creates rectangular matrix of size m x n.
|
||||
ComplexDenseMatrix(int m, int n);
|
||||
|
||||
/// Change the size of the DenseMatrix to s x s.
|
||||
void SetSize(int s) { SetSize(s, s); }
|
||||
|
||||
/// Change the size of the DenseMatrix to h x w.
|
||||
void SetSize(int h, int w);
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline complex<double> *Data() const
|
||||
{ return const_cast<complex<double>*>((const complex<double>*)data);}
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline complex<double> *GetData() const { return Data(); }
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
inline complex<double> &operator()(int i, int j);
|
||||
inline const complex<double> &operator()(int i, int j) const;
|
||||
|
||||
inline int Height() const { return height; }
|
||||
inline int Width() const { return width; }
|
||||
|
||||
/// Sets the matrix elements equal to constant c
|
||||
ComplexDenseMatrix &operator=(std::complex<double> c);
|
||||
ComplexDenseMatrix &operator=(double c);
|
||||
|
||||
/// Sets the matrix size and elements equal to those of m
|
||||
ComplexDenseMatrix &operator=(const ComplexDenseMatrix &m);
|
||||
ComplexDenseMatrix &operator+=(const complex<double> *m);
|
||||
ComplexDenseMatrix &operator+=(const ComplexDenseMatrix &m);
|
||||
ComplexDenseMatrix &operator-=(const ComplexDenseMatrix &m);
|
||||
ComplexDenseMatrix &operator*=(complex<double> c);
|
||||
|
||||
/// Calculates the determinant of the matrix
|
||||
/// (for 2x2, 3x3)
|
||||
std::complex<double> Det() const;
|
||||
|
||||
virtual void Print(std::ostream &out = mfem::out, int width_ = 4) const;
|
||||
virtual void PrintMatlab(std::ostream &out = mfem::out) const;
|
||||
|
||||
DenseMatrix * real() const;
|
||||
DenseMatrix * imag() const;
|
||||
|
||||
void GetReal(DenseMatrix & Ar);
|
||||
void GetImag(DenseMatrix & Ai);
|
||||
};
|
||||
|
||||
inline complex<double> &ComplexDenseMatrix::operator()(int i, int j)
|
||||
{
|
||||
MFEM_VERIFY(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
// return data[i*width+j];
|
||||
return data[j*height+i];
|
||||
}
|
||||
|
||||
inline const complex<double> &ComplexDenseMatrix::operator()(int i, int j) const
|
||||
{
|
||||
MFEM_VERIFY(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
// return data[i*width+j];
|
||||
return data[j*height+i];
|
||||
}
|
||||
|
||||
|
||||
class ComplexDenseMatrixInverse : public ComplexDenseMatrix
|
||||
{
|
||||
private:
|
||||
public:
|
||||
ComplexDenseMatrixInverse(const ComplexDenseMatrix & );
|
||||
};
|
||||
|
||||
/// Matrix matrix multiplication. A = B * C.
|
||||
void Mult(const ComplexDenseMatrix &b, const ComplexDenseMatrix &c, ComplexDenseMatrix &a);
|
||||
|
||||
/// Multiply the transpose of a matrix A with a matrix B: At*B
|
||||
void MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB);
|
||||
|
||||
/// Multiply the conjugate transpose of a matrix A with a matrix B: At*B
|
||||
void MultAhB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB);
|
||||
@@ -0,0 +1,404 @@
|
||||
//
|
||||
// Compile with: make helmholtzp
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ./helmholtzp -nd 2 -nx 4 -ny 4 -sr 3 -pr 3 -k 16.0 -o 2
|
||||
// mpirun -np 4 ./helmholtzp -nd 3 -nx 2 -ny 2 -nz 2 -sr 3 -pr 1 -k 2.0 -o 2
|
||||
//
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include "ParDST/ParDST.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Exact solution and r.h.s., see below for implementation.
|
||||
double f_exact_Re(const Vector &x);
|
||||
double f_exact_Im(const Vector &x);
|
||||
|
||||
double wavespeed(const Vector &x);
|
||||
|
||||
double funccoeff_re(const Vector & x);
|
||||
double funccoeff_im(const Vector & x);
|
||||
|
||||
|
||||
int dim;
|
||||
double omega;
|
||||
int sol = 1;
|
||||
double length = 1.0;
|
||||
double pml_length = 0.25;
|
||||
Array2D<double>comp_bdr;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
// 2. Parse command-line options.
|
||||
// finite element order of approximation
|
||||
int order = 1;
|
||||
bool visualization = 1;
|
||||
// number of wavelengths
|
||||
double k = 0.5;
|
||||
// number of serial refinements
|
||||
int ser_ref_levels = 1;
|
||||
// number of parallel refinements
|
||||
int par_ref_levels = 2;
|
||||
// dimension
|
||||
int nd = 2;
|
||||
int nx=2;
|
||||
int ny=2;
|
||||
int nz=2;
|
||||
bool herm_conv = true;
|
||||
|
||||
// optional command line inputs
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&nd, "-nd", "--dim","Problem space dimension");
|
||||
args.AddOption(&nx, "-nx", "--nx","Number of subdomains in x direction");
|
||||
args.AddOption(&ny, "-ny", "--ny","Number of subdomains in y direction");
|
||||
args.AddOption(&nz, "-nz", "--nz","Number of subdomains in z direction");
|
||||
args.AddOption(&sol, "-sol", "--exact",
|
||||
"Exact solution flag - 0:polynomial, 1: plane wave, -1: unknown exact");
|
||||
args.AddOption(&k, "-k", "--wavelengths",
|
||||
"Number of wavelengths.");
|
||||
args.AddOption(&pml_length, "-pml_length", "--pml_length",
|
||||
"Length of the PML region in each direction");
|
||||
args.AddOption(&length, "-length", "--length",
|
||||
"length of the domain in each direction.");
|
||||
args.AddOption(&ser_ref_levels, "-sr", "--ser_ref_levels",
|
||||
"Number of Serial Refinements.");
|
||||
args.AddOption(&par_ref_levels, "-pr", "--par_ref_levels",
|
||||
"Number of Parallel Refinements.");
|
||||
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
|
||||
"--no-hermitian", "Use convention for Hermitian operators.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
// check if the inputs are correct
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
// Angular frequency
|
||||
omega = 2.0 * M_PI * k;
|
||||
|
||||
// 3. Read the mesh from the given mesh file.
|
||||
Mesh *mesh;
|
||||
|
||||
if (nd == 2)
|
||||
{
|
||||
mesh = new Mesh(1, 1, Element::QUADRILATERAL, true, length, length, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh = new Mesh(1, 1, 1, Element::HEXAHEDRON, true, length, length, length,false);
|
||||
}
|
||||
|
||||
// 3. Executing uniform h-refinement
|
||||
dim = mesh->Dimension();
|
||||
for (int i = 0; i < ser_ref_levels; i++ )
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 4. Define a parallel mesh by a partitioning of the serial mesh.
|
||||
// ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
int nprocs;
|
||||
int nprocsx;
|
||||
int nprocsy;
|
||||
int nprocsz;
|
||||
if (dim == 2)
|
||||
{
|
||||
nprocs = sqrt(num_procs);
|
||||
// nprocsx = nprocs;
|
||||
// nprocsy = nprocs;
|
||||
nprocsx = 1;
|
||||
nprocsy = num_procs;
|
||||
nprocsz = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nprocs = cbrt(num_procs);
|
||||
// nprocsx = nprocs;
|
||||
// nprocsy = nprocs;
|
||||
// nprocsz = nprocs;
|
||||
nprocsx = 1;
|
||||
if (nz != 1)
|
||||
{
|
||||
nprocsy = sqrt(num_procs);
|
||||
nprocsz = nprocsy;
|
||||
}
|
||||
else
|
||||
{
|
||||
nprocsy = num_procs;
|
||||
nprocsz = 1;
|
||||
}
|
||||
}
|
||||
// MFEM_VERIFY(nprocs*nprocs == num_procs, "Check MPI partitioning");
|
||||
// int nxyz[3] = {num_procs,1,1};
|
||||
// int nxyz[3] = {nprocs,nprocs,1};
|
||||
// int nxyz[3] = {1,num_procs,1};
|
||||
|
||||
int nxyz[3] = {nprocsx,nprocsy,nprocsz};
|
||||
// int nxyz[3] = {num_procs,1,1};
|
||||
int * part = mesh->CartesianPartitioning(nxyz);
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD,*mesh,part);
|
||||
// ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD,*mesh);
|
||||
delete [] part;
|
||||
delete mesh;
|
||||
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
|
||||
double hl = GetUniformMeshElementSize(pmesh);
|
||||
int nrlayers = 3;
|
||||
|
||||
Array2D<double> lengths(dim,2);
|
||||
lengths = hl*nrlayers;
|
||||
// lengths[0][1] = 0.0;
|
||||
// lengths[1][1] = 0.0;
|
||||
// lengths[1][0] = 0.0;
|
||||
// lengths[0][0] = 0.0;
|
||||
CartesianPML pml(pmesh,lengths);
|
||||
pml.SetOmega(omega);
|
||||
comp_bdr.SetSize(dim,2);
|
||||
comp_bdr = pml.GetCompDomainBdr();
|
||||
|
||||
// 6. Define a finite element space on the mesh.
|
||||
FiniteElementCollection *fec = new H1_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int size = fespace->GlobalTrueVSize();
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
// 6. Set up the linear form (Real and Imaginary part)
|
||||
FunctionCoefficient f_Re(f_exact_Re);
|
||||
FunctionCoefficient f_Im(f_exact_Im);
|
||||
|
||||
// 8. Setup Complex Operator convention
|
||||
ComplexOperator::Convention conv =
|
||||
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
|
||||
|
||||
// ParLinearForm *b_Re(new ParLinearForm);
|
||||
ParComplexLinearForm b(fespace, conv);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(f_Re),
|
||||
new DomainLFIntegrator(f_Im));
|
||||
b.real().Vector::operator=(0.0);
|
||||
b.imag().Vector::operator=(0.0);
|
||||
b.Assemble();
|
||||
|
||||
// 7. Set up the bilinear form (Real and Imaginary part)
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient sigma(-pow(omega, 2));
|
||||
|
||||
FunctionCoefficient ws(wavespeed);
|
||||
|
||||
PmlMatrixCoefficient c1_re(dim,pml_detJ_JT_J_inv_Re,&pml);
|
||||
PmlMatrixCoefficient c1_im(dim,pml_detJ_JT_J_inv_Im,&pml);
|
||||
|
||||
PmlCoefficient detJ_re(pml_detJ_Re,&pml);
|
||||
PmlCoefficient detJ_im(pml_detJ_Im,&pml);
|
||||
|
||||
ProductCoefficient c2_re0(sigma, detJ_re);
|
||||
ProductCoefficient c2_im0(sigma, detJ_im);
|
||||
|
||||
ProductCoefficient c2_re(c2_re0, ws);
|
||||
ProductCoefficient c2_im(c2_im0, ws);
|
||||
|
||||
ParSesquilinearForm a(fespace,conv);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(c1_re),
|
||||
new DiffusionIntegrator(c1_im));
|
||||
a.AddDomainIntegrator(new MassIntegrator(c2_re),
|
||||
new MassIntegrator(c2_im));
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
// Solution grid function
|
||||
ParComplexGridFunction p_gf(fespace); p_gf = 0.0;
|
||||
OperatorHandle Ah;
|
||||
Vector X, B;
|
||||
|
||||
a.FormLinearSystem(ess_tdof_list, p_gf, b, Ah, X, B);
|
||||
{
|
||||
StopWatch chrono;
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
ParDST S(&a,lengths,omega, &ws,nrlayers,nx,ny,nz);
|
||||
chrono.Stop();
|
||||
double t1 = chrono.RealTime();
|
||||
|
||||
chrono.Clear();
|
||||
chrono.Start();
|
||||
// X = 0.0;
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPreconditioner(S);
|
||||
gmres.SetOperator(*Ah);
|
||||
gmres.SetRelTol(1e-6);
|
||||
gmres.SetMaxIter(20);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.Mult(B, X);
|
||||
chrono.Stop();
|
||||
|
||||
double t2 = chrono.RealTime();
|
||||
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
|
||||
|
||||
cout << " myid: " << myid
|
||||
<< ", setup time: " << t1
|
||||
<< ", solution time: " << t2 << endl;
|
||||
|
||||
|
||||
a.RecoverFEMSolution(X,B,p_gf);
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
string keys;
|
||||
if (dim ==2 )
|
||||
{
|
||||
keys = "keys mrRljc\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
keys = "keys mc\n";
|
||||
}
|
||||
socketstream sol_sock_re(vishost, visport);
|
||||
sol_sock_re.precision(8);
|
||||
sol_sock_re << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pmesh << p_gf.real() << keys
|
||||
<< "window_title 'Numerical Pressure: Real Part' " << flush;
|
||||
|
||||
socketstream sol_sock_im(vishost, visport);
|
||||
sol_sock_im.precision(8);
|
||||
sol_sock_im << "parallel " << num_procs << " " << myid << "\n"
|
||||
<< "solution\n" << *pmesh << p_gf.imag() << keys
|
||||
<< "window_title 'Numerical Pressure: Imag Part' " << flush;
|
||||
}
|
||||
}
|
||||
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double f_exact_Re(const Vector &x)
|
||||
{
|
||||
|
||||
int nrsources = (dim == 2) ? 4 : 8;
|
||||
Vector x0(nrsources);
|
||||
Vector y0(nrsources);
|
||||
Vector z0(nrsources);
|
||||
x0(0) = 0.25; y0(0) = 0.25; z0(0) = 0.25;
|
||||
x0(1) = 0.75; y0(1) = 0.25; z0(1) = 0.25;
|
||||
x0(2) = 0.25; y0(2) = 0.75; z0(2) = 0.25;
|
||||
x0(3) = 0.75; y0(3) = 0.75; z0(3) = 0.25;
|
||||
if (dim == 3)
|
||||
{
|
||||
x0(4) = 0.25; y0(4) = 0.25; z0(4) = 0.75;
|
||||
x0(5) = 0.75; y0(5) = 0.25; z0(5) = 0.75;
|
||||
x0(6) = 0.25; y0(6) = 0.75; z0(6) = 0.75;
|
||||
x0(7) = 0.75; y0(7) = 0.75; z0(7) = 0.75;
|
||||
}
|
||||
|
||||
double n = 4.0*omega/M_PI;
|
||||
double coeff = 16.0*omega*omega/M_PI/M_PI/M_PI;
|
||||
|
||||
double f_re = 0.0;
|
||||
// for (int i = 0; i<1; i++)
|
||||
for (int i = 0; i<nrsources; i++)
|
||||
{
|
||||
double beta = pow(x0(i)-x(0),2) + pow(y0(i)-x(1),2);
|
||||
if (dim == 3) { beta += pow(z0(i)-x(2),2); }
|
||||
double alpha = -pow(n,2) * beta;
|
||||
f_re += coeff*exp(alpha);
|
||||
}
|
||||
|
||||
bool in_pml = false;
|
||||
for (int i = 0; i<dim; i++)
|
||||
{
|
||||
if (x(i)<=comp_bdr(i,0) || x(i)>=comp_bdr(i,1))
|
||||
{
|
||||
in_pml = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (in_pml) f_re = 0.0;
|
||||
|
||||
return f_re;
|
||||
|
||||
}
|
||||
double f_exact_Im(const Vector &x)
|
||||
{
|
||||
double f_im;
|
||||
f_im = 0.0;
|
||||
return f_im;
|
||||
}
|
||||
|
||||
double wavespeed(const Vector &x)
|
||||
{
|
||||
double ws;
|
||||
ws = 1.0;
|
||||
return ws;
|
||||
}
|
||||
|
||||
double funccoeff_re(const Vector & x)
|
||||
{
|
||||
return sin(3*M_PI*(x.Sum()));
|
||||
}
|
||||
|
||||
double funccoeff_im(const Vector & x)
|
||||
{
|
||||
return cos(10*M_PI*(x.Sum()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user