Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a21bd41e8a | ||
|
|
936fe796c2 | ||
|
|
f6904eea28 | ||
|
|
2c103d0454 | ||
|
|
de320537e8 | ||
|
|
acc58bd2b2 |
@@ -33,7 +33,6 @@ env:
|
||||
HYPRE_ARCHIVE: v2.19.0.tar.gz
|
||||
HYPRE_TOP_DIR: hypre-2.19.0
|
||||
METIS_ARCHIVE: metis-4.0.3.tar.gz
|
||||
METIS_ARCHIVE_MAC: metis-4.0.3-mac.tgz
|
||||
METIS_TOP_DIR: metis-4.0.3
|
||||
MFEM_TOP_DIR: mfem
|
||||
|
||||
@@ -53,7 +52,6 @@ jobs:
|
||||
mpi: [seq, par]
|
||||
build-system: [make, cmake]
|
||||
hypre-target: [int32]
|
||||
precision: [fp64]
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
build-system: cmake
|
||||
@@ -77,8 +75,6 @@ jobs:
|
||||
- os: ubuntu-latest
|
||||
target: dbg
|
||||
config-opts: 'CPPFLAGS+=-Og'
|
||||
- os: macos-latest
|
||||
codecov: NO
|
||||
- os: windows-latest
|
||||
codecov: NO
|
||||
- os: windows-latest
|
||||
@@ -91,7 +87,6 @@ jobs:
|
||||
mpi: par
|
||||
build-system: cmake
|
||||
hypre-target: int32
|
||||
precision: fp64
|
||||
# This option can be set to pass additional configuration options to
|
||||
# the MFEM configuration command.
|
||||
# config-opts: '-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||
@@ -101,15 +96,7 @@ jobs:
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-target: int64
|
||||
precision: fp64
|
||||
- os: ubuntu-latest
|
||||
target: opt
|
||||
codecov: NO
|
||||
mpi: par
|
||||
build-system: make
|
||||
hypre-target: int32
|
||||
precision: fp32
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -139,17 +126,6 @@ jobs:
|
||||
# Fetch the complete history for codecov to access commits ID
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Xcode version setup (MacOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
XCODE_PATH="/Applications/Xcode_15.3.app"
|
||||
echo "> sudo xcode-select -s ${XCODE_PATH}"
|
||||
sudo xcode-select -s ${XCODE_PATH}
|
||||
echo "> g++ -v"
|
||||
g++ -v
|
||||
echo "> clang++ -v"
|
||||
clang++ -v
|
||||
|
||||
# Only get MPI if defined for the job.
|
||||
# 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.
|
||||
@@ -193,27 +169,25 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-v2.2
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.4
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: ${{ matrix.hypre-target }}
|
||||
build-system: make
|
||||
precision: ${{ matrix.precision }}
|
||||
|
||||
- name: get hypre (Windows)
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.4
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
target: ${{ matrix.hypre-target }}
|
||||
build-system: cmake
|
||||
precision: ${{ matrix.precision }}
|
||||
|
||||
# Get Metis through cache, or build it.
|
||||
# Install will only run on cache miss.
|
||||
@@ -223,13 +197,13 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.2
|
||||
|
||||
- name: install metis
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
uses: mfem/github-actions/build-metis@v2.4
|
||||
with:
|
||||
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
- name: cache vcpkg (Windows)
|
||||
@@ -254,7 +228,7 @@ jobs:
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
uses: mfem/github-actions/build-mfem@v2.4
|
||||
env:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
|
||||
with:
|
||||
@@ -266,7 +240,6 @@ jobs:
|
||||
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
metis-dir: ${{ env.METIS_TOP_DIR }}
|
||||
mfem-dir: ${{ env.MFEM_TOP_DIR }}
|
||||
precision: ${{ matrix.precision }}
|
||||
config-options: ${{ matrix.config-opts }}
|
||||
library-only: ${{ matrix.target == 'dbg' && matrix.os != 'ubuntu-latest' }}
|
||||
|
||||
@@ -309,7 +282,7 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v2.5
|
||||
uses: mfem/github-actions/upload-coverage@v2.4
|
||||
with:
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
|
||||
@@ -53,11 +53,11 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.HYPRE_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.5
|
||||
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.2
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.4
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -68,18 +68,18 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.METIS_TOP_DIR }}
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
|
||||
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.2
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
uses: mfem/github-actions/build-metis@v2.4
|
||||
with:
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build-mfem
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
uses: mfem/github-actions/build-mfem@v2.4
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
path: mfem
|
||||
|
||||
- name: MFEM Build
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
uses: mfem/github-actions/build-mfem@v2.4
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
|
||||
+2
-4
@@ -57,8 +57,6 @@ examples/ex2[0-9]
|
||||
examples/ex2[0-9]p
|
||||
examples/ex3[0-9]
|
||||
examples/ex3[0-9]p
|
||||
examples/ex4[0-9]
|
||||
examples/ex4[0-9]p
|
||||
|
||||
examples/refined.mesh
|
||||
examples/displaced.mesh
|
||||
@@ -234,7 +232,7 @@ miniapps/meshing/mobius-strip.mesh
|
||||
miniapps/meshing/klein-bottle.mesh
|
||||
miniapps/meshing/toroid-*.mesh
|
||||
miniapps/meshing/twist-*.mesh
|
||||
miniapps/meshing/mesh-explorer.mesh*
|
||||
miniapps/meshing/mesh-explorer.mesh
|
||||
miniapps/meshing/partitioning.txt
|
||||
miniapps/meshing/mesh-explorer-visit*
|
||||
miniapps/meshing/mesh-explorer-paraview/
|
||||
@@ -371,7 +369,7 @@ miniapps/dpg/ParaView
|
||||
miniapps/spde/generate_random_field
|
||||
miniapps/spde/ParaView
|
||||
|
||||
miniapps/tribol/contact-patch-test
|
||||
miniapps/tribol/ContactPatchTest
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
# at Lawrence Livermore National Laboratory (LLNL). This entire pipeline is
|
||||
# LLNL-specific!
|
||||
|
||||
include:
|
||||
- project: 'lc-templates/id_tokens'
|
||||
file: 'id_tokens.yml'
|
||||
|
||||
# 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
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
include:
|
||||
- project: 'lc-templates/id_tokens'
|
||||
file: 'id_tokens.yml'
|
||||
|
||||
# We define the following GitLab pipeline variables:
|
||||
variables:
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ variables:
|
||||
- when: on_success
|
||||
|
||||
# Lassen uses a different job scheduler (spectrum lsf) that does not allow
|
||||
# pre-allocation the same way slurm does. We use the pci queue on lassen
|
||||
# to speed-up the allocation.
|
||||
# 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
|
||||
@@ -44,5 +45,5 @@ variables:
|
||||
- echo ${MFEM_DATA_DIR}
|
||||
- echo ${SPEC}
|
||||
# Next script uses 'THREADS': leaving it empty --> it uses 'make all -j'
|
||||
- lalloc 1 -W 45 -q pci --atsdisable tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
- lalloc 1 -W 45 -q pdebug --atsdisable tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
needs: [setup]
|
||||
|
||||
@@ -52,4 +52,4 @@ variables:
|
||||
- echo ${JOBID}
|
||||
- echo ${MFEM_DATA_DIR}
|
||||
- echo ${SPEC}
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 45 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
|
||||
|
||||
@@ -14,14 +14,14 @@ stages:
|
||||
- build_and_test
|
||||
- report
|
||||
|
||||
opt_mpi_cuda_gcc:
|
||||
opt_mpi_cuda_xl_16_1_1_12:
|
||||
variables:
|
||||
SPEC: "%gcc@8.3.1 +mpi +cuda cuda_arch=70"
|
||||
SPEC: "%xl@16.1.1.12 +mpi +cuda cuda_arch=70"
|
||||
extends: .build_and_test_on_lassen
|
||||
|
||||
opt_mpi_cuda_hypre_cuda_gcc:
|
||||
opt_mpi_cuda_hypre_cuda_xl:
|
||||
variables:
|
||||
SPEC: "%gcc@8.3.1 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
|
||||
SPEC: "%xl@16.1.1.12 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
|
||||
extends: .build_and_test_on_lassen
|
||||
|
||||
# Jobs report
|
||||
|
||||
@@ -32,11 +32,11 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "quartz" || "${MACHINE_NAME}" == "ruby" ]]; then
|
||||
salloc --nodes=1 --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
salloc --nodes=1 -p pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
|
||||
lalloc 1 -q pci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
lalloc 1 -q pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
else
|
||||
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
|
||||
exit 1
|
||||
|
||||
@@ -8,110 +8,77 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.7.1 (development)
|
||||
Version 4.6.1 (development)
|
||||
===========================
|
||||
|
||||
- Added an MFEM example for the eikonal equation. This new solver is based on
|
||||
the proximal Galerkin method introduced by Keith and Surowiec.
|
||||
|
||||
|
||||
Version 4.7, released on May 7, 2024
|
||||
====================================
|
||||
|
||||
- Added support for single precision (with corresponding hypre build). The MFEM
|
||||
floating point type was generalized from `double` to `real_t`. For details see
|
||||
https://github.com/orgs/mfem/discussions/4207.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Added the capability to partition (big) serial meshes in serial code, see the
|
||||
new classes MeshPartitioner and MeshPart. This capability is also exposed as a
|
||||
menu option in the mesh-explorer miniapp in miniapps/meshing.
|
||||
|
||||
- Added named attribute sets and basic supporting methods to the Mesh class as a
|
||||
convenient means of referring to sets of domain or boundary attribute numbers.
|
||||
See the new Example 39/39p and data/compass.mesh.
|
||||
|
||||
- Introduced formulas for refinement of patches in NURBS meshes. Refinement by
|
||||
arbitrary integer factors is also enabled, e.g. in the mesh-explorer miniapp.
|
||||
NURBS coarsening and knot removal are also introduced.
|
||||
|
||||
- Added support for internal boundary elements in nonconforming meshes.
|
||||
|
||||
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added a new nonlinear integrator, `HyperbolicFormIntegrator` that implements
|
||||
- Introduced support for higher order non conformal Nedelec elements on
|
||||
simplices in ParMesh.
|
||||
- Introduced support for internal boundary elements in nonconformal adapted
|
||||
meshes.
|
||||
|
||||
- Added functionality for construction of cut-surface and cut-volume
|
||||
IntegrationRules through a moment-fitting approach. The cut is specified by
|
||||
the zero level set of a Coefficient. See fem/intrules_cut.hpp and Example 38.
|
||||
|
||||
- Added a new nonlinear integrator, `HyperbolicFormIntegrator`. This implements
|
||||
both element-wise weak divergence and face-wise numerical flux for a general
|
||||
system of hyperbolic conservation laws. To use the integrator for a specific
|
||||
system of hyperbolic conservation laws. To use this integrator for a specific
|
||||
flux function, users can define a derived class of `FluxFunction`. Currently,
|
||||
advection, Burgers, shallow-water and Euler equations (see Example 18/18p) are
|
||||
advection, Burgers', shallow-water, Euler equations (see, Example 18) are
|
||||
available.
|
||||
|
||||
- Added a capability to construct cut-surface and cut-volume IntegrationRules
|
||||
through a moment-fitting approach. The cut is specified by the zero level set
|
||||
of a Coefficient. See fem/intrules_cut.hpp and the new Example 38.
|
||||
|
||||
- Introduced support for high-order nonconforming Nedelec elements on simplices.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added partial assembly and GPU support for the DG diffusion integrator.
|
||||
|
||||
- Efficient GPU-accelerated LOR assembly is now supported on surface meshes.
|
||||
|
||||
- Added functionality to automatically configure hypre's compute policy to match
|
||||
MFEM's compute policy when hypre is built with GPU support. Requires version
|
||||
hypre-2.31.0 or later.
|
||||
|
||||
GPU support
|
||||
----------------------------
|
||||
- Added support for full assembly on simplices.
|
||||
|
||||
- Added partial assembly for linear elasticity (no sum factorization for now).
|
||||
|
||||
- Added functionality for BilinearFormIntegrators to use kernels that work for
|
||||
both tensor and unstructured elements.
|
||||
|
||||
- The RAJA backend will use `seq_exec` for serial loop execution when RAJA
|
||||
v2023.06.00 and beyond is detected as `loop_exec` is deprecated.
|
||||
|
||||
- API change: The macro MFEM_HYPRE_FORALL (from hypre.hpp) which was intended
|
||||
for internal use, has been removed and replaced by the function template
|
||||
mfem::hypre_forall in general/forall.hpp.
|
||||
- Added functionality for BilinearFormIntegrators to use kernels that work for both
|
||||
tensor and unstructured elements.
|
||||
- Added partial assembly for linear elasticity. Does not use sum factorization for now.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new miniapp illustrating elastic contact based on the Tribol library,
|
||||
(https://github.com/LLNL/Tribol). See miniapps/tribol.
|
||||
- Added a new block solver in miniapp/solvers for the Darcy problem.
|
||||
The new solver is based on a Bramble-Pasciak preconditioning. User can
|
||||
use and implement their own preconditioner for the mass matrix.
|
||||
|
||||
- Added a miniapp to demonstrate low order refined (LOR) block preconditioning
|
||||
for linear elasticity on GPUs. See miniapps/solvers/lor_elast.
|
||||
- Added miniapp to demonstrate new elasticity integrator and unstructured element GPU support,
|
||||
and a block diagonal preconditioner using low order refinement. Allows comparison with
|
||||
currently existing legacy mode integrator. See miniapps/solvers/lor_elast.
|
||||
|
||||
- Added a new block solver in miniapp/solvers for the Darcy problem. The new
|
||||
solver is based on a Bramble-Pasciak preconditioning. User can use and
|
||||
implement their own preconditioner for the mass matrix.
|
||||
|
||||
- Added a small miniapp for printing the shape functions of a KnotVector. See
|
||||
miniapps/nurbs/nurbs_printfunc.cpp.
|
||||
|
||||
- Added two new example codes: 38 and 39/39p described above. Substantially
|
||||
updated Example 18/18p.
|
||||
- Added a new mortar contact patch test miniapp using the Tribol interface
|
||||
physics library (see https://github.com/LLNL/Tribol). See miniapps/tribol.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added support for single and double precision, with corresponding hypre build.
|
||||
Generalized the floating point type from `double` to `real_t`. For more
|
||||
details see https://github.com/orgs/mfem/discussions/4207.
|
||||
|
||||
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
|
||||
|
||||
- Updated the Doxygen documentation style, which now requires Doxygen version
|
||||
1.9.8 or later. See the doc/ directory.
|
||||
|
||||
- Improved thread safety for global variables in the library, e.g. for IntRules,
|
||||
RefinedIntRules, GlobGeometryRefiner, and FiniteElement::dof2quad_array.
|
||||
- Improved thread safety for global variables in the library, for example
|
||||
IntegrationRules IntRules, RefinedIntRules, GeometryRefiner
|
||||
GlobGeometryRefiner, and FiniteElement::dof2quad_array.
|
||||
|
||||
- PETSc integration now generally requires PETSc version 3.21 or later, though
|
||||
depending on the functionality older versions may still work.
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
- Added GSLIB-based gather-scatter operator.
|
||||
- RAJA backend will use seq_exec for serial loop execution when RAJA
|
||||
v2023.06.00 and beyond is detected as loop_exec is deprecated.
|
||||
|
||||
- Adding named attribute sets and basic supporting methods to the Mesh class as
|
||||
a convenient means of referring to sets of domain or boundary attribute
|
||||
numbers. Also adding related serial and parallel examples which illustrate.
|
||||
|
||||
Version 4.6, released on September 27, 2023
|
||||
===========================================
|
||||
@@ -132,6 +99,7 @@ Meshing improvements
|
||||
* The edge to knot map for NURBS meshes can be determined automatically. It is
|
||||
no longer needed to specify this in the NURBS mesh.
|
||||
* Added curve interpolation method for NURBS.
|
||||
* Added new small miniapp for printing of shape functions of a KnotVector
|
||||
* See miniapps/nurbs for example meshes and miniapps.
|
||||
|
||||
Discretization improvements
|
||||
@@ -178,6 +146,8 @@ Linear and nonlinear solvers
|
||||
|
||||
- Added HIP support to the PETSc and SUNDIALS interfaces.
|
||||
|
||||
- Efficient GPU-accelerated LOR assembly now supports surface meshes.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new H(div) solver miniapp demonstrating the use of a matrix-free
|
||||
|
||||
+1
-1
@@ -58,7 +58,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.7.1)
|
||||
set(${PROJECT_NAME}_VERSION 4.6.1)
|
||||
|
||||
# Prohibit in-source build
|
||||
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
|
||||
|
||||
@@ -75,8 +75,6 @@ and miniapps. See https://glvis.org and https://mfem.org/building.
|
||||
|
||||
Quick start with GNU make
|
||||
=========================
|
||||
See also: https://mfem.org/building
|
||||
|
||||
Serial build:
|
||||
make serial -j 4
|
||||
|
||||
@@ -85,7 +83,6 @@ Parallel build:
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
make parallel -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
CUDA build:
|
||||
make cuda -j 4
|
||||
@@ -119,7 +116,6 @@ Parallel build:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
|
||||
make -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
CUDA build:
|
||||
(this build requires CMake 3.8 or newer)
|
||||
@@ -616,13 +612,9 @@ The specific libraries and their options are:
|
||||
HYPRE >= 2.20.0 (HYPRE built with '--enable-mixedint')
|
||||
HYPRE >= 2.22.1 (HYPRE built with CUDA)
|
||||
HYPRE >= 2.23.0 (HYPRE built with HIP)
|
||||
HYPRE >= 2.31.0 (runtime selectable HYPRE execution on CPU/GPU)
|
||||
|
||||
- METIS, used when MFEM_USE_METIS = YES. If using METIS 5, set
|
||||
MFEM_USE_METIS_5 = YES (default is to use METIS 4). For building instructions,
|
||||
see the following:
|
||||
- METIS 4.0.3: https://mfem.org/building/#parallel-mpi-version-of-mfem
|
||||
- METIS 5.1.0: https://mfem.org/building/#parallel-build-using-metis-5
|
||||
MFEM_USE_METIS_5 = YES (default is to use METIS 4).
|
||||
URL: https://github.com/mfem/tpls (MFEM mirror, see above)
|
||||
Options: METIS_OPT, METIS_LIB.
|
||||
Versions: METIS 4.0.3 or 5.1.0.
|
||||
|
||||
@@ -16,22 +16,12 @@
|
||||
# - MUMPS_VERSION
|
||||
|
||||
include(MfemCmakeUtilities)
|
||||
|
||||
# Toggle which precision of MUMPS to use depending on the precision of MFEM.
|
||||
if (MFEM_USE_DOUBLE)
|
||||
set(_mumps_header dmumps_c.h)
|
||||
set(_mumps_lib dmumps)
|
||||
elseif(MFEM_USE_SINGLE)
|
||||
set(_mumps_header smumps_c.h)
|
||||
set(_mumps_lib smumps)
|
||||
endif()
|
||||
|
||||
mfem_find_package(MUMPS MUMPS MUMPS_DIR
|
||||
"include" ${_mumps_header} "lib" ${_mumps_lib}
|
||||
"include" dmumps_c.h "lib" dmumps
|
||||
"Paths to headers required by MUMPS."
|
||||
"Libraries required by MUMPS."
|
||||
ADD_COMPONENT mumps_common "include" ${_mumps_header} "lib" mumps_common
|
||||
ADD_COMPONENT pord "include" ${_mumps_header} "lib" pord)
|
||||
ADD_COMPONENT mumps_common "include" dmumps_c.h "lib" mumps_common
|
||||
ADD_COMPONENT pord "include" dmumps_c.h "lib" pord)
|
||||
|
||||
if (MUMPS_FOUND AND (NOT MUMPS_VERSION))
|
||||
try_run(MUMPS_VERSION_RUN_RESULT MUMPS_VERSION_COMPILE_RESULT
|
||||
|
||||
@@ -120,15 +120,6 @@ constexpr real_t operator""_r(unsigned long long v)
|
||||
|
||||
// Check dependencies:
|
||||
|
||||
// Define MFEM_MPI_REAL_T to be the appropriate MPI real type
|
||||
#ifdef MFEM_USE_MPI
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
#define MFEM_MPI_REAL_T MPI_FLOAT
|
||||
#elif defined MFEM_USE_DOUBLE
|
||||
#define MFEM_MPI_REAL_T MPI_DOUBLE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Options that require MPI
|
||||
#ifndef MFEM_USE_MPI
|
||||
#ifdef MFEM_USE_SUPERLU
|
||||
|
||||
+3
-15
@@ -170,18 +170,6 @@ MFEM_USE_PARELAG = NO
|
||||
MFEM_USE_TRIBOL = NO
|
||||
MFEM_USE_ENZYME = NO
|
||||
|
||||
# Process MFEM_PRECISION -> MFEM_USE_SINGLE, MFEM_USE_DOUBLE
|
||||
ifneq ($(filter double Double DOUBLE,$(MFEM_PRECISION)),)
|
||||
MFEM_USE_DOUBLE = YES
|
||||
MFEM_USE_SINGLE = NO
|
||||
else ifneq ($(filter single Single SINGLE,$(MFEM_PRECISION)),)
|
||||
MFEM_USE_DOUBLE = NO
|
||||
MFEM_USE_SINGLE = YES
|
||||
else ifeq ($(MAKECMDGOALS),config)
|
||||
$(error Invalid floating-point precision: \
|
||||
MFEM_PRECISION = $(MFEM_PRECISION))
|
||||
endif
|
||||
|
||||
# MPI library compile and link flags
|
||||
# These settings are used only when building MFEM with MPI + HIP
|
||||
ifeq ($(MFEM_USE_MPI)$(MFEM_USE_HIP),YESYES)
|
||||
@@ -331,13 +319,13 @@ MPI_FORTRAN_LIB = -lmpifort
|
||||
# MUMPS library configuration
|
||||
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.5.0
|
||||
MUMPS_OPT = -I$(MUMPS_DIR)/include
|
||||
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib
|
||||
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib \
|
||||
-lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
|
||||
ifeq ($(MFEM_USE_SINGLE),YES)
|
||||
MUMPS_LIB += -lsmumps
|
||||
else
|
||||
MUMPS_LIB += -ldmumps
|
||||
endif
|
||||
MUMPS_LIB += -lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
|
||||
|
||||
# STRUMPACK library configuration
|
||||
STRUMPACK_DIR = @MFEM_DIR@/../STRUMPACK-build
|
||||
@@ -388,7 +376,7 @@ GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LINK_LIB_DIR) -L$(GINKGO_LINK_LIB_DIR)\
|
||||
# AmgX library configuration
|
||||
AMGX_DIR = @MFEM_DIR@/../amgx
|
||||
AMGX_OPT = -I$(AMGX_DIR)/include
|
||||
AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
|
||||
AMGX_LIB = -lcusparse -lcusolver -lcublas -lnvToolsExt -L$(AMGX_DIR)/lib -lamgx
|
||||
|
||||
# GnuTLS library configuration
|
||||
GNUTLS_OPT =
|
||||
|
||||
+1
-1
@@ -110,4 +110,4 @@ config-mk:
|
||||
|
||||
clean:
|
||||
rm -f $(CONFIG_HPP) $(CONFIG_MK) sample-runs-build.log
|
||||
rm -f $(GHV) $(GHV).out $(GMV) $(GMV).out *.dSYM
|
||||
rm -f $(GHV) $(GHV).out $(GMV) $(GMV).out
|
||||
|
||||
@@ -92,5 +92,4 @@ vertices
|
||||
-0.70710678 -0.70710678
|
||||
0 -1
|
||||
0.70710678 -0.70710678
|
||||
|
||||
mfem_mesh_end
|
||||
|
||||
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v4.7.1
|
||||
PROJECT_NUMBER = v4.6.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
|
||||
|
||||
@@ -110,13 +110,9 @@ namespace mfem {
|
||||
* - <a class="el" href="ex35p_8cpp_source.html">Example 35p</a>: parallel multi-domain damped harmonic oscillators
|
||||
* - <a class="el" href="ex36_8cpp_source.html">Example 36</a>: Proximal Galerkin FEM for the obstacle problem
|
||||
* - <a class="el" href="ex36p_8cpp_source.html">Example 36p</a>: parallel Proximal Galerkin FEM for the obstacle problem
|
||||
* - <a class="el" href="ex37_8cpp_source.html">Example 37</a>: topology optimization
|
||||
* - <a class="el" href="ex37_8cpp_source.html">Example 37</a>: Topology optimization
|
||||
* - <a class="el" href="ex37p_8cpp_source.html">Example 37p</a>: parallel topology optimization
|
||||
* - <a class="el" href="ex38_8cpp_source.html">Example 38</a>: cut-surface and cut-volume integration
|
||||
* - <a class="el" href="ex39_8cpp_source.html">Example 39</a>: named mesh attributes
|
||||
* - <a class="el" href="ex39p_8cpp_source.html">Example 39p</a>: parallel named mesh attributes
|
||||
* - <a class="el" href="ex40_8cpp_source.html">Example 40</a>: eikonal equation
|
||||
* - <a class="el" href="ex40p_8cpp_source.html">Example 40p</a>: parallel eikonal equation
|
||||
*
|
||||
* <H4>AmgX Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -218,8 +214,6 @@ namespace mfem {
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
|
||||
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
|
||||
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
|
||||
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
|
||||
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
|
||||
* - <a class="el" href="lor__elast_8cpp_source.html">LOR Elasticity</a>: solve linear elasticity with LOR preconditioning on GPUs
|
||||
|
||||
@@ -46,7 +46,7 @@ class DoxygenAwesomeDarkModeToggle extends HTMLElement {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
})
|
||||
// Update the color scheme when the tab is made visible again.
|
||||
// It is possible that the appearance was changed in another tab
|
||||
// It is possible that the appearance was changed in another tab
|
||||
// while this tab was in the background.
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
@@ -97,7 +97,7 @@ class DoxygenAwesomeDarkModeToggle extends HTMLElement {
|
||||
* @returns `true` for dark-mode, `false` for light-mode user preference
|
||||
*/
|
||||
static get userPreference() {
|
||||
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
|
||||
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
|
||||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
|
||||
}
|
||||
|
||||
|
||||
+3
-10
@@ -45,7 +45,6 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex37.cpp
|
||||
ex38.cpp
|
||||
ex39.cpp
|
||||
ex40.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -88,7 +87,6 @@ if (MFEM_USE_MPI)
|
||||
ex36p.cpp
|
||||
ex37p.cpp
|
||||
ex39p.cpp
|
||||
ex40p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -148,10 +146,10 @@ if (MFEM_ENABLE_TESTING)
|
||||
# Add CUDA/HIP tests.
|
||||
set(DEVICE_EXAMPLES
|
||||
# serial examples with device support:
|
||||
ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
|
||||
ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26 ex34
|
||||
# parallel examples with device support:
|
||||
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p ex22p ex24p ex25p
|
||||
ex26p ex34p ex35p)
|
||||
ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p ex24p ex25p ex26p
|
||||
ex34p ex35p)
|
||||
set(MFEM_TEST_DEVICE)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_TEST_DEVICE "cuda")
|
||||
@@ -161,11 +159,6 @@ if (MFEM_ENABLE_TESTING)
|
||||
if (MFEM_TEST_DEVICE)
|
||||
foreach(TEST_NAME ${DEVICE_EXAMPLES})
|
||||
set(THIS_TEST_OPTIONS "-no-vis" "-d" "${MFEM_TEST_DEVICE}")
|
||||
if (${TEST_NAME} MATCHES "ex14p")
|
||||
list(APPEND THIS_TEST_OPTIONS "-rs" "2" "-rp" "0" "-pa")
|
||||
elseif (${TEST_NAME} MATCHES "ex14")
|
||||
list(APPEND THIS_TEST_OPTIONS "-r" "2" "-pa")
|
||||
endif()
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
add_test(NAME ${TEST_NAME}_${MFEM_TEST_DEVICE}_ser
|
||||
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
|
||||
|
||||
+4
-1
@@ -646,7 +646,10 @@ real_t HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
|
||||
|
||||
real_t HyperelasticOperator::KineticEnergy(const ParGridFunction &v) const
|
||||
{
|
||||
real_t energy = 0.5*M.ParInnerProduct(v, v);
|
||||
real_t loc_energy = 0.5*M.InnerProduct(v, v);
|
||||
real_t energy;
|
||||
MPI_Allreduce(&loc_energy, &energy, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, fespace.GetComm());
|
||||
return energy;
|
||||
}
|
||||
|
||||
|
||||
+55
-79
@@ -18,12 +18,6 @@
|
||||
// ex14 -m ../data/amr-quad.mesh -r 3
|
||||
// ex14 -m ../data/amr-hex.mesh
|
||||
// ex14 -m ../data/fichera-amr.mesh
|
||||
// ex14 -pa -r 1 -o 3
|
||||
// ex14 -pa -r 1 -o 3 -m ../data/fichera.mesh
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex14 -pa -r 2 -d cuda -o 3
|
||||
// ex14 -pa -r 2 -d cuda -o 3 -m ../data/fichera.mesh
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// discontinuous Galerkin (DG) finite element discretization of
|
||||
@@ -52,9 +46,7 @@ int main(int argc, char *argv[])
|
||||
real_t sigma = -1.0;
|
||||
real_t kappa = -1.0;
|
||||
real_t eta = 0.0;
|
||||
bool pa = false;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -70,13 +62,9 @@ int main(int argc, char *argv[])
|
||||
"One of the three DG penalty parameters, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
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,129 +77,117 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
|
||||
// NURBS meshes are projected to second order meshes.
|
||||
Mesh mesh(mesh_file);
|
||||
const int dim = mesh.Dimension();
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 3. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. By default, or if ref_levels < 0,
|
||||
// we choose it to be the largest number that gives a final mesh with no
|
||||
// more than 50,000 elements.
|
||||
{
|
||||
if (ref_levels < 0)
|
||||
{
|
||||
ref_levels = (int)floor(log(50000./mesh.GetNE())/log(2.)/dim);
|
||||
ref_levels = (int)floor(log(50000./mesh->GetNE())/log(2.)/dim);
|
||||
}
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
if (mesh.NURBSext)
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh.SetCurvature(max(order, 1));
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use discontinuous
|
||||
// 4. Define a finite element space on the mesh. Here we use discontinuous
|
||||
// finite elements of the specified order >= 0.
|
||||
const auto bt = pa ? BasisType::GaussLobatto : BasisType::GaussLegendre;
|
||||
DG_FECollection fec(order, dim, bt);
|
||||
FiniteElementSpace fespace(&mesh, &fec);
|
||||
cout << "Number of unknowns: " << fespace.GetVSize() << endl;
|
||||
FiniteElementCollection *fec = new DG_FECollection(order, dim);
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
cout << "Number of unknowns: " << fespace->GetVSize() << endl;
|
||||
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// 5. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system.
|
||||
LinearForm b(&fespace);
|
||||
LinearForm *b = new LinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.AddBdrFaceIntegrator(
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->AddBdrFaceIntegrator(
|
||||
new DGDirichletLFIntegrator(zero, one, sigma, kappa));
|
||||
b.Assemble();
|
||||
b->Assemble();
|
||||
|
||||
// 7. Define the solution vector x as a finite element grid function
|
||||
// 6. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero.
|
||||
GridFunction x(&fespace);
|
||||
GridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 8. Set up the bilinear form a(.,.) on the finite element space
|
||||
// 7. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
// domain integrator and the interior and boundary DG face integrators.
|
||||
// Note that boundary conditions are imposed weakly in the form, so there
|
||||
// is no need for dof elimination. After assembly and finalizing we
|
||||
// extract the corresponding sparse matrix A.
|
||||
BilinearForm a(&fespace);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
MFEM_VERIFY(!pa, "BR2 not yet compatible with partial assembly.");
|
||||
a.AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a.AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
}
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
const SparseMatrix &A = a->SpMat();
|
||||
|
||||
// 9. Define a simple symmetric Gauss-Seidel preconditioner and use it to
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// 8. Define a simple symmetric Gauss-Seidel preconditioner and use it to
|
||||
// solve the system Ax=b with PCG in the symmetric case, and GMRES in the
|
||||
// non-symmetric one. (Note that tolerances are squared: 1e-12 corresponds
|
||||
// to a relative tolerance of 1e-6).
|
||||
//
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
if (pa)
|
||||
// non-symmetric one.
|
||||
GSSmoother M(A);
|
||||
if (sigma == -1.0)
|
||||
{
|
||||
MFEM_VERIFY(sigma == -1.0,
|
||||
"The case of PA with sigma != -1 is not yet supported.");
|
||||
CG(a, b, x, 1, 500, 1e-12, 0.0);
|
||||
PCG(A, M, *b, x, 1, 500, 1e-12, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const SparseMatrix &A = a.SpMat();
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
GSSmoother M(A);
|
||||
if (sigma == -1.0)
|
||||
{
|
||||
PCG(A, M, b, x, 1, 500, 1e-12, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
GMRES(A, M, b, x, 1, 500, 10, 1e-12, 0.0);
|
||||
}
|
||||
#else
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(A);
|
||||
umf_solver.Mult(b, x);
|
||||
#endif
|
||||
GMRES(A, M, *b, x, 1, 500, 10, 1e-12, 0.0);
|
||||
}
|
||||
#else
|
||||
// 8. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(A);
|
||||
umf_solver.Mult(*b, x);
|
||||
#endif
|
||||
|
||||
// 10. Save the refined mesh and the solution. This output can be viewed
|
||||
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
|
||||
// 9. Save the refined mesh and the solution. This output can be viewed later
|
||||
// using GLVis: "glvis -m refined.mesh -g sol.gf".
|
||||
ofstream mesh_ofs("refined.mesh");
|
||||
mesh_ofs.precision(8);
|
||||
mesh.Print(mesh_ofs);
|
||||
mesh->Print(mesh_ofs);
|
||||
ofstream sol_ofs("sol.gf");
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
|
||||
// 11. Send the solution by socket to a GLVis server.
|
||||
// 10. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
}
|
||||
|
||||
// 11. Free the used memory.
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+78
-86
@@ -17,12 +17,6 @@
|
||||
// mpirun -np 4 ex14p -m ../data/inline-segment.mesh -rs 5
|
||||
// mpirun -np 4 ex14p -m ../data/amr-quad.mesh -rs 3
|
||||
// mpirun -np 4 ex14p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex14p -pa -rs 1 -rp 0 -o 3
|
||||
// mpirun -np 4 ex14p -pa -rs 1 -rp 0 -m ../data/fichera.mesh -o 3
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex14p -pa -rs 2 -rp 0 -d cuda -o 3
|
||||
// mpirun -np 4 ex14p -pa -rs 2 -rp 0 -d cuda -m ../data/fichera.mesh -o 3
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// discontinuous Galerkin (DG) finite element discretization of
|
||||
@@ -44,14 +38,11 @@ using namespace mfem;
|
||||
|
||||
class CustomSolverMonitor : public IterativeSolverMonitor
|
||||
{
|
||||
private:
|
||||
const ParMesh &pmesh;
|
||||
ParGridFunction &pgf;
|
||||
public:
|
||||
CustomSolverMonitor(const ParMesh &pmesh_,
|
||||
ParGridFunction &pgf_) :
|
||||
pmesh(pmesh_),
|
||||
pgf(pgf_) {}
|
||||
CustomSolverMonitor(const ParMesh *m,
|
||||
ParGridFunction *f) :
|
||||
pmesh(m),
|
||||
pgf(f) {}
|
||||
|
||||
void MonitorSolution(int i, real_t norm, const Vector &x, bool final)
|
||||
{
|
||||
@@ -59,24 +50,30 @@ public:
|
||||
int visport = 19916;
|
||||
int num_procs, myid;
|
||||
|
||||
MPI_Comm_size(pmesh.GetComm(), &num_procs);
|
||||
MPI_Comm_rank(pmesh.GetComm(), &myid);
|
||||
MPI_Comm_size(pmesh->GetComm(),&num_procs);
|
||||
MPI_Comm_rank(pmesh->GetComm(),&myid);
|
||||
|
||||
pgf.SetFromTrueDofs(x);
|
||||
pgf->SetFromTrueDofs(x);
|
||||
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << pgf
|
||||
sol_sock << "solution\n" << *pmesh << *pgf
|
||||
<< "window_title 'Iteration no " << i << "'"
|
||||
<< "keys rRjlc\n" << flush;
|
||||
}
|
||||
|
||||
private:
|
||||
const ParMesh *pmesh;
|
||||
ParGridFunction *pgf;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init(argc, argv);
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
@@ -87,9 +84,7 @@ int main(int argc, char *argv[])
|
||||
real_t sigma = -1.0;
|
||||
real_t kappa = -1.0;
|
||||
real_t eta = 0.0;
|
||||
bool pa = false;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -108,17 +103,13 @@ int main(int argc, char *argv[])
|
||||
"One of the three DG penalty parameters, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
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())
|
||||
{
|
||||
if (Mpi::Root())
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
@@ -128,19 +119,16 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
if (Mpi::Root())
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
Device device(device_config);
|
||||
if (Mpi::Root()) { device.Print(); }
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
|
||||
// with the same code. NURBS meshes are projected to second order meshes.
|
||||
Mesh mesh(mesh_file);
|
||||
int dim = mesh.Dimension();
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ser_ref_levels' of uniform refinement. By default,
|
||||
@@ -149,54 +137,53 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
if (ser_ref_levels < 0)
|
||||
{
|
||||
ser_ref_levels = (int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
ser_ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
|
||||
}
|
||||
for (int l = 0; l < ser_ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
if (mesh.NURBSext)
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh.SetCurvature(max(order, 1));
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
}
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use discontinuous finite elements of the specified order >= 0.
|
||||
const auto bt = pa ? BasisType::GaussLobatto : BasisType::GaussLegendre;
|
||||
DG_FECollection fec(order, dim, bt);
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
if (Mpi::Root())
|
||||
FiniteElementCollection *fec = new DG_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_BigInt size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 7. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system.
|
||||
ParLinearForm b(&fespace);
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.AddBdrFaceIntegrator(
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->AddBdrFaceIntegrator(
|
||||
new DGDirichletLFIntegrator(zero, one, sigma, kappa));
|
||||
b.Assemble();
|
||||
b->Assemble();
|
||||
|
||||
// 8. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero.
|
||||
ParGridFunction x(&fespace);
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 9. Set up the bilinear form a(.,.) on the finite element space
|
||||
@@ -205,51 +192,42 @@ int main(int argc, char *argv[])
|
||||
// Note that boundary conditions are imposed weakly in the form, so there
|
||||
// is no need for dof elimination. After serial and parallel assembly we
|
||||
// extract the corresponding parallel matrix A.
|
||||
ParBilinearForm a(&fespace);
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
MFEM_VERIFY(!pa, "BR2 not yet compatible with partial assembly.");
|
||||
a.AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a.AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
|
||||
}
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.Assemble();
|
||||
a.Finalize();
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
|
||||
// 10. Define the parallel (hypre) matrix and vectors representing a(.,.),
|
||||
// b(.) and the finite element approximation.
|
||||
OperatorHandle A;
|
||||
HypreParMatrix *A = a->ParallelAssemble();
|
||||
HypreParVector *B = b->ParallelAssemble();
|
||||
HypreParVector *X = x.ParallelProject();
|
||||
|
||||
std::unique_ptr<HypreBoomerAMG> amg;
|
||||
if (pa)
|
||||
{
|
||||
A.Reset(&a, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
A.SetType(Operator::Hypre_ParCSR);
|
||||
a.ParallelAssemble(A);
|
||||
amg.reset(new HypreBoomerAMG(*A.As<HypreParMatrix>()));
|
||||
}
|
||||
delete a;
|
||||
delete b;
|
||||
|
||||
// 11. Depending on the symmetry of A, define and apply a parallel PCG or
|
||||
// GMRES solver for AX=B using the BoomerAMG preconditioner from hypre.
|
||||
HypreSolver *amg = new HypreBoomerAMG(*A);
|
||||
if (sigma == -1.0)
|
||||
{
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(500);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
if (amg) { cg.SetPreconditioner(*amg); }
|
||||
cg.Mult(b, x);
|
||||
HyprePCG pcg(*A);
|
||||
pcg.SetTol(1e-12);
|
||||
pcg.SetMaxIter(500);
|
||||
pcg.SetPrintLevel(2);
|
||||
pcg.SetPreconditioner(*amg);
|
||||
pcg.Mult(*B, *X);
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomSolverMonitor monitor(pmesh, x);
|
||||
CustomSolverMonitor monitor(pmesh, &x);
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetRelTol(1e-12);
|
||||
@@ -257,37 +235,51 @@ int main(int argc, char *argv[])
|
||||
gmres.SetKDim(10);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetOperator(*A);
|
||||
if (amg) { gmres.SetPreconditioner(*amg); }
|
||||
gmres.SetPreconditioner(*amg);
|
||||
gmres.SetMonitor(monitor);
|
||||
gmres.Mult(b, x);
|
||||
gmres.Mult(*B, *X);
|
||||
}
|
||||
delete amg;
|
||||
|
||||
// 12. Save the refined mesh and the solution in parallel. This output can
|
||||
// 12. Extract the parallel grid function corresponding to the finite element
|
||||
// approximation X. This is the local solution on each processor.
|
||||
x = *X;
|
||||
|
||||
// 13. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << Mpi::WorldRank();
|
||||
sol_name << "sol." << setfill('0') << setw(6) << Mpi::WorldRank();
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh.Print(mesh_ofs);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 13. Send the solution by socket to a GLVis server.
|
||||
// 14. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << Mpi::WorldSize() << " " << Mpi::WorldRank() << "\n";
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << x << flush;
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
delete X;
|
||||
delete B;
|
||||
delete A;
|
||||
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+4
-4
@@ -39,8 +39,8 @@ private:
|
||||
// Base Nonlinear Form
|
||||
std::unique_ptr<NonlinearForm> nonlinearForm;
|
||||
// element-wise inverse mass matrix
|
||||
std::vector<DenseMatrix> invmass; // local scalar inverse mass
|
||||
std::vector<DenseMatrix> weakdiv; // local weak divergence (trial space ByDim)
|
||||
std::vector<DenseMatrix> invmass; // local scalar inverse mass.
|
||||
std::vector<DenseMatrix> weakdiv; // local weakdivergence. Trial space is ByDim.
|
||||
// global maximum characteristic speed. Updated by form integrators
|
||||
mutable real_t max_char_speed;
|
||||
// auxiliary variable used in Mult
|
||||
@@ -169,9 +169,9 @@ void DGHyperbolicConservationLaws::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// 0. Reset wavespeed computation before operator application.
|
||||
formIntegrator->ResetMaxCharSpeed();
|
||||
// 1. Apply Nonlinear form to obtain an auxiliary result
|
||||
// 1. Apply Nonlinear form to obtain an axiliary result
|
||||
// z = - <F̂(u_h,n), [[v]]>_e
|
||||
// If weak-divergence is not preassembled, we also have weak-divergence
|
||||
// If weak-divergencee is not preassembled, we also have weak-divergence
|
||||
// z = - <F̂(u_h,n), [[v]]>_e + (F(u_h), ∇v)
|
||||
nonlinearForm->Mult(x, z);
|
||||
if (!weakdiv.empty()) // if weak divergence is pre-assembled
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
// MFEM Example 40
|
||||
//
|
||||
// Compile with: make ex40
|
||||
//
|
||||
// Sample runs: ex40 -step 10 -gr 2.0
|
||||
// ex40 -step 10 -gr 2.0 -o 3 -r 1
|
||||
// ex40 -step 10 -gr 2.0 -r 4 -m ../data/l-shape.mesh
|
||||
// ex40 -step 10 -gr 2.0 -r 2 -m ../data/fichera.mesh
|
||||
//
|
||||
// Description: This example code demonstrates how to use MFEM to solve the
|
||||
// eikonal equation,
|
||||
//
|
||||
// |∇𝑢| = 1 in Ω, 𝑢 = g on ∂Ω.
|
||||
//
|
||||
// The solution of this problem coincides with the unique optimum of
|
||||
// the nonlinear program
|
||||
//
|
||||
// maximize ∫_Ω 𝑢 d𝑥 subject to |∇𝑢| ≤ 1, 𝑢 = g on Ω, (⋆)
|
||||
//
|
||||
// which is the foundation for method implemented below.
|
||||
//
|
||||
// Following the proximal Galerkin methodology [1] (see also Example
|
||||
// 36), we construct a Legendre function for the unit ball
|
||||
// 𝐵₁ := {𝑥 ∈ Rⁿ | |𝑥| < 1}. Our choice is the Hellinger entropy,
|
||||
//
|
||||
// h(𝑥) = −( 1 − |𝑥|² )^{1/2},
|
||||
//
|
||||
// although other choices are possible, each leading to a slightly
|
||||
// different algorithm. We then adaptively regularize the optimization
|
||||
// problem (⋆) with the Bregman divergence of the Hellinger entropy,
|
||||
//
|
||||
// maximize ∫_Ω 𝑢 d𝑥 - αₖ⁻¹ Dₕ(∇𝑢,∇𝑢ₖ₋₁) subject to 𝑢 = g on Ω.
|
||||
//
|
||||
// This results in a sequence of functions ( 𝜓ₖ , 𝑢ₖ ),
|
||||
//
|
||||
// 𝑢ₖ → 𝑢, 𝜓ₖ/|𝜓ₖ| → ∇𝑢 as k → \infty,
|
||||
//
|
||||
// defined by the nonlinear saddle-point problems
|
||||
//
|
||||
// Find 𝜓ₖ ∈ H(div,Ω) and 𝑢ₖ ∈ L²(Ω) such that
|
||||
// ( Zₖ(𝜓ₖ) , τ ) + ( 𝑢ₖ , ∇⋅τ ) = ⟨ g , τ⋅n ⟩ ∀ τ ∈ H(div,Ω)
|
||||
// ( ∇⋅𝜓ₖ , v ) = ( ∇⋅𝜓ₖ₋₁ - 1 , v ) ∀ v ∈ L²(Ω)
|
||||
//
|
||||
// where Zₖ(𝜓) := ∇h⁻¹(αₖ 𝜓) = 𝜓 / ( αₖ⁻² + |𝜓|² )^{1/2} and step size
|
||||
// αₖ > 0. These saddle-point problems are solved using a damped Newton's
|
||||
// method. This example assumes that g = 0 and allows the step size to
|
||||
// grow geometrically, αₖ = α₀rᵏ, where r ≥ 1 is the growth rate.
|
||||
//
|
||||
// [1] Keith, B. and Surowiec, T. (2023) Proximal Galerkin: A structure-
|
||||
// preserving finite element method for pointwise bound constraints.
|
||||
// arXiv:2307.12444 [math.NA]
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ZCoefficient : public VectorCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
ZCoefficient(int vdim, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
void SetAlpha(real_t alpha_) { alpha = alpha_; }
|
||||
};
|
||||
|
||||
class DZCoefficient : public MatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
DZCoefficient(int height, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: MatrixCoefficient(height), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
void SetAlpha(real_t alpha_) { alpha = alpha_; }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int max_it = 5;
|
||||
int ref_levels = 3;
|
||||
real_t alpha = 1.0;
|
||||
real_t growth_rate = 1.0;
|
||||
real_t newton_scaling = 0.9;
|
||||
real_t tichonov = 1e-1;
|
||||
real_t tol = 1e-4;
|
||||
bool visualization = true;
|
||||
|
||||
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(&ref_levels, "-r", "--refs",
|
||||
"Number of h-refinements.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
"Maximum number of iterations");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"Stopping criteria based on the difference between"
|
||||
"successive solution updates");
|
||||
args.AddOption(&alpha, "-step", "--step",
|
||||
"Initial size alpha");
|
||||
args.AddOption(&growth_rate, "-gr", "--growth-rate",
|
||||
"Growth rate of the step size alpha");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
|
||||
MFEM_ASSERT(mesh.bdr_attributes.Size(),
|
||||
"This example does not currently support meshes"
|
||||
" without boundary attributes."
|
||||
)
|
||||
|
||||
// 3. Postprocess the mesh.
|
||||
// 3A. Refine the mesh to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// 3B. Interpolate the geometry after refinement to control geometry error.
|
||||
// NOTE: Minimum second-order interpolation is used to improve the accuracy.
|
||||
int curvature_order = max(order,2);
|
||||
mesh.SetCurvature(curvature_order);
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
RT_FECollection RTfec(order, dim);
|
||||
FiniteElementSpace RTfes(&mesh, &RTfec);
|
||||
|
||||
L2_FECollection L2fec(order, dim);
|
||||
FiniteElementSpace L2fes(&mesh, &L2fec);
|
||||
|
||||
cout << "Number of H(div) dofs: "
|
||||
<< RTfes.GetTrueVSize() << endl;
|
||||
cout << "Number of L² dofs: "
|
||||
<< L2fes.GetTrueVSize() << endl;
|
||||
|
||||
// 5. Define the offsets for the block matrices
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = RTfes.GetVSize();
|
||||
offsets[2] = L2fes.GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
BlockVector x(offsets), rhs(offsets);
|
||||
x = 0.0; rhs = 0.0;
|
||||
|
||||
// 6. Define the solution vectors as a finite element grid functions
|
||||
// corresponding to the fespaces.
|
||||
GridFunction u_gf, delta_psi_gf;
|
||||
delta_psi_gf.MakeRef(&RTfes,x,offsets[0]);
|
||||
u_gf.MakeRef(&L2fes,x,offsets[1]);
|
||||
|
||||
GridFunction psi_old_gf(&RTfes);
|
||||
GridFunction psi_gf(&RTfes);
|
||||
GridFunction u_old_gf(&L2fes);
|
||||
|
||||
// 7. Define initial guesses for the solution variables.
|
||||
delta_psi_gf = 0.0;
|
||||
psi_gf = 0.0;
|
||||
u_gf = 0.0;
|
||||
psi_old_gf = psi_gf;
|
||||
u_old_gf = u_gf;
|
||||
|
||||
// 8. Prepare for glvis output.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost,visport);
|
||||
sol_sock.precision(8);
|
||||
}
|
||||
|
||||
// 9. Coefficients to be used later.
|
||||
ConstantCoefficient neg_one(-1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
ConstantCoefficient tichonov_cf(tichonov);
|
||||
ConstantCoefficient neg_tichonov_cf(-1.0*tichonov);
|
||||
ZCoefficient Z(sdim, psi_gf, alpha);
|
||||
DZCoefficient DZ(sdim, psi_gf, alpha);
|
||||
ScalarVectorProductCoefficient neg_Z(-1.0, Z);
|
||||
DivergenceGridFunctionCoefficient div_psi_cf(&psi_gf);
|
||||
DivergenceGridFunctionCoefficient div_psi_old_cf(&psi_old_gf);
|
||||
SumCoefficient psi_old_minus_psi(div_psi_old_cf, div_psi_cf, 1.0, -1.0);
|
||||
|
||||
// 10. Assemble constant matrices/vectors to avoid reassembly in the loop.
|
||||
LinearForm b0, b1;
|
||||
b0.MakeRef(&RTfes,rhs.GetBlock(0),0);
|
||||
b1.MakeRef(&L2fes,rhs.GetBlock(1),0);
|
||||
|
||||
b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(neg_Z));
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(neg_one));
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(psi_old_minus_psi));
|
||||
|
||||
BilinearForm a00(&RTfes);
|
||||
a00.AddDomainIntegrator(new VectorFEMassIntegrator(DZ));
|
||||
a00.AddDomainIntegrator(new VectorFEMassIntegrator(tichonov_cf));
|
||||
|
||||
MixedBilinearForm a10(&RTfes,&L2fes);
|
||||
a10.AddDomainIntegrator(new VectorFEDivergenceIntegrator());
|
||||
a10.Assemble();
|
||||
a10.Finalize();
|
||||
SparseMatrix &A10 = a10.SpMat();
|
||||
SparseMatrix *A01 = Transpose(A10);
|
||||
|
||||
BilinearForm a11(&L2fes);
|
||||
a11.AddDomainIntegrator(new MassIntegrator(neg_tichonov_cf));
|
||||
a11.Assemble();
|
||||
a11.Finalize();
|
||||
SparseMatrix &A11 = a11.SpMat();
|
||||
|
||||
// 11. Iterate.
|
||||
int k;
|
||||
int total_iterations = 0;
|
||||
real_t increment_u = 0.1;
|
||||
GridFunction u_tmp(&L2fes);
|
||||
for (k = 0; k < max_it; k++)
|
||||
{
|
||||
u_tmp = u_old_gf;
|
||||
Z.SetAlpha(alpha);
|
||||
DZ.SetAlpha(alpha);
|
||||
|
||||
mfem::out << "\nOUTER ITERATION " << k+1 << endl;
|
||||
|
||||
int j;
|
||||
for ( j = 0; j < 5; j++)
|
||||
{
|
||||
total_iterations++;
|
||||
|
||||
b0.Assemble();
|
||||
b1.Assemble();
|
||||
|
||||
a00.Assemble(false);
|
||||
a00.Finalize(false);
|
||||
SparseMatrix &A00 = a00.SpMat();
|
||||
|
||||
// Construct Schur-complement preconditioner
|
||||
Vector A00_diag(a00.Height());
|
||||
A00.GetDiag(A00_diag);
|
||||
A00_diag.Reciprocal();
|
||||
SparseMatrix *S = Mult_AtDA(*A01, A00_diag);
|
||||
|
||||
BlockDiagonalPreconditioner prec(offsets);
|
||||
prec.SetDiagonalBlock(0,new DSmoother(A00));
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
prec.SetDiagonalBlock(1,new GSSmoother(*S));
|
||||
#else
|
||||
prec.SetDiagonalBlock(1,new UMFPackSolver(*S));
|
||||
#endif
|
||||
prec.owns_blocks = 1;
|
||||
|
||||
BlockOperator A(offsets);
|
||||
A.SetBlock(0,0,&A00);
|
||||
A.SetBlock(1,0,&A10);
|
||||
A.SetBlock(0,1,A01);
|
||||
A.SetBlock(1,1,&A11);
|
||||
|
||||
GMRES(A,prec,rhs,x,0,2000,500,1e-12,0.0);
|
||||
delete S;
|
||||
|
||||
u_tmp -= u_gf;
|
||||
real_t Newton_update_size = u_tmp.ComputeL2Error(zero);
|
||||
u_tmp = u_gf;
|
||||
|
||||
// Damped Newton update
|
||||
psi_gf.Add(newton_scaling, delta_psi_gf);
|
||||
a00.Update();
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock << "solution\n" << mesh << u_gf << "window_title 'Discrete solution'"
|
||||
<< flush;
|
||||
}
|
||||
|
||||
mfem::out << "Newton_update_size = " << Newton_update_size << endl;
|
||||
|
||||
if (Newton_update_size < increment_u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u_tmp = u_gf;
|
||||
u_tmp -= u_old_gf;
|
||||
increment_u = u_tmp.ComputeL2Error(zero);
|
||||
|
||||
mfem::out << "Number of Newton iterations = " << j+1 << endl;
|
||||
mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_u << endl;
|
||||
|
||||
u_old_gf = u_gf;
|
||||
psi_old_gf = psi_gf;
|
||||
|
||||
if (increment_u < tol || k == max_it-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
alpha *= max(growth_rate, 1_r);
|
||||
|
||||
}
|
||||
|
||||
mfem::out << "\n Outer iterations: " << k+1
|
||||
<< "\n Total iterations: " << total_iterations
|
||||
<< "\n Total dofs: " << RTfes.GetTrueVSize() + L2fes.GetTrueVSize()
|
||||
<< endl;
|
||||
|
||||
delete A01;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(vdim);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/(alpha*alpha) + norm*norm);
|
||||
|
||||
V = psi_vals;
|
||||
V *= phi;
|
||||
}
|
||||
|
||||
void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(height);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/(alpha*alpha) + norm*norm);
|
||||
|
||||
K = 0.0;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
K(i,i) = phi;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
K(i,j) -= psi_vals(i) * psi_vals(j) * pow(phi, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
// MFEM Example 40 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex40p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex40p -step 10 -gr 2.0
|
||||
// mpirun -np 4 ex40p -step 10 -gr 2.0 -o 3 -r 1
|
||||
// mpirun -np 4 ex40p -step 10 -gr 2.0 -r 4 -m ../data/l-shape.mesh
|
||||
// mpirun -np 4 ex40p -step 10 -gr 2.0 -r 2 -m ../data/fichera.mesh
|
||||
//
|
||||
// Description: This example code demonstrates how to use MFEM to solve the
|
||||
// eikonal equation,
|
||||
//
|
||||
// |∇𝑢| = 1 in Ω, 𝑢 = g on ∂Ω.
|
||||
//
|
||||
// The solution of this problem coincides with the unique optimum of
|
||||
// the nonlinear program
|
||||
//
|
||||
// maximize ∫_Ω 𝑢 d𝑥 subject to |∇𝑢| ≤ 1, 𝑢 = g on Ω, (⋆)
|
||||
//
|
||||
// which is the foundation for method implemented below.
|
||||
//
|
||||
// Following the proximal Galerkin methodology [1] (see also Example
|
||||
// 36), we construct a Legendre function for the unit ball
|
||||
// 𝐵₁ := {𝑥 ∈ Rⁿ | |𝑥| < 1}. Our choice is the Hellinger entropy,
|
||||
//
|
||||
// h(𝑥) = −( 1 − |𝑥|² )^{1/2},
|
||||
//
|
||||
// although other choices are possible, each leading to a slightly
|
||||
// different algorithm. We then adaptively regularize the optimization
|
||||
// problem (⋆) with the Bregman divergence of the Hellinger entropy,
|
||||
//
|
||||
// maximize ∫_Ω 𝑢 d𝑥 - αₖ⁻¹ Dₕ(∇𝑢,∇𝑢ₖ₋₁) subject to 𝑢 = g on Ω.
|
||||
//
|
||||
// This results in a sequence of functions ( 𝜓ₖ , 𝑢ₖ ),
|
||||
//
|
||||
// 𝑢ₖ → 𝑢, 𝜓ₖ/|𝜓ₖ| → ∇𝑢 as k → \infty,
|
||||
//
|
||||
// defined by the nonlinear saddle-point problems
|
||||
//
|
||||
// Find 𝜓ₖ ∈ H(div,Ω) and 𝑢ₖ ∈ L²(Ω) such that
|
||||
// ( Zₖ(𝜓ₖ) , τ ) + ( 𝑢ₖ , ∇⋅τ ) = ⟨ g , τ⋅n ⟩ ∀ τ ∈ H(div,Ω)
|
||||
// ( ∇⋅𝜓ₖ , v ) = ( ∇⋅𝜓ₖ₋₁ - 1 , v ) ∀ v ∈ L²(Ω)
|
||||
//
|
||||
// where Zₖ(𝜓) := ∇h⁻¹(αₖ 𝜓) = 𝜓 / ( αₖ⁻² + |𝜓|² )^{1/2} and step size
|
||||
// αₖ > 0. These saddle-point problems are solved using a damped Newton's
|
||||
// method. This example assumes that g = 0 and allows the step size to
|
||||
// grow geometrically, αₖ = α₀rᵏ, where r ≥ 1 is the growth rate.
|
||||
//
|
||||
// [1] Keith, B. and Surowiec, T. (2023) Proximal Galerkin: A structure-
|
||||
// preserving finite element method for pointwise bound constraints.
|
||||
// arXiv:2307.12444 [math.NA]
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ZCoefficient : public VectorCoefficient
|
||||
{
|
||||
protected:
|
||||
ParGridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
ZCoefficient(int vdim, ParGridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
void SetAlpha(real_t alpha_) { alpha = alpha_; }
|
||||
};
|
||||
|
||||
class DZCoefficient : public MatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
ParGridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
DZCoefficient(int height, ParGridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: MatrixCoefficient(height), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
void SetAlpha(real_t alpha_) { alpha = alpha_; }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 0. Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int max_it = 5;
|
||||
int ref_levels = 3;
|
||||
real_t alpha = 1.0;
|
||||
real_t growth_rate = 1.0;
|
||||
real_t newton_scaling = 0.9;
|
||||
real_t tichonov = 1e-1;
|
||||
real_t tol = 1e-4;
|
||||
bool visualization = true;
|
||||
|
||||
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(&ref_levels, "-r", "--refs",
|
||||
"Number of h-refinements.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
"Maximum number of iterations");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"Stopping criteria based on the difference between"
|
||||
"successive solution updates");
|
||||
args.AddOption(&alpha, "-step", "--step",
|
||||
"Initial size alpha");
|
||||
args.AddOption(&growth_rate, "-gr", "--growth-rate",
|
||||
"Growth rate of the step size alpha");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
|
||||
MFEM_ASSERT(mesh.bdr_attributes.Size(),
|
||||
"This example does not currently support meshes"
|
||||
" without boundary attributes."
|
||||
)
|
||||
|
||||
// 3. Postprocess the mesh.
|
||||
// 3A. Refine the mesh to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// 3B. Interpolate the geometry after refinement to control geometry error.
|
||||
// NOTE: Minimum second-order interpolation is used to improve the accuracy.
|
||||
int curvature_order = max(order,2);
|
||||
mesh.SetCurvature(curvature_order);
|
||||
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
RT_FECollection RTfec(order, dim);
|
||||
ParFiniteElementSpace RTfes(&pmesh, &RTfec);
|
||||
|
||||
L2_FECollection L2fec(order, dim);
|
||||
ParFiniteElementSpace L2fes(&pmesh, &L2fec);
|
||||
|
||||
int num_dofs_RT = RTfes.GlobalTrueVSize();
|
||||
int num_dofs_L2 = L2fes.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of H(div) dofs: "
|
||||
<< num_dofs_RT << endl;
|
||||
cout << "Number of L² dofs: "
|
||||
<< num_dofs_L2 << endl;
|
||||
}
|
||||
|
||||
// 5. Define the offsets for the block matrices
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = RTfes.GetVSize();
|
||||
offsets[2] = L2fes.GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
Array<int> toffsets(3);
|
||||
toffsets[0] = 0;
|
||||
toffsets[1] = RTfes.GetTrueVSize();
|
||||
toffsets[2] = L2fes.GetTrueVSize();
|
||||
toffsets.PartialSum();
|
||||
|
||||
BlockVector x(offsets), rhs(offsets);
|
||||
x = 0.0; rhs = 0.0;
|
||||
|
||||
BlockVector tx(toffsets), trhs(toffsets);
|
||||
tx = 0.0; trhs = 0.0;
|
||||
|
||||
// 6. Define the solution vectors as a finite element grid functions
|
||||
// corresponding to the fespaces.
|
||||
ParGridFunction u_gf, delta_psi_gf;
|
||||
delta_psi_gf.MakeRef(&RTfes,x,offsets[0]);
|
||||
u_gf.MakeRef(&L2fes,x,offsets[1]);
|
||||
|
||||
ParGridFunction psi_old_gf(&RTfes);
|
||||
ParGridFunction psi_gf(&RTfes);
|
||||
ParGridFunction u_old_gf(&L2fes);
|
||||
|
||||
// 7. Define initial guesses for the solution variables.
|
||||
delta_psi_gf = 0.0;
|
||||
psi_gf = 0.0;
|
||||
u_gf = 0.0;
|
||||
psi_old_gf = psi_gf;
|
||||
u_old_gf = u_gf;
|
||||
|
||||
// 8. Prepare for glvis output.
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost,visport);
|
||||
sol_sock.precision(8);
|
||||
}
|
||||
|
||||
// 9. Coefficients to be used later.
|
||||
ConstantCoefficient neg_one(-1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
ConstantCoefficient tichonov_cf(tichonov);
|
||||
ConstantCoefficient neg_tichonov_cf(-1.0*tichonov);
|
||||
ZCoefficient Z(sdim, psi_gf, alpha);
|
||||
DZCoefficient DZ(sdim, psi_gf, alpha);
|
||||
ScalarVectorProductCoefficient neg_Z(-1.0, Z);
|
||||
DivergenceGridFunctionCoefficient div_psi_cf(&psi_gf);
|
||||
DivergenceGridFunctionCoefficient div_psi_old_cf(&psi_old_gf);
|
||||
SumCoefficient psi_old_minus_psi(div_psi_old_cf, div_psi_cf, 1.0, -1.0);
|
||||
|
||||
// 10. Assemble constant matrices/vectors to avoid reassembly in the loop.
|
||||
ParLinearForm b0, b1;
|
||||
b0.MakeRef(&RTfes,rhs.GetBlock(0),0);
|
||||
b1.MakeRef(&L2fes,rhs.GetBlock(1),0);
|
||||
|
||||
b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(neg_Z));
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(neg_one));
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(psi_old_minus_psi));
|
||||
|
||||
ParBilinearForm a00(&RTfes);
|
||||
a00.AddDomainIntegrator(new VectorFEMassIntegrator(DZ));
|
||||
a00.AddDomainIntegrator(new VectorFEMassIntegrator(tichonov_cf));
|
||||
|
||||
ParMixedBilinearForm a10(&RTfes,&L2fes);
|
||||
a10.AddDomainIntegrator(new VectorFEDivergenceIntegrator());
|
||||
a10.Assemble();
|
||||
a10.Finalize();
|
||||
HypreParMatrix *A10 = a10.ParallelAssemble();
|
||||
|
||||
HypreParMatrix *A01 = A10->Transpose();
|
||||
|
||||
ParBilinearForm a11(&L2fes);
|
||||
a11.AddDomainIntegrator(new MassIntegrator(neg_tichonov_cf));
|
||||
a11.Assemble();
|
||||
a11.Finalize();
|
||||
HypreParMatrix *A11 = a11.ParallelAssemble();
|
||||
|
||||
// 11. Iterate.
|
||||
int k;
|
||||
int total_iterations = 0;
|
||||
real_t increment_u = 0.1;
|
||||
ParGridFunction u_tmp(&L2fes);
|
||||
for (k = 0; k < max_it; k++)
|
||||
{
|
||||
u_tmp = u_old_gf;
|
||||
Z.SetAlpha(alpha);
|
||||
DZ.SetAlpha(alpha);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\nOUTER ITERATION " << k+1 << endl;
|
||||
}
|
||||
|
||||
int j;
|
||||
for ( j = 0; j < 5; j++)
|
||||
{
|
||||
total_iterations++;
|
||||
|
||||
b0.Assemble();
|
||||
b0.ParallelAssemble(trhs.GetBlock(0));
|
||||
|
||||
b1.Assemble();
|
||||
b1.ParallelAssemble(trhs.GetBlock(1));
|
||||
|
||||
a00.Assemble(false);
|
||||
a00.Finalize(false);
|
||||
HypreParMatrix *A00 = a00.ParallelAssemble();
|
||||
|
||||
// Construct Schur-complement preconditioner
|
||||
HypreParVector A00_diag(MPI_COMM_WORLD, A00->GetGlobalNumRows(),
|
||||
A00->GetRowStarts());
|
||||
A00->GetDiag(A00_diag);
|
||||
HypreParMatrix S_tmp(*A01);
|
||||
S_tmp.InvScaleRows(A00_diag);
|
||||
HypreParMatrix *S = ParMult(A10, &S_tmp, true);
|
||||
|
||||
BlockDiagonalPreconditioner prec(toffsets);
|
||||
HypreBoomerAMG P00(*A00);
|
||||
P00.SetPrintLevel(0);
|
||||
HypreBoomerAMG P11(*S);
|
||||
P11.SetPrintLevel(0);
|
||||
prec.SetDiagonalBlock(0,&P00);
|
||||
prec.SetDiagonalBlock(1,&P11);
|
||||
|
||||
BlockOperator A(toffsets);
|
||||
A.SetBlock(0,0,A00);
|
||||
A.SetBlock(1,0,A10);
|
||||
A.SetBlock(0,1,A01);
|
||||
A.SetBlock(1,1,A11);
|
||||
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPrintLevel(-1);
|
||||
gmres.SetRelTol(1e-8);
|
||||
gmres.SetMaxIter(2000);
|
||||
gmres.SetKDim(500);
|
||||
gmres.SetOperator(A);
|
||||
gmres.SetPreconditioner(prec);
|
||||
gmres.Mult(trhs,tx);
|
||||
delete S;
|
||||
delete A00;
|
||||
|
||||
delta_psi_gf.SetFromTrueDofs(tx.GetBlock(0));
|
||||
u_gf.SetFromTrueDofs(tx.GetBlock(1));
|
||||
|
||||
u_tmp -= u_gf;
|
||||
real_t Newton_update_size = u_tmp.ComputeL2Error(zero);
|
||||
u_tmp = u_gf;
|
||||
|
||||
// Damped Newton update
|
||||
psi_gf.Add(newton_scaling, delta_psi_gf);
|
||||
a00.Update();
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock << "solution\n" << pmesh << u_gf << "window_title 'Discrete solution'"
|
||||
<< flush;
|
||||
}
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "Newton_update_size = " << Newton_update_size << endl;
|
||||
}
|
||||
|
||||
if (Newton_update_size < increment_u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u_tmp = u_gf;
|
||||
u_tmp -= u_old_gf;
|
||||
increment_u = u_tmp.ComputeL2Error(zero);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "Number of Newton iterations = " << j+1 << endl;
|
||||
mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_u << endl;
|
||||
}
|
||||
|
||||
u_old_gf = u_gf;
|
||||
psi_old_gf = psi_gf;
|
||||
|
||||
if (increment_u < tol || k == max_it-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
alpha *= max(growth_rate, 1_r);
|
||||
|
||||
}
|
||||
|
||||
// 12. Print stats.
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "\n Outer iterations: " << k+1
|
||||
<< "\n Total iterations: " << total_iterations
|
||||
<< "\n Total dofs: " << RTfes.GetTrueVSize() + L2fes.GetTrueVSize()
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// 13. Free the used memory.
|
||||
delete A01;
|
||||
delete A10;
|
||||
delete A11;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(vdim);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/(alpha*alpha) + norm*norm);
|
||||
|
||||
V = psi_vals;
|
||||
V *= phi;
|
||||
}
|
||||
|
||||
void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(height);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/(alpha*alpha) + norm*norm);
|
||||
|
||||
K = 0.0;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
K(i,i) = phi;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
K(i,j) -= psi_vals(i) * psi_vals(j) * pow(phi, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
// MFEM Example 41
|
||||
//
|
||||
// Compile with: make ex41
|
||||
//
|
||||
// Sample runs: ex41 -o 2
|
||||
// ex41 -o 1 -r 4
|
||||
//
|
||||
// Description: This example code demonstrates how to use MFEM to solve the
|
||||
// Eikonal equation,
|
||||
//
|
||||
// |∇u| = 1 in Ω, u = g on ∂Ω.
|
||||
//
|
||||
// This example constructs a fast converging sequence,
|
||||
//
|
||||
// uₖ → u as k → \infty,
|
||||
//
|
||||
// by using in Newton's method to solve the sequence of nonlinear
|
||||
// saddle-point problems
|
||||
//
|
||||
// Find ψₖ ∈ H(div,Ω) and uₖ ∈ L²(Ω) such that
|
||||
// ( Zₖ(ψₖ) , τ ) + ( uₖ , ∇⋅τ ) = 0 ∀ τ ∈ H(div,Ω)
|
||||
// ( ∇⋅ψₖ , v ) = ( -1 + ∇⋅ψₖ₋₁ , v ) ∀ v ∈ L²(Ω)
|
||||
//
|
||||
// where Zₖ(ψ) = ψ / ( 1/αₖ + |ψ|² )^{1/2} and αₖ > 0.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ZCoefficient : public VectorCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
ZCoefficient(int vdim, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class DZCoefficient : public MatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
DZCoefficient(int height, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: MatrixCoefficient(height, true), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int max_it = 5;
|
||||
int ref_levels = 3;
|
||||
real_t alpha = 1.0;
|
||||
real_t tol = 1e-4;
|
||||
bool visualization = true;
|
||||
|
||||
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(&ref_levels, "-r", "--refs",
|
||||
"Number of h-refinements.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
"Maximum number of iterations");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"Stopping criteria based on the difference between"
|
||||
"successive solution updates");
|
||||
args.AddOption(&alpha, "-step", "--step",
|
||||
"Step size alpha");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
|
||||
// MFEM_ASSERT(mesh.bdr_attributes.Size(),
|
||||
// "This example does not currently support meshes"
|
||||
// " without boundary attributes."
|
||||
// )
|
||||
|
||||
bool zero_average = not mesh.bdr_attributes.Size();
|
||||
if (zero_average)
|
||||
{
|
||||
cout << "\nThe domain has no boundary. "
|
||||
<< "Solving for zero-average solution.\n" << endl;
|
||||
}
|
||||
|
||||
// 3. Postprocess the mesh.
|
||||
// 3A. Refine the mesh to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// 3B. Interpolate the geometry after refinement to control geometry error.
|
||||
// NOTE: Minimum second-order interpolation is used to improve the accuracy.
|
||||
int curvature_order = max(order,2);
|
||||
mesh.SetCurvature(curvature_order);
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
RT_FECollection RTfec(order, dim);
|
||||
FiniteElementSpace RTfes(&mesh, &RTfec);
|
||||
|
||||
L2_FECollection L2fec(order, dim);
|
||||
FiniteElementSpace L2fes(&mesh, &L2fec);
|
||||
|
||||
cout << "Number of Hdiv finite element unknowns: "
|
||||
<< RTfes.GetTrueVSize() << endl;
|
||||
cout << "Number of L2 finite element unknowns: "
|
||||
<< L2fes.GetTrueVSize() << endl;
|
||||
|
||||
// 5. Determine the list of true (i.e., conforming) essential boundary dofs.
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = RTfes.GetVSize();
|
||||
offsets[2] = L2fes.GetVSize();
|
||||
if (zero_average)
|
||||
{
|
||||
offsets.Append(1);
|
||||
}
|
||||
offsets.PartialSum();
|
||||
|
||||
BlockVector x(offsets), rhs(offsets);
|
||||
x = 0.0; rhs = 0.0;
|
||||
|
||||
// 6. Define an initial guess for the solution.
|
||||
ConstantCoefficient neg_one(-1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
|
||||
// 7. Define the solution vectors as a finite element grid functions
|
||||
// corresponding to the fespaces.
|
||||
GridFunction u_gf, delta_psi_gf;
|
||||
|
||||
delta_psi_gf.MakeRef(&RTfes,x,offsets[0]);
|
||||
u_gf.MakeRef(&L2fes,x,offsets[1]);
|
||||
delta_psi_gf = 0.0;
|
||||
|
||||
GridFunction psi_old_gf(&RTfes);
|
||||
GridFunction psi_gf(&RTfes);
|
||||
GridFunction u_old_gf(&L2fes);
|
||||
u_old_gf = 0.0;
|
||||
|
||||
// 8. Define the function coefficients for the solution and use them to
|
||||
// initialize the initial guess
|
||||
psi_gf = 0.0;
|
||||
psi_old_gf = psi_gf;
|
||||
u_old_gf = u_gf;
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost,visport);
|
||||
sol_sock.precision(8);
|
||||
}
|
||||
|
||||
SparseMatrix A21(1, L2fes.GetVSize());
|
||||
if (zero_average)
|
||||
{
|
||||
Array<int> rows(L2fes.GetVSize());
|
||||
Vector values(L2fes.GetVSize());
|
||||
for (int i = 0; i < L2fes.GetVSize(); i++)
|
||||
{
|
||||
rows[i] = i;
|
||||
}
|
||||
values = 1.0;
|
||||
A21.AddRow(0,rows,values);
|
||||
}
|
||||
A21.Finalize();
|
||||
SparseMatrix *A12 = Transpose(A21);
|
||||
|
||||
// 10. Iterate
|
||||
int k;
|
||||
int total_iterations = 0;
|
||||
real_t increment_u = 0.1;
|
||||
for (k = 0; k < max_it; k++)
|
||||
{
|
||||
GridFunction u_tmp(&L2fes);
|
||||
u_tmp = u_old_gf;
|
||||
|
||||
mfem::out << "\nOUTER ITERATION " << k+1 << endl;
|
||||
|
||||
int j;
|
||||
for ( j = 0; j < 5; j++)
|
||||
{
|
||||
total_iterations++;
|
||||
|
||||
ConstantCoefficient alpha_cf(alpha);
|
||||
|
||||
LinearForm b0,b1;
|
||||
b0.Update(&RTfes,rhs.GetBlock(0),0);
|
||||
b1.Update(&L2fes,rhs.GetBlock(1),0);
|
||||
|
||||
ZCoefficient Z(sdim, psi_gf, alpha);
|
||||
DZCoefficient DZ(sdim, psi_gf, alpha);
|
||||
|
||||
ScalarVectorProductCoefficient neg_Z(-1.0, Z);
|
||||
b0.AddDomainIntegrator(new VectorFEDomainLFIntegrator(neg_Z));
|
||||
b0.Assemble();
|
||||
|
||||
DivergenceGridFunctionCoefficient div_psi_cf(&psi_gf);
|
||||
DivergenceGridFunctionCoefficient div_psi_old_cf(&psi_old_gf);
|
||||
SumCoefficient psi_old_minus_psi(div_psi_old_cf, div_psi_cf, 1.0, -1.0);
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(neg_one));
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(psi_old_minus_psi));
|
||||
b1.Assemble();
|
||||
|
||||
BilinearForm a00(&RTfes);
|
||||
a00.AddDomainIntegrator(new VectorFEMassIntegrator(DZ));
|
||||
ConstantCoefficient eps(1e-1);
|
||||
a00.AddDomainIntegrator(new VectorFEMassIntegrator(eps));
|
||||
a00.Assemble();
|
||||
a00.Finalize();
|
||||
SparseMatrix &A00 = a00.SpMat();
|
||||
|
||||
MixedBilinearForm a10(&RTfes,&L2fes);
|
||||
a10.AddDomainIntegrator(new VectorFEDivergenceIntegrator());
|
||||
a10.Assemble();
|
||||
// Array<int> dof_elim_array(1);
|
||||
// int dof_elim = 10;
|
||||
// dof_elim_array[0] = dof_elim;
|
||||
a10.Finalize();
|
||||
SparseMatrix &A10 = a10.SpMat();
|
||||
// A10.EliminateRow(dof_elim);
|
||||
|
||||
SparseMatrix *A01 = Transpose(A10);
|
||||
|
||||
BilinearForm a11(&L2fes);
|
||||
ConstantCoefficient neg_eps(-1e-2);
|
||||
a11.AddDomainIntegrator(new MassIntegrator(neg_eps));
|
||||
a11.Assemble(false);
|
||||
a11.Finalize();
|
||||
SparseMatrix &A11 = a11.SpMat();
|
||||
|
||||
// BilinearForm a11(&L2fes);
|
||||
// a11.AddDomainIntegrator(new MassIntegrator(zero));
|
||||
// a11.Assemble(false);
|
||||
// SparseMatrix A11;
|
||||
// a11.FormSystemMatrix(dof_elim_array, A11);
|
||||
// A11.Set(dof_elim, dof_elim, 1.0);
|
||||
// rhs[offsets[1] + dof_elim] = 0.0;
|
||||
|
||||
// BlockOperator A(offsets);
|
||||
// A.SetBlock(0,0,&A00);
|
||||
// A.SetBlock(1,0,&A10);
|
||||
// A.SetBlock(0,1,A01);
|
||||
|
||||
// BlockDiagonalPreconditioner prec(offsets);
|
||||
// prec.SetDiagonalBlock(0,new GSSmoother(A00));
|
||||
// prec.SetDiagonalBlock(1,new GSSmoother(A11));
|
||||
// prec.owns_blocks = 1;
|
||||
|
||||
// GMRES(A,prec,rhs,x,0,10000,500,1e-12,0.0);
|
||||
|
||||
BlockMatrix A(offsets);
|
||||
A.SetBlock(0,0,&A00);
|
||||
A.SetBlock(1,0,&A10);
|
||||
A.SetBlock(0,1,A01);
|
||||
A.SetBlock(1,1,&A11);
|
||||
if (zero_average)
|
||||
{
|
||||
A.SetBlock(1,2,A12);
|
||||
A.SetBlock(2,1,&A21);
|
||||
}
|
||||
|
||||
SparseMatrix * A_mono = A.CreateMonolithic();
|
||||
UMFPackSolver umf(*A_mono);
|
||||
umf.Mult(rhs,x);
|
||||
|
||||
delta_psi_gf.MakeRef(&RTfes, x.GetBlock(0), 0);
|
||||
u_gf.MakeRef(&L2fes, x.GetBlock(1), 0);
|
||||
|
||||
u_tmp -= u_gf;
|
||||
real_t Newton_update_size = u_tmp.ComputeL2Error(zero);
|
||||
u_tmp = u_gf;
|
||||
|
||||
real_t gamma = 0.9;
|
||||
delta_psi_gf *= gamma;
|
||||
psi_gf += delta_psi_gf;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
// sol_sock << "solution\n" << mesh << psi_gf << "window_title 'Discrete solution'"
|
||||
sol_sock << "solution\n" << mesh << u_gf << "window_title 'Discrete solution'"
|
||||
<< flush;
|
||||
mfem::out << "Newton_update_size = " << Newton_update_size << endl;
|
||||
}
|
||||
|
||||
delete A01;
|
||||
|
||||
if (Newton_update_size < increment_u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u_tmp = u_gf;
|
||||
u_tmp -= u_old_gf;
|
||||
increment_u = u_tmp.ComputeL2Error(zero);
|
||||
|
||||
mfem::out << "Number of Newton iterations = " << j+1 << endl;
|
||||
mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_u << endl;
|
||||
|
||||
u_old_gf = u_gf;
|
||||
psi_old_gf = psi_gf;
|
||||
|
||||
if (increment_u < tol || k == max_it-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// alpha *= 2.0;
|
||||
|
||||
}
|
||||
|
||||
mfem::out << "\n Outer iterations: " << k+1
|
||||
<< "\n Total iterations: " << total_iterations
|
||||
<< "\n Total dofs: " << RTfes.GetTrueVSize() + L2fes.GetTrueVSize()
|
||||
<< endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(vdim);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/alpha + norm*norm);
|
||||
|
||||
V = psi_vals;
|
||||
V *= phi;
|
||||
}
|
||||
|
||||
void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(height);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/alpha + norm*norm);
|
||||
|
||||
K = 0.0;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
K(i,i) = phi;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
K(i,j) -= psi_vals(i) * psi_vals(j) * pow(phi, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
// MFEM Example 41
|
||||
//
|
||||
// Compile with: make ex41
|
||||
//
|
||||
// Sample runs: ex41 -o 2
|
||||
// ex41 -o 2 -r 4
|
||||
//
|
||||
// Description: This example code demonstrates how to use MFEM to solve the
|
||||
// Eikonal equation,
|
||||
//
|
||||
// |∇u| = 1 in Ω, u = g on ∂Ω.
|
||||
//
|
||||
// This example constructs a fast converging sequence,
|
||||
//
|
||||
// uₖ → u as k → \infty,
|
||||
//
|
||||
// by using in Newton's method to solve the sequence of nonlinear
|
||||
// saddle-point problems
|
||||
//
|
||||
// Find qₖ ∈ H¹₀(Ω) and uₖ ∈ H¹₀(Ω) such that
|
||||
// ( ϕₖ(|∇qₖ|) ∇qₖ , ∇w ) + ( ∇uₖ , ∇w ) = 0 ∀ w ∈ H¹₀(Ω)
|
||||
// ( ∇qₖ , ∇v ) = ( -1 , v ) + ( ∇qₖ₋₁ , ∇v ) ∀ v ∈ H¹₀(Ω)
|
||||
//
|
||||
// where ϕₖ(s) = 1 / ( 1/αₖ + s² )^{1/2} and αₖ > 0.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ZCoefficient : public VectorCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *q;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
ZCoefficient(int vdim, GridFunction &q_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), q(&q_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class DZCoefficient : public MatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *q;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
DZCoefficient(int height, GridFunction &q_, real_t alpha_ = 1.0)
|
||||
: MatrixCoefficient(height, true), q(&q_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int max_it = 5;
|
||||
int ref_levels = 3;
|
||||
real_t alpha = 1.0;
|
||||
real_t tol = 1e-4;
|
||||
bool visualization = true;
|
||||
|
||||
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(&ref_levels, "-r", "--refs",
|
||||
"Number of h-refinements.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
"Maximum number of iterations");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"Stopping criteria based on the difference between"
|
||||
"successive solution updates");
|
||||
args.AddOption(&alpha, "-step", "--step",
|
||||
"Step size alpha");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
|
||||
// 3. Postprocess the mesh.
|
||||
// 3A. Refine the mesh to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// 3B. Interpolate the geometry after refinement to control geometry error.
|
||||
// NOTE: Minimum second-order interpolation is used to improve the accuracy.
|
||||
int curvature_order = max(order,2);
|
||||
mesh.SetCurvature(curvature_order);
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
H1_FECollection H1fec(order, dim);
|
||||
FiniteElementSpace H1fes(&mesh, &H1fec);
|
||||
|
||||
cout << "Number of dofs: "
|
||||
<< H1fes.GetTrueVSize() * 2 << endl;
|
||||
|
||||
// 5. Determine the list of true (i.e., conforming) essential boundary dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
H1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
ess_tdof_list.Append(0);
|
||||
}
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = H1fes.GetVSize();
|
||||
offsets[2] = H1fes.GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
BlockVector x(offsets), rhs(offsets);
|
||||
x = 0.0; rhs = 0.0;
|
||||
|
||||
// 6. Define an initial guess for the solution.
|
||||
ConstantCoefficient neg_one(-1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
|
||||
// 7. Define the solution vectors as a finite element grid functions
|
||||
// corresponding to the fespaces.
|
||||
GridFunction u_gf, delta_q_gf;
|
||||
|
||||
delta_q_gf.MakeRef(&H1fes,x,offsets[0]);
|
||||
u_gf.MakeRef(&H1fes,x,offsets[1]);
|
||||
delta_q_gf = 0.0;
|
||||
|
||||
GridFunction q_old_gf(&H1fes);
|
||||
GridFunction q_gf(&H1fes);
|
||||
GridFunction u_old_gf(&H1fes);
|
||||
q_old_gf = 0.0;
|
||||
u_old_gf = 0.0;
|
||||
|
||||
// 8. Define the function coefficients for the solution and use them to
|
||||
// initialize the initial guess
|
||||
q_gf = 0.0;
|
||||
q_old_gf = q_gf;
|
||||
u_old_gf = u_gf;
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost,visport);
|
||||
sol_sock.precision(8);
|
||||
}
|
||||
|
||||
// 10. Iterate
|
||||
int k;
|
||||
int total_iterations = 0;
|
||||
real_t increment_u = 0.1;
|
||||
for (k = 0; k < max_it; k++)
|
||||
{
|
||||
GridFunction u_tmp(&H1fes);
|
||||
u_tmp = u_old_gf;
|
||||
|
||||
mfem::out << "\nOUTER ITERATION " << k+1 << endl;
|
||||
|
||||
ConstantCoefficient alpha_cf(alpha);
|
||||
|
||||
int j;
|
||||
for ( j = 0; j < 5; j++)
|
||||
{
|
||||
total_iterations++;
|
||||
|
||||
LinearForm b0,b1;
|
||||
b0.Update(&H1fes,rhs.GetBlock(0),0);
|
||||
b1.Update(&H1fes,rhs.GetBlock(1),0);
|
||||
|
||||
ZCoefficient Z(sdim, q_gf, alpha);
|
||||
DZCoefficient DZ(sdim, q_gf, alpha);
|
||||
|
||||
ScalarVectorProductCoefficient neg_Z(-1.0, Z);
|
||||
b0.AddDomainIntegrator(new DomainLFGradIntegrator(neg_Z));
|
||||
b0.Assemble();
|
||||
|
||||
GradientGridFunctionCoefficient grad_q_cf(&q_gf);
|
||||
GradientGridFunctionCoefficient grad_q_old_cf(&q_old_gf);
|
||||
VectorSumCoefficient grad_q_old_minus_q(grad_q_old_cf, grad_q_cf, 1.0, -1.0);
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(neg_one));
|
||||
b1.AddDomainIntegrator(new DomainLFGradIntegrator(grad_q_old_minus_q));
|
||||
b1.Assemble();
|
||||
|
||||
BilinearForm a00(&H1fes);
|
||||
// a00.AddDomainIntegrator(new DiffusionIntegrator());
|
||||
a00.AddDomainIntegrator(new DiffusionIntegrator(DZ));
|
||||
a00.Assemble();
|
||||
a00.EliminateVDofs(ess_tdof_list, mfem::Operator::DIAG_ZERO);
|
||||
// a00.EliminateVDofs(ess_tdof_list,x.GetBlock(0),rhs.GetBlock(0),
|
||||
// mfem::Operator::DIAG_ONE);
|
||||
a00.Finalize();
|
||||
SparseMatrix &A00 = a00.SpMat();
|
||||
|
||||
BilinearForm a10(&H1fes);
|
||||
a10.AddDomainIntegrator(new DiffusionIntegrator());
|
||||
a10.Assemble();
|
||||
a10.EliminateVDofs(ess_tdof_list,x.GetBlock(0),rhs.GetBlock(1),
|
||||
mfem::Operator::DIAG_ONE);
|
||||
a10.Finalize();
|
||||
SparseMatrix &A10 = a10.SpMat();
|
||||
|
||||
SparseMatrix *A01 = Transpose(A10);
|
||||
|
||||
// BlockOperator A(offsets);
|
||||
// A.SetBlock(0,0,&A00);
|
||||
// A.SetBlock(1,0,&A10);
|
||||
// A.SetBlock(0,1,A01);
|
||||
|
||||
// BlockDiagonalPreconditioner prec(offsets);
|
||||
// prec.SetDiagonalBlock(0,new GSSmoother(A00));
|
||||
// prec.SetDiagonalBlock(1,new GSSmoother(A11));
|
||||
// prec.owns_blocks = 1;
|
||||
|
||||
// GMRES(A,prec,rhs,x,0,10000,500,1e-12,0.0);
|
||||
|
||||
BlockMatrix A(offsets);
|
||||
A.SetBlock(0,0,&A00);
|
||||
A.SetBlock(0,1,A01);
|
||||
A.SetBlock(1,0,&A10);
|
||||
|
||||
SparseMatrix * A_mono = A.CreateMonolithic();
|
||||
UMFPackSolver umf(*A_mono);
|
||||
umf.Mult(rhs,x);
|
||||
|
||||
delta_q_gf.MakeRef(&H1fes, x.GetBlock(0), 0);
|
||||
u_gf.MakeRef(&H1fes, x.GetBlock(1), 0);
|
||||
|
||||
u_tmp -= u_gf;
|
||||
real_t Newton_update_size = u_tmp.ComputeL2Error(zero);
|
||||
u_tmp = u_gf;
|
||||
|
||||
real_t gamma = 1.0;
|
||||
delta_q_gf *= gamma;
|
||||
q_gf += delta_q_gf;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock << "solution\n" << mesh << u_tmp << "window_title 'Discrete solution'"
|
||||
// sol_sock << "solution\n" << mesh << q_gf << "window_title 'Discrete solution'"
|
||||
// sol_sock << "solution\n" << mesh << u_gf << "window_title 'Discrete solution'"
|
||||
<< flush;
|
||||
mfem::out << "Newton_update_size = " << Newton_update_size << endl;
|
||||
}
|
||||
|
||||
delete A01;
|
||||
|
||||
if (Newton_update_size < increment_u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u_tmp = u_gf;
|
||||
u_tmp -= u_old_gf;
|
||||
increment_u = u_tmp.ComputeL2Error(zero);
|
||||
|
||||
mfem::out << "Number of Newton iterations = " << j+1 << endl;
|
||||
mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_u << endl;
|
||||
|
||||
u_old_gf = u_gf;
|
||||
q_old_gf = q_gf;
|
||||
|
||||
if (increment_u < tol || k == max_it-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
alpha *= 2.0;
|
||||
|
||||
}
|
||||
|
||||
mfem::out << "\n Outer iterations: " << k+1
|
||||
<< "\n Total iterations: " << total_iterations
|
||||
<< "\n Total dofs: " << H1fes.GetTrueVSize() * 2
|
||||
<< endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(q != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector gradq(vdim);
|
||||
q->GetGradient(T,gradq);
|
||||
real_t norm = gradq.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/alpha + norm*norm);
|
||||
|
||||
V = gradq;
|
||||
V *= phi;
|
||||
}
|
||||
|
||||
void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(q != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector gradq(height);
|
||||
q->GetGradient(T,gradq);
|
||||
real_t norm = gradq.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/alpha + norm*norm);
|
||||
|
||||
K = 0.0;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
K(i,i) = phi;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
K(i,j) -= gradq(i) * gradq(j) * pow(phi, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
// MFEM Example 41
|
||||
//
|
||||
// Compile with: make ex41
|
||||
//
|
||||
// Sample runs: ex41 -o 2
|
||||
// ex41 -o 1 -r 4
|
||||
//
|
||||
// Description: This example code demonstrates how to use MFEM to solve the
|
||||
// Eikonal equation,
|
||||
//
|
||||
// |∇u| = 1 in Ω, u = g on ∂Ω.
|
||||
//
|
||||
// This example constructs a fast converging sequence,
|
||||
//
|
||||
// uₖ → u as k → \infty,
|
||||
//
|
||||
// by using in Newton's method to solve the sequence of nonlinear
|
||||
// saddle-point problems
|
||||
//
|
||||
// Find ψₖ ∈ L²(Ω)ⁿ and uₖ ∈ H¹₀(Ω) such that
|
||||
// ( Zₖ(ψₖ) , τ ) + ( ∇uₖ , τ ) = 0 ∀ τ ∈ L²(Ω)ⁿ
|
||||
// ( ψₖ , ∇v ) = ( -1 , v) + ( ψₖ₋₁ , ∇v ) ∀ v ∈ H¹₀(Ω)
|
||||
//
|
||||
// where Zₖ(ψ) = ψ / ( 1/αₖ + |ψ|² )^{1/2} and αₖ > 0.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ZCoefficient : public VectorCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
ZCoefficient(int vdim, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: VectorCoefficient(vdim), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
class DZCoefficient : public MatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
GridFunction *psi;
|
||||
real_t alpha;
|
||||
|
||||
public:
|
||||
DZCoefficient(int height, GridFunction &psi_, real_t alpha_ = 1.0)
|
||||
: MatrixCoefficient(height, true), psi(&psi_), alpha(alpha_) { }
|
||||
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T, const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
int max_it = 5;
|
||||
int ref_levels = 3;
|
||||
real_t alpha = 1.0;
|
||||
real_t tol = 1e-4;
|
||||
bool visualization = true;
|
||||
|
||||
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(&ref_levels, "-r", "--refs",
|
||||
"Number of h-refinements.");
|
||||
args.AddOption(&max_it, "-mi", "--max-it",
|
||||
"Maximum number of iterations");
|
||||
args.AddOption(&tol, "-tol", "--tol",
|
||||
"Stopping criteria based on the difference between"
|
||||
"successive solution updates");
|
||||
args.AddOption(&alpha, "-step", "--step",
|
||||
"Step size alpha.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
|
||||
MFEM_ASSERT(mesh.bdr_attributes.Size(),
|
||||
"This example does not currently support meshes"
|
||||
" without boundary attributes."
|
||||
)
|
||||
|
||||
// 3. Postprocess the mesh.
|
||||
// 3A. Refine the mesh to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// 3B. Interpolate the geometry after refinement to control geometry error.
|
||||
// NOTE: Minimum second-order interpolation is used to improve the accuracy.
|
||||
int curvature_order = max(order,2);
|
||||
mesh.SetCurvature(curvature_order);
|
||||
|
||||
// 4. Define the necessary finite element spaces on the mesh.
|
||||
L2_FECollection L2fec(order, dim);
|
||||
FiniteElementSpace L2fes(&mesh, &L2fec, sdim);
|
||||
|
||||
H1_FECollection H1fec(order, dim);
|
||||
FiniteElementSpace H1fes(&mesh, &H1fec);
|
||||
|
||||
cout << "Number of L2 finite element unknowns: "
|
||||
<< L2fes.GetTrueVSize() << endl;
|
||||
cout << "Number of H1 finite element unknowns: "
|
||||
<< H1fes.GetTrueVSize() << endl;
|
||||
|
||||
// 5. Determine the list of true (i.e., conforming) essential boundary dofs.
|
||||
Array<int> ess_vdof_list;
|
||||
ess_vdof_list.SetSize(H1fes.GetTrueVSize());
|
||||
if (mesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
H1fes.GetEssentialVDofs(ess_bdr, ess_vdof_list);
|
||||
}
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = L2fes.GetVSize();
|
||||
offsets[2] = H1fes.GetVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
BlockVector x(offsets), rhs(offsets);
|
||||
x = 0.0; rhs = 0.0;
|
||||
|
||||
// 6. Define an initial guess for the solution.
|
||||
ConstantCoefficient one(-1.0);
|
||||
ConstantCoefficient neg_one(-1.0);
|
||||
ConstantCoefficient zero(0.0);
|
||||
|
||||
// 7. Define the solution vectors as a finite element grid functions
|
||||
// corresponding to the fespaces.
|
||||
GridFunction u_gf, delta_psi_gf;
|
||||
|
||||
delta_psi_gf.MakeRef(&L2fes,x,offsets[0]);
|
||||
u_gf.MakeRef(&H1fes,x,offsets[1]);
|
||||
delta_psi_gf = 0.0;
|
||||
|
||||
GridFunction psi_old_gf(&L2fes);
|
||||
GridFunction psi_gf(&L2fes);
|
||||
GridFunction u_old_gf(&H1fes);
|
||||
u_old_gf = 0.0;
|
||||
|
||||
// 8. Define the function coefficients for the solution and use them to
|
||||
// initialize the initial guess
|
||||
psi_gf = 0.0;
|
||||
psi_old_gf = psi_gf;
|
||||
u_old_gf = u_gf;
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
sol_sock.open(vishost,visport);
|
||||
sol_sock.precision(8);
|
||||
}
|
||||
|
||||
// 10. Iterate
|
||||
int k;
|
||||
int total_iterations = 0;
|
||||
real_t increment_u = 0.1;
|
||||
for (k = 0; k < max_it; k++)
|
||||
{
|
||||
GridFunction u_tmp(&H1fes);
|
||||
u_tmp = u_old_gf;
|
||||
|
||||
mfem::out << "\nOUTER ITERATION " << k+1 << endl;
|
||||
|
||||
int j;
|
||||
for ( j = 0; j < 5; j++)
|
||||
{
|
||||
total_iterations++;
|
||||
|
||||
ConstantCoefficient alpha_cf(alpha);
|
||||
|
||||
LinearForm b0,b1;
|
||||
b0.Update(&L2fes,rhs.GetBlock(0),0);
|
||||
b1.Update(&H1fes,rhs.GetBlock(1),0);
|
||||
|
||||
ZCoefficient Z(sdim, psi_gf, alpha);
|
||||
DZCoefficient DZ(sdim, psi_gf, alpha);
|
||||
|
||||
ScalarVectorProductCoefficient neg_Z(-1.0, Z);
|
||||
b0.AddDomainIntegrator(new VectorDomainLFIntegrator(neg_Z));
|
||||
b0.Assemble();
|
||||
|
||||
VectorGridFunctionCoefficient psi_cf(&psi_gf);
|
||||
VectorGridFunctionCoefficient psi_old_cf(&psi_old_gf);
|
||||
VectorSumCoefficient psi_old_minus_psi(psi_old_cf, psi_cf, 1.0, -1.0);
|
||||
|
||||
b1.AddDomainIntegrator(new DomainLFIntegrator(neg_one));
|
||||
b1.AddDomainIntegrator(new DomainLFGradIntegrator(psi_old_minus_psi));
|
||||
b1.Assemble();
|
||||
|
||||
BilinearForm a00(&L2fes);
|
||||
a00.AddDomainIntegrator(new VectorMassIntegrator(DZ));
|
||||
// ConstantCoefficient eps(1e-2);
|
||||
// a00.AddDomainIntegrator(new VectorMassIntegrator(eps));
|
||||
a00.Assemble();
|
||||
a00.Finalize();
|
||||
SparseMatrix &A00 = a00.SpMat();
|
||||
|
||||
MixedBilinearForm a01(&H1fes,&L2fes);
|
||||
a01.AddDomainIntegrator(new GradientIntegrator());
|
||||
a01.Assemble();
|
||||
a01.EliminateEssentialBCFromTrialDofs(ess_vdof_list,x.GetBlock(1),rhs.GetBlock(0));
|
||||
a01.Finalize();
|
||||
SparseMatrix &A01 = a01.SpMat();
|
||||
|
||||
SparseMatrix *A10 = Transpose(A01);
|
||||
|
||||
BilinearForm a11(&H1fes);
|
||||
a11.AddDomainIntegrator(new MassIntegrator(zero));
|
||||
a11.Assemble(false);
|
||||
a11.EliminateEssentialBCFromDofs(ess_vdof_list,x.GetBlock(1),rhs.GetBlock(1));
|
||||
a11.Finalize();
|
||||
SparseMatrix &A11 = a11.SpMat();
|
||||
|
||||
// BlockOperator A(offsets);
|
||||
// A.SetBlock(0,0,&A00);
|
||||
// A.SetBlock(1,0,&A10);
|
||||
// A.SetBlock(0,1,A01);
|
||||
|
||||
// BlockDiagonalPreconditioner prec(offsets);
|
||||
// prec.SetDiagonalBlock(0,new GSSmoother(A00));
|
||||
// prec.SetDiagonalBlock(1,new GSSmoother(A11));
|
||||
// prec.owns_blocks = 1;
|
||||
|
||||
// GMRES(A,prec,rhs,x,0,10000,500,1e-12,0.0);
|
||||
|
||||
BlockMatrix A(offsets);
|
||||
A.SetBlock(0,0,&A00);
|
||||
A.SetBlock(1,0,A10);
|
||||
A.SetBlock(0,1,&A01);
|
||||
A.SetBlock(1,1,&A11);
|
||||
|
||||
SparseMatrix * A_mono = A.CreateMonolithic();
|
||||
UMFPackSolver umf(*A_mono);
|
||||
umf.Mult(rhs,x);
|
||||
|
||||
delta_psi_gf.MakeRef(&L2fes, x.GetBlock(0), 0);
|
||||
u_gf.MakeRef(&H1fes, x.GetBlock(1), 0);
|
||||
|
||||
u_tmp -= u_gf;
|
||||
real_t Newton_update_size = u_tmp.ComputeL2Error(zero);
|
||||
u_tmp = u_gf;
|
||||
|
||||
real_t gamma = 0.1;
|
||||
delta_psi_gf *= gamma;
|
||||
psi_gf += delta_psi_gf;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
// sol_sock << "solution\n" << mesh << psi_gf << "window_title 'Discrete solution'"
|
||||
sol_sock << "solution\n" << mesh << u_gf << "window_title 'Discrete solution'"
|
||||
<< flush;
|
||||
mfem::out << "Newton_update_size = " << Newton_update_size << endl;
|
||||
}
|
||||
|
||||
delete A10;
|
||||
|
||||
if (Newton_update_size < increment_u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u_tmp = u_gf;
|
||||
u_tmp -= u_old_gf;
|
||||
increment_u = u_tmp.ComputeL2Error(zero);
|
||||
|
||||
mfem::out << "Number of Newton iterations = " << j+1 << endl;
|
||||
mfem::out << "Increment (|| uₕ - uₕ_prvs||) = " << increment_u << endl;
|
||||
|
||||
u_old_gf = u_gf;
|
||||
psi_old_gf = psi_gf;
|
||||
|
||||
if (increment_u < tol || k == max_it-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// alpha *= 2.0;
|
||||
|
||||
}
|
||||
|
||||
mfem::out << "\n Outer iterations: " << k+1
|
||||
<< "\n Total iterations: " << total_iterations
|
||||
<< "\n Total dofs: " << L2fes.GetTrueVSize() + H1fes.GetTrueVSize()
|
||||
<< endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(vdim);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/alpha + norm*norm);
|
||||
|
||||
V = psi_vals;
|
||||
V *= phi;
|
||||
}
|
||||
|
||||
void DZCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(psi != NULL, "grid function is not set");
|
||||
MFEM_ASSERT(alpha > 0, "alpha is not positive");
|
||||
|
||||
Vector psi_vals(height);
|
||||
psi->GetVectorValue(T, ip, psi_vals);
|
||||
real_t norm = psi_vals.Norml2();
|
||||
real_t phi = 1.0 / sqrt(1.0/alpha + norm*norm);
|
||||
|
||||
K = 0.0;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
K(i,i) = phi;
|
||||
for (int j = 0; j < height; j++)
|
||||
{
|
||||
K(i,j) -= psi_vals(i) * psi_vals(j) * pow(phi, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-13
@@ -23,14 +23,14 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
|
||||
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
|
||||
ex37p ex39p ex40p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
|
||||
ex22p ex24p ex25p ex26p ex34p ex35p
|
||||
ex37p ex39p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26 ex34
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p \
|
||||
ex24p ex25p ex26p ex34p ex35p
|
||||
|
||||
ifeq ($(MFEM_USE_LAPACK),YES)
|
||||
SEQ_EXAMPLES += ex38
|
||||
@@ -138,14 +138,6 @@ ex10-test-seq: ex10
|
||||
@$(call mfem-test,$<,, Serial example,-tf 5)
|
||||
ex10p-test-par: ex10p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-tf 5)
|
||||
ex14-test-seq-cuda: ex14
|
||||
@$(call mfem-test,$<,, Serial CUDA example,-r 2 -pa -d cuda)
|
||||
ex14p-test-par-cuda: ex14p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel CUDA example,-rs 2 -rp 0 -pa -d cuda)
|
||||
ex14-test-seq-hip: ex14
|
||||
@$(call mfem-test,$<,, Serial HIP example,-r 2 -pa -d hip)
|
||||
ex14p-test-par-hip: ex14p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel HIP example,-rs 2 -rp 0 -pa -d hip)
|
||||
ex15-test-seq: ex15
|
||||
@$(call mfem-test,$<,, Serial example,-e 1)
|
||||
ex15p-test-par: ex15p
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -709,7 +709,10 @@ real_t HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
|
||||
|
||||
real_t HyperelasticOperator::KineticEnergy(const ParGridFunction &v) const
|
||||
{
|
||||
real_t energy = 0.5*M.ParInnerProduct(v, v);
|
||||
real_t loc_energy = 0.5*M.InnerProduct(v, v);
|
||||
real_t energy;
|
||||
MPI_Allreduce(&loc_energy, &energy, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
fespace.GetComm());
|
||||
return energy;
|
||||
}
|
||||
|
||||
|
||||
@@ -856,7 +856,10 @@ double HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
|
||||
|
||||
double HyperelasticOperator::KineticEnergy(const ParGridFunction &v) const
|
||||
{
|
||||
double energy = 0.5*M.ParInnerProduct(v, v);
|
||||
double loc_energy = 0.5*M.InnerProduct(v, v);
|
||||
double energy;
|
||||
MPI_Allreduce(&loc_energy, &energy, 1, MPI_DOUBLE, MPI_SUM,
|
||||
fespace.GetComm());
|
||||
return energy;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ set(SRCS
|
||||
integ/bilininteg_convection_pa.cpp
|
||||
integ/bilininteg_convection_ea.cpp
|
||||
integ/bilininteg_curlcurl_pa.cpp
|
||||
integ/bilininteg_dgdiffusion_pa.cpp
|
||||
integ/bilininteg_dgtrace_pa.cpp
|
||||
integ/bilininteg_dgtrace_ea.cpp
|
||||
integ/bilininteg_diffusion_mf.cpp
|
||||
@@ -118,7 +117,6 @@ set(SRCS
|
||||
quadinterpolator.cpp
|
||||
quadinterpolator_face.cpp
|
||||
restriction.cpp
|
||||
normal_deriv_restriction.cpp
|
||||
staticcond.cpp
|
||||
tmop.cpp
|
||||
tmop/tmop_pa.cpp
|
||||
@@ -230,7 +228,6 @@ set(HDRS
|
||||
quadinterpolator.hpp
|
||||
quadinterpolator_face.hpp
|
||||
restriction.hpp
|
||||
normal_deriv_restriction.hpp
|
||||
fespacehierarchy.hpp
|
||||
staticcond.hpp
|
||||
tbilinearform.hpp
|
||||
|
||||
@@ -340,9 +340,9 @@ public:
|
||||
$ M^{-1} $ (currently returns NULL) */
|
||||
virtual MatrixInverse *Inverse() const;
|
||||
|
||||
/** @brief Finalizes the matrix initialization if the ::AssemblyLevel is
|
||||
/** @brief Finalizes the matrix initialization if the ::AssemblyLevel is
|
||||
AssemblyLevel::LEGACY.
|
||||
The matrix that gets finalized is different if you are using static
|
||||
THe matrix that gets finalized is different if you are using static
|
||||
condensation or hybridization.*/
|
||||
virtual void Finalize(int skip_zeros = 1);
|
||||
|
||||
@@ -643,7 +643,7 @@ public:
|
||||
void EliminateVDofs(const Array<int> &vdofs, const Vector &sol, Vector &rhs,
|
||||
DiagonalPolicy dpolicy = DIAG_ONE);
|
||||
|
||||
/** @brief Eliminate the given @a vdofs, storing the eliminated part
|
||||
/** @brief Eliminate the given @a vdofs, storing the eliminated part
|
||||
internally in $ M_e $.
|
||||
|
||||
This method works in conjunction with EliminateVDofsInRHS() and allows
|
||||
@@ -826,7 +826,7 @@ public:
|
||||
$ M^{-1} $ (currently unimplemented and returns NULL)*/
|
||||
virtual MatrixInverse *Inverse() const;
|
||||
|
||||
/** @brief Finalizes the matrix initialization if the ::AssemblyLevel is
|
||||
/** @brief Finalizes the matrix initialization if the ::AssemblyLevel is
|
||||
AssemblyLevel::LEGACY.*/
|
||||
virtual void Finalize(int skip_zeros = 1);
|
||||
|
||||
|
||||
+8
-136
@@ -282,22 +282,6 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
int_face_X.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
int_face_Y.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
int_face_Y.UseDevice(true); // ensure 'int_face_Y = 0.0' is done on device
|
||||
|
||||
bool needs_normal_derivs = false;
|
||||
auto &integs = *a->GetFBFI();
|
||||
for (int i = 0; i < integs.Size(); ++i)
|
||||
{
|
||||
if (integs[i]->RequiresFaceNormalDerivatives())
|
||||
{
|
||||
needs_normal_derivs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needs_normal_derivs)
|
||||
{
|
||||
int_face_dXdn.SetSize(int_face_restrict_lex->Height());
|
||||
int_face_dYdn.SetSize(int_face_restrict_lex->Height());
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_bdr_integs = (a->GetBFBFI()->Size() > 0 ||
|
||||
@@ -312,22 +296,6 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
bdr_face_Y.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
|
||||
bdr_face_Y.UseDevice(true); // ensure 'faceBoundY = 0.0' is done on device
|
||||
|
||||
bool needs_normal_derivs = false;
|
||||
auto &integs = *a->GetBFBFI();
|
||||
for (int i = 0; i < integs.Size(); ++i)
|
||||
{
|
||||
if (integs[i]->RequiresFaceNormalDerivatives())
|
||||
{
|
||||
needs_normal_derivs = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needs_normal_derivs)
|
||||
{
|
||||
bdr_face_dXdn.SetSize(bdr_face_restrict_lex->Height());
|
||||
bdr_face_dYdn.SetSize(bdr_face_restrict_lex->Height());
|
||||
}
|
||||
|
||||
const Mesh &mesh = *trial_fes->GetMesh();
|
||||
// See LinearFormExtension::Update for explanation of f_to_be logic.
|
||||
std::unordered_map<int,int> f_to_be;
|
||||
@@ -574,8 +542,8 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
localY = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*integrators[i], localX, elem_markers[i],
|
||||
elem_attributes, false, localY);
|
||||
AddMultWithMarkers(*integrators[i], localX, elem_markers[i], elem_attributes,
|
||||
false, localY);
|
||||
}
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
@@ -589,57 +557,15 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
const int iFISz = intFaceIntegrators.Size();
|
||||
if (int_face_restrict_lex && iFISz>0)
|
||||
{
|
||||
// When assembling interior face integrators for DG spaces, we need to
|
||||
// exchange the face-neighbor information. This happens inside member
|
||||
// functions of the 'int_face_restrict_lex'. To avoid repeated calls to
|
||||
// ParGridFunction::ExchangeFaceNbrData, if we have a parallel space
|
||||
// with interior face integrators, we create a ParGridFunction that
|
||||
// will be used to cache the face-neighbor data. x_dg should be passed
|
||||
// to any restriction operator that may need to use face-neighbor data.
|
||||
const Vector *x_dg = &x;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParGridFunction x_pgf;
|
||||
if (auto *pfes = dynamic_cast<ParFiniteElementSpace*>(a->FESpace()))
|
||||
{
|
||||
x_pgf.MakeRef(pfes, const_cast<Vector&>(x), 0);
|
||||
x_dg = &x_pgf;
|
||||
}
|
||||
#endif
|
||||
|
||||
int_face_restrict_lex->Mult(*x_dg, int_face_X);
|
||||
if (int_face_dXdn.Size() > 0)
|
||||
{
|
||||
int_face_restrict_lex->NormalDerivativeMult(*x_dg, int_face_dXdn);
|
||||
}
|
||||
if (int_face_X.Size() > 0)
|
||||
int_face_restrict_lex->Mult(x, int_face_X);
|
||||
if (int_face_X.Size()>0)
|
||||
{
|
||||
int_face_Y = 0.0;
|
||||
|
||||
// if normal derivatives are needed by at least one integrator...
|
||||
if (int_face_dYdn.Size() > 0)
|
||||
{
|
||||
int_face_dYdn = 0.0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < iFISz; ++i)
|
||||
{
|
||||
if (intFaceIntegrators[i]->RequiresFaceNormalDerivatives())
|
||||
{
|
||||
intFaceIntegrators[i]->AddMultPAFaceNormalDerivatives(
|
||||
int_face_X, int_face_dXdn,
|
||||
int_face_Y, int_face_dYdn);
|
||||
}
|
||||
else
|
||||
{
|
||||
intFaceIntegrators[i]->AddMultPA(int_face_X, int_face_Y);
|
||||
}
|
||||
intFaceIntegrators[i]->AddMultPA(int_face_X, int_face_Y);
|
||||
}
|
||||
int_face_restrict_lex->AddMultTransposeInPlace(int_face_Y, y);
|
||||
if (int_face_dYdn.Size() > 0)
|
||||
{
|
||||
int_face_restrict_lex->NormalDerivativeAddMultTranspose(
|
||||
int_face_dYdn, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,19 +579,9 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
Array<Array<int>*> &bdr_markers = *a->GetBBFI_Marker();
|
||||
Array<Array<int>*> &bdr_face_markers = *a->GetBFBFI_Marker();
|
||||
bdr_face_restrict_lex->Mult(x, bdr_face_X);
|
||||
if (bdr_face_dXdn.Size() > 0)
|
||||
{
|
||||
bdr_face_restrict_lex->NormalDerivativeMult(x, bdr_face_dXdn);
|
||||
}
|
||||
if (bdr_face_X.Size() > 0)
|
||||
if (bdr_face_X.Size()>0)
|
||||
{
|
||||
bdr_face_Y = 0.0;
|
||||
|
||||
// if normal derivatives are needed by at least one integrator...
|
||||
if (bdr_face_dYdn.Size() > 0)
|
||||
{
|
||||
bdr_face_dYdn = 0.0;
|
||||
}
|
||||
for (int i = 0; i < n_bdr_integs; ++i)
|
||||
{
|
||||
AddMultWithMarkers(*bdr_integs[i], bdr_face_X, bdr_markers[i], bdr_attributes,
|
||||
@@ -673,23 +589,10 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
for (int i = 0; i < n_bdr_face_integs; ++i)
|
||||
{
|
||||
if (bdr_face_integs[i]->RequiresFaceNormalDerivatives())
|
||||
{
|
||||
AddMultNormalDerivativesWithMarkers(
|
||||
*bdr_face_integs[i], bdr_face_X, bdr_face_dXdn,
|
||||
bdr_face_markers[i], bdr_attributes, bdr_face_Y, bdr_face_dYdn);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
|
||||
bdr_attributes, false, bdr_face_Y);
|
||||
}
|
||||
AddMultWithMarkers(*bdr_face_integs[i], bdr_face_X, bdr_face_markers[i],
|
||||
bdr_attributes, false, bdr_face_Y);
|
||||
}
|
||||
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
|
||||
if (bdr_face_dYdn.Size() > 0)
|
||||
{
|
||||
bdr_face_restrict_lex->NormalDerivativeAddMultTranspose(bdr_face_dYdn, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -790,37 +693,6 @@ static void AddWithMarkers_(
|
||||
});
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::AddMultNormalDerivativesWithMarkers(
|
||||
const BilinearFormIntegrator &integ,
|
||||
const Vector &x,
|
||||
const Vector &dxdn,
|
||||
const Array<int> *markers,
|
||||
const Array<int> &attributes,
|
||||
Vector &y,
|
||||
Vector &dydn) const
|
||||
{
|
||||
if (markers)
|
||||
{
|
||||
tmp_evec.SetSize(y.Size() + dydn.Size());
|
||||
tmp_evec = 0.0;
|
||||
Vector tmp_y(tmp_evec, 0, y.Size());
|
||||
Vector tmp_dydn(tmp_evec, y.Size(), dydn.Size());
|
||||
|
||||
integ.AddMultPAFaceNormalDerivatives(x, dxdn, tmp_y, tmp_dydn);
|
||||
|
||||
const int ne = attributes.Size();
|
||||
const int nd_1 = x.Size() / ne;
|
||||
const int nd_2 = dxdn.Size() / ne;
|
||||
|
||||
AddWithMarkers_(ne, nd_1, tmp_y, *markers, attributes, y);
|
||||
AddWithMarkers_(ne, nd_2, tmp_dydn, *markers, attributes, dydn);
|
||||
}
|
||||
else
|
||||
{
|
||||
integ.AddMultPAFaceNormalDerivatives(x, dxdn, y, dydn);
|
||||
}
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::AddMultWithMarkers(
|
||||
const BilinearFormIntegrator &integ,
|
||||
const Vector &x,
|
||||
|
||||
@@ -74,8 +74,6 @@ protected:
|
||||
mutable Vector localX, localY;
|
||||
mutable Vector int_face_X, int_face_Y;
|
||||
mutable Vector bdr_face_X, bdr_face_Y;
|
||||
mutable Vector int_face_dXdn, int_face_dYdn;
|
||||
mutable Vector bdr_face_dXdn, bdr_face_dYdn;
|
||||
const Operator *elem_restrict; // Not owned
|
||||
const FaceRestriction *int_face_restrict_lex; // Not owned
|
||||
const FaceRestriction *bdr_face_restrict_lex; // Not owned
|
||||
@@ -115,23 +113,6 @@ protected:
|
||||
const Array<int> &attributes,
|
||||
const bool transpose,
|
||||
Vector &y) const;
|
||||
|
||||
/// @brief Performs the same function as AddMultWithMarkers, but takes as
|
||||
/// input and output face normal derivatives.
|
||||
///
|
||||
/// This is required when the integrator requires face normal derivatives,
|
||||
/// for example, DGDiffusionIntegrator.
|
||||
///
|
||||
/// This is called when the integrator's member function
|
||||
/// BilinearFormIntegrator::RequiresFaceNormalDerivatives() returns true.
|
||||
void AddMultNormalDerivativesWithMarkers(
|
||||
const BilinearFormIntegrator &integ,
|
||||
const Vector &x,
|
||||
const Vector &dxdn,
|
||||
const Array<int> *markers,
|
||||
const Array<int> &attributes,
|
||||
Vector &y,
|
||||
Vector &dydn) const;
|
||||
};
|
||||
|
||||
/// Data and methods for element-assembled bilinear forms
|
||||
|
||||
+17
-21
@@ -189,12 +189,6 @@ void BilinearFormIntegrator::AssembleTraceFaceMatrix (int elem,
|
||||
" Integrator class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddMultPAFaceNormalDerivatives(
|
||||
const Vector &x, const Vector &dxdn, Vector &y, Vector &dydn) const
|
||||
{
|
||||
MFEM_ABORT("Not implemented.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssembleElementVector(
|
||||
const FiniteElement &el, ElementTransformation &Tr, const Vector &elfun,
|
||||
Vector &elvect)
|
||||
@@ -774,16 +768,17 @@ void GradientIntegrator::AssembleElementMatrix2(
|
||||
ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
dim = test_fe.GetDim();
|
||||
int spaceDim = Trans.GetSpaceDim();
|
||||
int trial_dof = trial_fe.GetDof();
|
||||
int test_dof = test_fe.GetDof();
|
||||
real_t c;
|
||||
Vector d_col;
|
||||
|
||||
dshape.SetSize(trial_dof, dim);
|
||||
gshape.SetSize(trial_dof, dim);
|
||||
Jadj.SetSize(dim);
|
||||
gshape.SetSize(trial_dof, spaceDim);
|
||||
Jadj.SetSize(dim, spaceDim);
|
||||
shape.SetSize(test_dof);
|
||||
elmat.SetSize(dim * test_dof, trial_dof);
|
||||
elmat.SetSize(spaceDim * test_dof, trial_dof);
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(trial_fe, test_fe,
|
||||
Trans);
|
||||
@@ -810,7 +805,7 @@ void GradientIntegrator::AssembleElementMatrix2(
|
||||
}
|
||||
shape *= c;
|
||||
|
||||
for (int d = 0; d < dim; ++d)
|
||||
for (int d = 0; d < spaceDim; ++d)
|
||||
{
|
||||
gshape.GetColumnReference(d, d_col);
|
||||
MultVWt(shape, d_col, elmat_comp);
|
||||
@@ -3429,7 +3424,7 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
int ndof1, ndof2, ndofs;
|
||||
int dim, ndof1, ndof2, ndofs;
|
||||
bool kappa_is_nonzero = (kappa != 0.);
|
||||
real_t w, wq = 0.0;
|
||||
|
||||
@@ -3472,9 +3467,17 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
const int order = (ndof2) ? max(el1.GetOrder(),
|
||||
el2.GetOrder()) : el1.GetOrder();
|
||||
ir = &GetRule(order, Trans);
|
||||
// a simple choice for the integration order; is this OK?
|
||||
int order;
|
||||
if (ndof2)
|
||||
{
|
||||
order = 2*max(el1.GetOrder(), el2.GetOrder());
|
||||
}
|
||||
else
|
||||
{
|
||||
order = 2*el1.GetOrder();
|
||||
}
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
// assemble: < {(Q \nabla u).n},[v] > --> elmat
|
||||
@@ -3652,13 +3655,6 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
|
||||
}
|
||||
}
|
||||
|
||||
const IntegrationRule &DGDiffusionIntegrator::GetRule(
|
||||
int order, FaceElementTransformations &T)
|
||||
{
|
||||
// order is typically the maximum of the order of the left and right elements
|
||||
// neighboring the given face.
|
||||
return IntRules.Get(T.GetGeometryType(), 2*order);
|
||||
}
|
||||
|
||||
// static method
|
||||
void DGElasticityIntegrator::AssembleBlock(
|
||||
|
||||
+7
-63
@@ -266,39 +266,6 @@ public:
|
||||
Vector &flux, Vector *d_energy = NULL)
|
||||
{ return 0.0; }
|
||||
|
||||
/** @brief For bilinear forms on element faces, specifies if the normal
|
||||
derivatives are needed on the faces or just the face restriction.
|
||||
|
||||
@details if RequiresFaceNormalDerivatives() == true, then
|
||||
AddMultPAFaceNormalDerivatives(...) should be invoked in place
|
||||
of AddMultPA(...) and L2NormalDerivativeFaceRestriction should
|
||||
be used to compute the normal derivatives. This is used for some
|
||||
DG integrators, for example DGDiffusionIntegrator.
|
||||
|
||||
@returns whether normal derivatives appear in the bilinear form.
|
||||
*/
|
||||
virtual bool RequiresFaceNormalDerivatives() const { return false; }
|
||||
|
||||
/// Method for partially assembled action.
|
||||
/** @brief For bilinear forms on element faces that depend on the normal
|
||||
derivative on the faces, computes the action of integrator to the
|
||||
face values @a x and reference-normal derivatives @a dxdn and adds
|
||||
the result to @a y and @a dydn.
|
||||
|
||||
@details This method can be called only after the method AssemblePA() has
|
||||
been called.
|
||||
|
||||
@param[in] x E-vector of face values (provided by
|
||||
FaceRestriction::Mult)
|
||||
@param[in] dxdn E-vector of face reference-normal derivatives
|
||||
(provided by FaceRestriction::NormalDerivativeMult)
|
||||
@param[in,out] y E-vector of face values to add action to.
|
||||
@param[in,out] dydn E-vector of face reference-normal derivative values to
|
||||
add action to.
|
||||
*/
|
||||
virtual void AddMultPAFaceNormalDerivatives(const Vector &x, const Vector &dxdn,
|
||||
Vector &y, Vector &dydn) const;
|
||||
|
||||
virtual ~BilinearFormIntegrator() { }
|
||||
};
|
||||
|
||||
@@ -1741,7 +1708,7 @@ public:
|
||||
{ vector_fe.CalcPhysDShape(Trans, shape); }
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\hat{V} \cdot \nabla u, \nabla \cdot v)$ in 2D
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\hat{V} \cdot \nabla \cdot u, \nabla \cdot v)$ in 2D
|
||||
or 3D and where $\hat{V}$ is a vector coefficient, $u$ is in $H^1$ and $v$ is in $H(div)$. */
|
||||
class MixedGradDivIntegrator : public MixedScalarVectorIntegrator
|
||||
{
|
||||
@@ -1780,7 +1747,7 @@ public:
|
||||
{ scalar_fe.CalcPhysDivShape(Trans, shape); }
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\hat{V} \nabla \cdot u, \nabla v)$ in 2D
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\hat{V} \nabla \cdot u, \nabla \cdot v)$ in 2D
|
||||
or 3D and where $\hat{V}$ is a vector coefficient, $u$ is in $H(div)$ and $v$ is in $H^1$. */
|
||||
class MixedDivGradIntegrator : public MixedScalarVectorIntegrator
|
||||
{
|
||||
@@ -1820,7 +1787,7 @@ public:
|
||||
{ scalar_fe.CalcPhysDivShape(Trans, shape); }
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\hat{V} u, \nabla v)$ in 2D or 3D
|
||||
/** Class for integrating the bilinear form $a(u,v) := (-\hat{V} u, \nabla \cdot v)$ in 2D or 3D
|
||||
and where $\hat{V}$ is a vector coefficient, $u$ is in $H^1$ or $L_2$ and $v$ is in $H^1$. */
|
||||
class MixedScalarWeakDivergenceIntegrator : public MixedScalarVectorIntegrator
|
||||
{
|
||||
@@ -3262,13 +3229,6 @@ protected:
|
||||
Vector shape1, shape2, dshape1dn, dshape2dn, nor, nh, ni;
|
||||
DenseMatrix jmat, dshape1, dshape2, mq, adjJ;
|
||||
|
||||
|
||||
// PA extension
|
||||
Vector pa_data; // (Q, h, dot(n,J)|el0, dot(n,J)|el1)
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
int dim, nf, nq, dofs1D, quad1D;
|
||||
IntegrationRules irs{0, Quadrature1D::GaussLobatto};
|
||||
|
||||
public:
|
||||
DGDiffusionIntegrator(const real_t s, const real_t k)
|
||||
: Q(NULL), MQ(NULL), sigma(s), kappa(k) { }
|
||||
@@ -3277,26 +3237,10 @@ public:
|
||||
DGDiffusionIntegrator(MatrixCoefficient &q, const real_t s, const real_t k)
|
||||
: Q(NULL), MQ(&q), sigma(s), kappa(k) { }
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
bool RequiresFaceNormalDerivatives() const override { return true; }
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
|
||||
void AssemblePAInteriorFaces(const FiniteElementSpace &fes) override;
|
||||
|
||||
void AssemblePABoundaryFaces(const FiniteElementSpace &fes) override;
|
||||
|
||||
void AddMultPAFaceNormalDerivatives(const Vector &x, const Vector &dxdn,
|
||||
Vector &y, Vector &dydn) const override;
|
||||
|
||||
const IntegrationRule &GetRule(int order, FaceElementTransformations &T);
|
||||
|
||||
private:
|
||||
void SetupPA(const FiniteElementSpace &fes, FaceType type);
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/** Integrator for the "BR2" diffusion stabilization term
|
||||
|
||||
+4
-118
@@ -807,7 +807,6 @@ void SymmetricMatrixCoefficient::ProjectSymmetric(QuadratureFunction &qf)
|
||||
|
||||
QuadratureSpaceBase &qspace = *qf.GetSpace();
|
||||
const int ne = qspace.GetNE();
|
||||
qf.HostWrite();
|
||||
DenseMatrix values;
|
||||
DenseSymmetricMatrix matrix;
|
||||
for (int iel = 0; iel < ne; ++iel)
|
||||
@@ -819,7 +818,7 @@ void SymmetricMatrixCoefficient::ProjectSymmetric(QuadratureFunction &qf)
|
||||
{
|
||||
const IntegrationPoint &ip = ir[iq];
|
||||
T.SetIntPoint(&ip);
|
||||
matrix.UseExternalData(&values(0, iq), height);
|
||||
matrix.UseExternalData(&values(0, iq), vdim);
|
||||
Eval(matrix, T, ip);
|
||||
}
|
||||
}
|
||||
@@ -829,12 +828,13 @@ void SymmetricMatrixCoefficient::ProjectSymmetric(QuadratureFunction &qf)
|
||||
void SymmetricMatrixCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
Eval(mat_aux, T, ip);
|
||||
mat.SetSize(height);
|
||||
Eval(mat, T, ip);
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
for (int i = 0; i < height; ++ i)
|
||||
{
|
||||
K(i, j) = mat_aux(i, j);
|
||||
K(i, j) = mat(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -924,75 +924,6 @@ void MatrixArrayCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
MatrixArrayVectorCoefficient::MatrixArrayVectorCoefficient (int dim)
|
||||
: MatrixCoefficient (dim)
|
||||
{
|
||||
Coeff.SetSize(height);
|
||||
ownCoeff.SetSize(height);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
Coeff[i] = NULL;
|
||||
ownCoeff[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixArrayVectorCoefficient::SetTime(real_t t)
|
||||
{
|
||||
for (int i=0; i < height; i++)
|
||||
{
|
||||
if (Coeff[i]) { Coeff[i]->SetTime(t); }
|
||||
}
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void MatrixArrayVectorCoefficient::Set(int i, VectorCoefficient * c, bool own)
|
||||
{
|
||||
MFEM_ASSERT(i < height && i >= 0, "Row "
|
||||
<< i << " does not exist. " <<
|
||||
"Matrix height = " << height << ".");
|
||||
if (ownCoeff[i]) { delete Coeff[i]; }
|
||||
Coeff[i] = c;
|
||||
ownCoeff[i] = own;
|
||||
}
|
||||
|
||||
MatrixArrayVectorCoefficient::~MatrixArrayVectorCoefficient ()
|
||||
{
|
||||
for (int i=0; i < height; i++)
|
||||
{
|
||||
if (ownCoeff[i]) { delete Coeff[i]; }
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixArrayVectorCoefficient::Eval(int i, Vector &V,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
MFEM_ASSERT(i < height && i >= 0, "Row "
|
||||
<< i << " does not exist. " <<
|
||||
"Matrix height = " << height << ".");
|
||||
if (Coeff[i])
|
||||
{
|
||||
Coeff[i] -> Eval(V, T, ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
V = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixArrayVectorCoefficient::Eval(DenseMatrix &K,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
K.SetSize(height, width);
|
||||
Vector V(width);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
this->Eval(i, V, T, ip);
|
||||
K.SetRow(i, V);
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixRestrictedCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (c) { c->SetTime(t); }
|
||||
@@ -1110,27 +1041,6 @@ real_t DeterminantCoefficient::Eval(ElementTransformation &T,
|
||||
return ma.Det();
|
||||
}
|
||||
|
||||
TraceCoefficient::TraceCoefficient(MatrixCoefficient &A)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth())
|
||||
{
|
||||
MFEM_ASSERT(A.GetHeight() == A.GetWidth(),
|
||||
"TraceCoefficient: "
|
||||
"Argument must be a square matrix.");
|
||||
}
|
||||
|
||||
void TraceCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t TraceCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(ma, T, ip);
|
||||
return ma.Trace();
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(int dim)
|
||||
: VectorCoefficient(dim),
|
||||
ACoef(NULL), BCoef(NULL),
|
||||
@@ -1416,30 +1326,6 @@ void InverseMatrixCoefficient::Eval(DenseMatrix &M,
|
||||
M.Invert();
|
||||
}
|
||||
|
||||
ExponentialMatrixCoefficient::ExponentialMatrixCoefficient(MatrixCoefficient &A)
|
||||
: MatrixCoefficient(A.GetHeight(), A.GetWidth()), a(&A)
|
||||
{
|
||||
MFEM_ASSERT(A.GetHeight() == A.GetWidth() && A.GetHeight() == 2,
|
||||
"ExponentialMatrixCoefficient: "
|
||||
<< "Argument must be a square 2x2 matrix."
|
||||
<< " Height = " << A.GetHeight()
|
||||
<< ", Width = " << A.GetWidth());
|
||||
}
|
||||
|
||||
void ExponentialMatrixCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->MatrixCoefficient::SetTime(t);
|
||||
}
|
||||
|
||||
void ExponentialMatrixCoefficient::Eval(DenseMatrix &M,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(M, T, ip);
|
||||
M.Exponential();
|
||||
}
|
||||
|
||||
OuterProductCoefficient::OuterProductCoefficient(VectorCoefficient &A,
|
||||
VectorCoefficient &B)
|
||||
: MatrixCoefficient(A.GetVDim(), B.GetVDim()), a(&A), b(&B),
|
||||
|
||||
+6
-100
@@ -1334,46 +1334,6 @@ public:
|
||||
virtual ~MatrixArrayCoefficient();
|
||||
};
|
||||
|
||||
/** @brief Matrix coefficient defined row-wise by an array of vector
|
||||
coefficients. Rows that are not set will evaluate to zero. The
|
||||
matrix coefficient is stored as an array indexing the rows of
|
||||
the matrix. */
|
||||
class MatrixArrayVectorCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
Array<VectorCoefficient *> Coeff;
|
||||
Array<bool> ownCoeff;
|
||||
|
||||
public:
|
||||
/** @brief Construct a coefficient matrix of dimensions @a dim * @a dim. The
|
||||
actual coefficients still need to be added with Set(). */
|
||||
explicit MatrixArrayVectorCoefficient (int dim);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Get the vector coefficient located at the i-th row of the matrix
|
||||
VectorCoefficient* GetCoeff (int i) { return Coeff[i]; }
|
||||
|
||||
/** @brief Set the coefficient located at the i-th row of the matrix.
|
||||
By this will take ownership of the Coefficient passed in, but this
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, VectorCoefficient * c, bool own=true);
|
||||
|
||||
using MatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate coefficient located at the i-th row of the matrix using integration
|
||||
/// point @a ip.
|
||||
void Eval(int i, Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/// Evaluate the matrix coefficient @a ip.
|
||||
void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
|
||||
virtual ~MatrixArrayVectorCoefficient();
|
||||
};
|
||||
|
||||
|
||||
/** @brief Derived matrix coefficient that has the value of the parent matrix
|
||||
coefficient where it is active and is zero otherwise. */
|
||||
@@ -1466,13 +1426,12 @@ public:
|
||||
class SymmetricMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
|
||||
/// Internal matrix used when evaluating this coefficient as a DenseMatrix.
|
||||
mutable DenseSymmetricMatrix mat_aux;
|
||||
DenseSymmetricMatrix mat;
|
||||
public:
|
||||
/// Construct a dim x dim matrix coefficient.
|
||||
explicit SymmetricMatrixCoefficient(int dimension)
|
||||
: MatrixCoefficient(dimension, true), mat_aux(height) { }
|
||||
: MatrixCoefficient(dimension, true) { }
|
||||
|
||||
/// Get the size of the matrix.
|
||||
int GetSize() const { return height; }
|
||||
@@ -1505,9 +1464,8 @@ public:
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
|
||||
/// @deprecated Return a reference to the internal matrix used when evaluating this coefficient as a DenseMatrix.
|
||||
MFEM_DEPRECATED const DenseSymmetricMatrix& GetMatrix() { return mat_aux; }
|
||||
/// Return a reference to the constant matrix.
|
||||
const DenseSymmetricMatrix& GetMatrix() { return mat; }
|
||||
|
||||
virtual ~SymmetricMatrixCoefficient() { }
|
||||
};
|
||||
@@ -1527,10 +1485,6 @@ public:
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseSymmetricMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) { M = mat; }
|
||||
|
||||
/// Return a reference to the constant matrix.
|
||||
const DenseSymmetricMatrix& GetMatrix() { return mat; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1807,31 +1761,6 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as the trace of a matrix coefficient
|
||||
class TraceCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
MatrixCoefficient * a;
|
||||
|
||||
mutable DenseMatrix ma;
|
||||
|
||||
public:
|
||||
/// Construct with the matrix.
|
||||
TraceCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
virtual real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Vector coefficient defined as the linear combination of two vectors
|
||||
class VectorSumCoefficient : public VectorCoefficient
|
||||
{
|
||||
@@ -2183,7 +2112,7 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Matrix coefficient defined as the transpose of a matrix coefficient
|
||||
/// Matrix coefficient defined as the transpose a matrix coefficient
|
||||
class TransposeMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
@@ -2206,7 +2135,7 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Matrix coefficient defined as the inverse of a matrix coefficient.
|
||||
/// Matrix coefficient defined as the inverse a matrix coefficient.
|
||||
class InverseMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
@@ -2229,29 +2158,6 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Matrix coefficient defined as the exponential of a matrix coefficient.
|
||||
class ExponentialMatrixCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
MatrixCoefficient * a;
|
||||
|
||||
public:
|
||||
/// Construct the matrix coefficient. Result is $ \exp(A) $.
|
||||
ExponentialMatrixCoefficient(MatrixCoefficient &A);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t);
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
/// Matrix coefficient defined as the outer product of two vector coefficients.
|
||||
class OuterProductCoefficient : public MatrixCoefficient
|
||||
{
|
||||
|
||||
+12
-3
@@ -1243,16 +1243,25 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
HypreParMatrix * Ah;
|
||||
A_i.Get(Ah);
|
||||
hypre_ParCSRMatrix *Aih = *Ah;
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
ess_tdof_list.HostRead();
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
const int j = ess_tdof_list[k];
|
||||
Aih->diag->data[Aih->diag->i[j]] = 0.0;
|
||||
}
|
||||
#else
|
||||
Ah->HypreReadWrite();
|
||||
const int *d_ess_tdof_list =
|
||||
ess_tdof_list.GetMemory().Read(GetHypreMemoryClass(), n);
|
||||
HYPRE_Int *d_diag_i = Aih->diag->i;
|
||||
ess_tdof_list.GetMemory().Read(MemoryClass::DEVICE, n);
|
||||
const int *d_diag_i = Aih->diag->i;
|
||||
real_t *d_diag_data = Aih->diag->data;
|
||||
mfem::hypre_forall(n, [=] MFEM_HOST_DEVICE (int k)
|
||||
MFEM_GPU_FORALL(k, n,
|
||||
{
|
||||
const int j = d_ess_tdof_list[k];
|
||||
d_diag_data[d_diag_i[j]] = 0.0;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+1
-11
@@ -1,18 +1,8 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "convergence.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
+2
-2
@@ -101,7 +101,7 @@ void DGMassInverse::SetRelTol(const real_t rel_tol_) { rel_tol = rel_tol_; }
|
||||
|
||||
void DGMassInverse::SetAbsTol(const real_t abs_tol_) { abs_tol = abs_tol_; }
|
||||
|
||||
void DGMassInverse::SetMaxIter(const int max_iter_) { max_iter = max_iter_; }
|
||||
void DGMassInverse::SetMaxIter(const real_t max_iter_) { max_iter = max_iter_; }
|
||||
|
||||
void DGMassInverse::Update()
|
||||
{
|
||||
@@ -137,7 +137,7 @@ void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
|
||||
const real_t RELTOL = rel_tol;
|
||||
const real_t ABSTOL = abs_tol;
|
||||
const int MAXIT = max_iter;
|
||||
const real_t MAXIT = max_iter;
|
||||
const bool IT_MODE = iterative_mode;
|
||||
const bool CHANGE_BASIS = (d2q != nullptr);
|
||||
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ public:
|
||||
/// Set the absolute tolerance.
|
||||
void SetAbsTol(const real_t abs_tol_);
|
||||
/// Set the maximum number of iterations.
|
||||
void SetMaxIter(const int max_iter_);
|
||||
void SetMaxIter(const real_t max_iter_);
|
||||
/// Recompute operator and preconditioner (when coefficient or mesh changes).
|
||||
void Update();
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define MFEM_FACE_MAP_UTILS_HPP
|
||||
|
||||
#include "../../general/array.hpp"
|
||||
#include "../../general/backends.hpp"
|
||||
#include <utility> // std::pair
|
||||
#include <vector>
|
||||
|
||||
@@ -52,189 +51,6 @@ void FillFaceMap(const int n_face_dofs_per_component,
|
||||
void GetTensorFaceMap(const int dim, const int order, const int face_id,
|
||||
Array<int> &face_map);
|
||||
|
||||
/// @brief Given a face DOF index in native (counter-clockwise) ordering, return
|
||||
/// the corresponding DOF index in lexicographic ordering (for a quadrilateral
|
||||
/// element).
|
||||
MFEM_HOST_DEVICE
|
||||
inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
|
||||
{
|
||||
if (face_id==2 || face_id==3)
|
||||
{
|
||||
return size1d-1-i;
|
||||
}
|
||||
else
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index on a shared face, ordered lexicographically
|
||||
/// relative to element 1, return the corresponding face DOF index ordered
|
||||
/// lexicographically relative to element 2.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace2D(const int face_id1, const int face_id2,
|
||||
const int orientation, const int size1d,
|
||||
const int index)
|
||||
{
|
||||
int new_index;
|
||||
// Convert from element 1 lex ordering to native ordering
|
||||
if (face_id1 == 2 || face_id1 == 3)
|
||||
{
|
||||
new_index = size1d-1-index;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_index = index;
|
||||
}
|
||||
// Permute based on face orientations
|
||||
if (orientation == 1)
|
||||
{
|
||||
new_index = size1d-1-new_index;
|
||||
}
|
||||
// Covert to element 2 lex ordering
|
||||
return ToLexOrdering2D(face_id2, size1d, new_index);
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index in native (counter-clockwise) ordering, return
|
||||
/// the corresponding DOF index in lexicographic ordering (for a hexahedral
|
||||
/// element).
|
||||
MFEM_HOST_DEVICE
|
||||
inline int ToLexOrdering3D(const int face_id, const int size1d, const int i,
|
||||
const int j)
|
||||
{
|
||||
if (face_id==2 || face_id==1 || face_id==5)
|
||||
{
|
||||
return i + j*size1d;
|
||||
}
|
||||
else if (face_id==3 || face_id==4)
|
||||
{
|
||||
return (size1d-1-i) + j*size1d;
|
||||
}
|
||||
else // face_id==0
|
||||
{
|
||||
return i + (size1d-1-j)*size1d;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Given the index of a face DOF in lexicographic ordering relative
|
||||
/// element 1, permute the index so that it is lexicographically ordered
|
||||
/// relative to element 2.
|
||||
///
|
||||
/// The given face corresponds to local face index @a face_id1 relative to
|
||||
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace3D(const int face_id1, const int face_id2,
|
||||
const int orientation,
|
||||
const int size1d, const int index)
|
||||
{
|
||||
int i=0, j=0, new_i=0, new_j=0;
|
||||
i = index%size1d;
|
||||
j = index/size1d;
|
||||
// Convert from lex ordering
|
||||
if (face_id1==3 || face_id1==4)
|
||||
{
|
||||
i = size1d-1-i;
|
||||
}
|
||||
else if (face_id1==0)
|
||||
{
|
||||
j = size1d-1-j;
|
||||
}
|
||||
// Permute based on face orientations
|
||||
switch (orientation)
|
||||
{
|
||||
case 0:
|
||||
new_i = i;
|
||||
new_j = j;
|
||||
break;
|
||||
case 1:
|
||||
new_i = j;
|
||||
new_j = i;
|
||||
break;
|
||||
case 2:
|
||||
new_i = j;
|
||||
new_j = (size1d-1-i);
|
||||
break;
|
||||
case 3:
|
||||
new_i = (size1d-1-i);
|
||||
new_j = j;
|
||||
break;
|
||||
case 4:
|
||||
new_i = (size1d-1-i);
|
||||
new_j = (size1d-1-j);
|
||||
break;
|
||||
case 5:
|
||||
new_i = (size1d-1-j);
|
||||
new_j = (size1d-1-i);
|
||||
break;
|
||||
case 6:
|
||||
new_i = (size1d-1-j);
|
||||
new_j = i;
|
||||
break;
|
||||
case 7:
|
||||
new_i = i;
|
||||
new_j = (size1d-1-j);
|
||||
break;
|
||||
}
|
||||
return ToLexOrdering3D(face_id2, size1d, new_i, new_j);
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF (or quadrature) index ordered lexicographically
|
||||
/// relative to element 1, return the associated (i, j) coordinates.
|
||||
///
|
||||
/// The returned coordinates will be relative to element 1 or element 2
|
||||
/// according to the value of side (side == 0 corresponds element 1).
|
||||
MFEM_HOST_DEVICE
|
||||
inline void FaceIdxToVolIdx2D(const int qi, const int nq, const int face_id0,
|
||||
const int face_id1, const int side, int &i, int &j)
|
||||
{
|
||||
// Note: in 2D, a consistently ordered mesh will always have the element 2
|
||||
// face reversed relative to element 1, so orientation is determined entirely
|
||||
// by side. (In 3D, separate orientation information is needed).
|
||||
const int orientation = side;
|
||||
|
||||
const int face_id = (side == 0) ? face_id0 : face_id1;
|
||||
const int edge_idx = (side == 0) ? qi : PermuteFace2D(face_id0, face_id1,
|
||||
orientation, nq, qi);
|
||||
|
||||
const int level = (face_id == 0 || face_id == 3) ? 0 : (nq-1);
|
||||
const bool x_axis = (face_id == 0 || face_id == 2);
|
||||
|
||||
i = x_axis ? edge_idx : level;
|
||||
j = x_axis ? level : edge_idx;
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF (or quadrature) index ordered lexicographically
|
||||
/// relative to element 1, return the associated (i, j, k) coordinates.
|
||||
///
|
||||
/// The returned coordinates will be relative to element 1 or element 2
|
||||
/// according to the value of side (side == 0 corresponds element 1).
|
||||
MFEM_HOST_DEVICE
|
||||
inline void FaceIdxToVolIdx3D(const int index, const int size1d,
|
||||
const int face_id0, const int face_id1,
|
||||
const int side, const int orientation,
|
||||
int& i, int& j, int& k)
|
||||
{
|
||||
MFEM_VERIFY_KERNEL(face_id1 >= 0 || side == 0,
|
||||
"Accessing second side but face_id1 is not valid.");
|
||||
|
||||
const int face_id = (side == 0) ? face_id0 : face_id1;
|
||||
const int fidx = (side == 0) ? index
|
||||
: PermuteFace3D(face_id0, face_id1, orientation, size1d, index);
|
||||
|
||||
const bool xy_plane = (face_id == 0 || face_id == 5);
|
||||
const bool yz_plane = (face_id == 2 || face_id == 4);
|
||||
|
||||
const int level = (face_id == 0 || face_id == 1 || face_id == 4)
|
||||
? 0 : (size1d-1);
|
||||
|
||||
const int _i = fidx % size1d;
|
||||
const int _j = fidx / size1d;
|
||||
|
||||
k = xy_plane ? level : _j;
|
||||
j = yz_plane ? _i : xy_plane ? _j : level;
|
||||
i = yz_plane ? level : _i;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+1
-1
@@ -316,7 +316,7 @@ public:
|
||||
int GetDim() const { return dim; }
|
||||
|
||||
/** @brief Returns the vector dimension for vector-valued finite elements,
|
||||
which is also the dimension of the interpolation operation. */
|
||||
which is also the dimension of the interpolation operatrion. */
|
||||
int GetRangeDim() const { return vdim; }
|
||||
|
||||
/// Returns the dimension of the curl for vector-valued finite elements.
|
||||
|
||||
+41
-63
@@ -1321,9 +1321,9 @@ void GridFunction::ProjectVectorFieldOn(GridFunction &vec_field, int comp)
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::AccumulateAndCountDerivativeValues(
|
||||
int comp, int der_comp, GridFunction &der,
|
||||
Array<int> &zones_per_dof) const
|
||||
void GridFunction::AccumulateAndCountDerivativeValues(int comp, int der_comp,
|
||||
GridFunction &der,
|
||||
Array<int> &zones_per_dof)
|
||||
{
|
||||
FiniteElementSpace * der_fes = der.FESpace();
|
||||
ElementTransformation * transf;
|
||||
@@ -1374,8 +1374,7 @@ void GridFunction::AccumulateAndCountDerivativeValues(
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetDerivative(int comp, int der_comp,
|
||||
GridFunction &der) const
|
||||
void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
|
||||
{
|
||||
Array<int> overlap;
|
||||
AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap);
|
||||
@@ -2062,37 +2061,41 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
Coefficient *coeff[], VectorCoefficient *vcoeff, const Array<int> &attr,
|
||||
Array<int> &values_counter)
|
||||
{
|
||||
int i, j, fdof, d, ind, vdim;
|
||||
real_t val;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *transf;
|
||||
Array<int> vdofs;
|
||||
Vector vc;
|
||||
|
||||
values_counter.SetSize(Size());
|
||||
values_counter = 0;
|
||||
|
||||
const int vdim = fes->GetVDim();
|
||||
vdim = fes->GetVDim();
|
||||
|
||||
HostReadWrite();
|
||||
|
||||
for (int i = 0; i < fes->GetNBE(); i++)
|
||||
for (i = 0; i < fes->GetNBE(); i++)
|
||||
{
|
||||
if (attr[fes->GetBdrAttribute(i) - 1] == 0) { continue; }
|
||||
|
||||
const FiniteElement *fe = fes->GetBE(i);
|
||||
const int fdof = fe->GetDof();
|
||||
ElementTransformation *transf = fes->GetBdrElementTransformation(i);
|
||||
fe = fes->GetBE(i);
|
||||
fdof = fe->GetDof();
|
||||
transf = fes->GetBdrElementTransformation(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
fes->GetBdrElementVDofs(i, vdofs);
|
||||
|
||||
for (int j = 0; j < fdof; j++)
|
||||
for (j = 0; j < fdof; j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
transf->SetIntPoint(&ip);
|
||||
if (vcoeff) { vcoeff->Eval(vc, *transf, ip); }
|
||||
for (int d = 0; d < vdim; d++)
|
||||
for (d = 0; d < vdim; d++)
|
||||
{
|
||||
if (!vcoeff && !coeff[d]) { continue; }
|
||||
|
||||
real_t val = vcoeff ? vc(d) : coeff[d]->Eval(*transf, ip);
|
||||
int ind = vdofs[fdof*d+j];
|
||||
if ( ind < 0 )
|
||||
val = vcoeff ? vc(d) : coeff[d]->Eval(*transf, ip);
|
||||
if ( (ind = vdofs[fdof*d+j]) < 0 )
|
||||
{
|
||||
val = -val, ind = -1-ind;
|
||||
}
|
||||
@@ -2114,11 +2117,10 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
// iff A_ij != 0. It is sufficient to resolve just the first level of
|
||||
// dependency, since A is a projection matrix: A^n = A due to cR.cP = I.
|
||||
// Cases like these arise in 3D when boundary edges are constrained by
|
||||
// (depend on) internal faces/elements, or for internal boundaries in 2 or
|
||||
// 3D. We use the virtual method GetBoundaryClosure from NCMesh to resolve
|
||||
// the dependencies.
|
||||
if (fes->Nonconforming() && (fes->GetMesh()->Dimension() == 2 ||
|
||||
fes->GetMesh()->Dimension() == 3))
|
||||
// (depend on) internal faces/elements. We use the virtual method
|
||||
// GetBoundaryClosure from NCMesh to resolve the dependencies.
|
||||
|
||||
if (fes->Nonconforming() && fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
Vector vals;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
@@ -2126,19 +2128,26 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
Array<int> bdr_edges, bdr_vertices, bdr_faces;
|
||||
ncmesh->GetBoundaryClosure(attr, bdr_vertices, bdr_edges, bdr_faces);
|
||||
|
||||
auto mark_dofs = [&](ElementTransformation &transf, const FiniteElement &fe)
|
||||
for (i = 0; i < bdr_edges.Size(); i++)
|
||||
{
|
||||
int edge = bdr_edges[i];
|
||||
fes->GetEdgeVDofs(edge, vdofs);
|
||||
if (vdofs.Size() == 0) { continue; }
|
||||
|
||||
transf = mesh->GetEdgeTransformation(edge);
|
||||
transf->Attribute = -1; // TODO: set the boundary attribute
|
||||
fe = fes->GetEdgeElement(edge);
|
||||
if (!vcoeff)
|
||||
{
|
||||
vals.SetSize(fe.GetDof());
|
||||
for (int d = 0; d < vdim; d++)
|
||||
vals.SetSize(fe->GetDof());
|
||||
for (d = 0; d < vdim; d++)
|
||||
{
|
||||
if (!coeff[d]) { continue; }
|
||||
|
||||
fe.Project(*coeff[d], transf, vals);
|
||||
fe->Project(*coeff[d], *transf, vals);
|
||||
for (int k = 0; k < vals.Size(); k++)
|
||||
{
|
||||
const int ind = vdofs[d*vals.Size()+k];
|
||||
ind = vdofs[d*vals.Size()+k];
|
||||
if (++values_counter[ind] == 1)
|
||||
{
|
||||
(*this)(ind) = vals(k);
|
||||
@@ -2152,11 +2161,11 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
}
|
||||
else // vcoeff != NULL
|
||||
{
|
||||
vals.SetSize(vdim*fe.GetDof());
|
||||
fe.Project(*vcoeff, transf, vals);
|
||||
vals.SetSize(vdim*fe->GetDof());
|
||||
fe->Project(*vcoeff, *transf, vals);
|
||||
for (int k = 0; k < vals.Size(); k++)
|
||||
{
|
||||
const int ind = vdofs[k];
|
||||
ind = vdofs[k];
|
||||
if (++values_counter[ind] == 1)
|
||||
{
|
||||
(*this)(ind) = vals(k);
|
||||
@@ -2167,26 +2176,6 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (auto edge : bdr_edges)
|
||||
{
|
||||
fes->GetEdgeVDofs(edge, vdofs);
|
||||
if (vdofs.Size() == 0) { continue; }
|
||||
|
||||
ElementTransformation *transf = mesh->GetEdgeTransformation(edge);
|
||||
const FiniteElement *fe = fes->GetEdgeElement(edge);
|
||||
mark_dofs(*transf, *fe);
|
||||
}
|
||||
|
||||
for (auto face : bdr_faces)
|
||||
{
|
||||
fes->GetFaceVDofs(face, vdofs);
|
||||
if (vdofs.Size() == 0) { continue; }
|
||||
|
||||
ElementTransformation *transf = mesh->GetFaceTransformation(face);
|
||||
const FiniteElement *fe = fes->GetFaceElement(face);
|
||||
mark_dofs(*transf, *fe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2239,37 +2228,26 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
|
||||
if (fes->Nonconforming() && (fes->GetMesh()->Dimension() == 2 ||
|
||||
fes->GetMesh()->Dimension() == 3))
|
||||
if (fes->Nonconforming() && fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
NCMesh *ncmesh = mesh->ncmesh;
|
||||
Array<int> bdr_edges, bdr_vertices, bdr_faces;
|
||||
ncmesh->GetBoundaryClosure(bdr_attr, bdr_vertices, bdr_edges, bdr_faces);
|
||||
|
||||
for (auto edge : bdr_edges)
|
||||
for (int i = 0; i < bdr_edges.Size(); i++)
|
||||
{
|
||||
int edge = bdr_edges[i];
|
||||
fes->GetEdgeDofs(edge, dofs);
|
||||
if (dofs.Size() == 0) { continue; }
|
||||
|
||||
T = mesh->GetEdgeTransformation(edge);
|
||||
T->Attribute = -1; // TODO: set the boundary attribute
|
||||
fe = fes->GetEdgeElement(edge);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
|
||||
for (auto face : bdr_faces)
|
||||
{
|
||||
fes->GetFaceDofs(face, dofs);
|
||||
if (dofs.Size() == 0) { continue; }
|
||||
|
||||
T = mesh->GetFaceTransformation(face);
|
||||
fe = fes->GetFaceElement(face);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
accumulate_dofs(dofs, lvec, *this, values_counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -321,7 +321,7 @@ public:
|
||||
@param[out] der The resulting derivative (scalar function). The
|
||||
FiniteElementSpace of this function must be set
|
||||
before the call. */
|
||||
void GetDerivative(int comp, int der_comp, GridFunction &der) const;
|
||||
void GetDerivative(int comp, int der_comp, GridFunction &der);
|
||||
|
||||
real_t GetDivergence(ElementTransformation &tr) const;
|
||||
|
||||
@@ -443,7 +443,7 @@ protected:
|
||||
GetDerivative() method; see its documentation. */
|
||||
void AccumulateAndCountDerivativeValues(int comp, int der_comp,
|
||||
GridFunction &der,
|
||||
Array<int> &zones_per_dof) const;
|
||||
Array<int> &zones_per_dof);
|
||||
|
||||
void AccumulateAndCountBdrValues(Coefficient *coeff[],
|
||||
VectorCoefficient *vcoeff,
|
||||
|
||||
@@ -1352,85 +1352,6 @@ void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
Interpolate(field_in, field_out);
|
||||
}
|
||||
|
||||
GSOPGSLIB::GSOPGSLIB(Array<long long> &ids)
|
||||
{
|
||||
gsl_comm = new gslib::comm;
|
||||
cr = new gslib::crystal;
|
||||
#ifdef MFEM_USE_MPI
|
||||
int initialized;
|
||||
MPI_Initialized(&initialized);
|
||||
if (!initialized) { MPI_Init(NULL, NULL); }
|
||||
MPI_Comm comm = MPI_COMM_WORLD;
|
||||
comm_init(gsl_comm, comm);
|
||||
#else
|
||||
comm_init(gsl_comm, 0);
|
||||
#endif
|
||||
crystal_init(cr, gsl_comm);
|
||||
UpdateIdentifiers(ids);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
GSOPGSLIB::GSOPGSLIB(MPI_Comm comm_, Array<long long> &ids)
|
||||
: cr(NULL), gsl_comm(NULL)
|
||||
{
|
||||
gsl_comm = new gslib::comm;
|
||||
cr = new gslib::crystal;
|
||||
comm_init(gsl_comm, comm_);
|
||||
crystal_init(cr, gsl_comm);
|
||||
UpdateIdentifiers(ids);
|
||||
}
|
||||
#endif
|
||||
|
||||
GSOPGSLIB::~GSOPGSLIB()
|
||||
{
|
||||
crystal_free(cr);
|
||||
gslib_gs_free(gsl_data);
|
||||
comm_free(gsl_comm);
|
||||
delete gsl_comm;
|
||||
delete cr;
|
||||
}
|
||||
|
||||
void GSOPGSLIB::UpdateIdentifiers(const Array<long long> &ids)
|
||||
{
|
||||
long long minval = ids.Min();
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Allreduce(MPI_IN_PLACE, &minval, 1, MPI_LONG_LONG_INT,
|
||||
MPI_MIN, gsl_comm->c);
|
||||
#endif
|
||||
MFEM_VERIFY(minval >= 0, "Unique identifier cannot be negative.");
|
||||
if (gsl_data != NULL) { gslib_gs_free(gsl_data); }
|
||||
num_ids = ids.Size();
|
||||
gsl_data = gslib_gs_setup(ids.GetData(),
|
||||
ids.Size(),
|
||||
gsl_comm, 0,
|
||||
gslib::gs_crystal_router, 0);
|
||||
}
|
||||
|
||||
void GSOPGSLIB::GS(Vector &senddata, GSOp op)
|
||||
{
|
||||
MFEM_VERIFY(senddata.Size() == num_ids,
|
||||
"Incompatible setup and GOP operation.");
|
||||
if (op == GSOp::ADD)
|
||||
{
|
||||
gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_add,0,gsl_data,0);
|
||||
}
|
||||
else if (op == GSOp::MUL)
|
||||
{
|
||||
gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_mul,0,gsl_data,0);
|
||||
}
|
||||
else if (op == GSOp::MAX)
|
||||
{
|
||||
gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_max,0,gsl_data,0);
|
||||
}
|
||||
else if (op == GSOp::MIN)
|
||||
{
|
||||
gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_min,0,gsl_data,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Invalid GSOp operation.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
+1
-62
@@ -23,16 +23,13 @@ struct comm;
|
||||
struct findpts_data_2;
|
||||
struct findpts_data_3;
|
||||
struct crystal;
|
||||
struct gs_data;
|
||||
}
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** \brief FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary
|
||||
* collection of points.
|
||||
*
|
||||
* There are three key functions in FindPointsGSLIB:
|
||||
* collection of points. There are three key functions in FindPointsGSLIB:
|
||||
*
|
||||
* 1. Setup - constructs the internal data structures of gslib.
|
||||
*
|
||||
@@ -229,7 +226,6 @@ public:
|
||||
|
||||
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
|
||||
overlapping grids.
|
||||
|
||||
The parameters in this class are the same as FindPointsGSLIB with the
|
||||
difference of additional inputs required to account for more than 1 mesh. */
|
||||
class OversetFindPointsGSLIB : public FindPointsGSLIB
|
||||
@@ -294,63 +290,6 @@ public:
|
||||
using FindPointsGSLIB::Interpolate;
|
||||
};
|
||||
|
||||
/** \brief Class for gather-scatter (gs) operations on Vectors based on
|
||||
corresponding global identifiers.
|
||||
|
||||
This functionality is useful for gs-ops on DOF values across processor
|
||||
boundary, where the global identifier would be the corresponding true DOF
|
||||
index. Operations currently supported are min, max, sum, and multiplication.
|
||||
Note: identifier 0 does not participate in the gather-scatter operation and
|
||||
a given identifier can be included multiple times on a given rank.
|
||||
For example, consider a vector, v:
|
||||
- v = [0.3, 0.4, 0.25, 0.7] on rank1,
|
||||
- v = [0.6, 0.1] on rank 2,
|
||||
- v = [-0.2, 0.3, 0.7, 0.] on rank 3.
|
||||
|
||||
Consider a corresponding Array<int>, a:
|
||||
- a = [1, 2, 3, 1] on rank 1,
|
||||
- a = [3, 2] on rank 2,
|
||||
- a = [1, 2, 0, 3] on rank 3.
|
||||
|
||||
A gather-scatter "minimum" operation, done as follows:
|
||||
GSOPGSLIB gs = GSOPGSLIB(MPI_COMM_WORLD, a);
|
||||
gs.GS(v, GSOp::MIN);
|
||||
would return into v:
|
||||
- v = [-0.2, 0.1, 0., -0.2] on rank 1,
|
||||
- v = [0., 0.1] on rank 2,
|
||||
- v = [-0.2, 0.1, 0.7, 0.] on rank 3,
|
||||
where the values have been compared across all processors based on the
|
||||
integer identifier. */
|
||||
class GSOPGSLIB
|
||||
{
|
||||
protected:
|
||||
struct gslib::crystal *cr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm; // gslib's internal data
|
||||
struct gslib::gs_data *gsl_data = NULL;
|
||||
int num_ids;
|
||||
|
||||
public:
|
||||
GSOPGSLIB(Array<long long> &ids);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
GSOPGSLIB(MPI_Comm comm_, Array<long long> &ids);
|
||||
#endif
|
||||
|
||||
virtual ~GSOPGSLIB();
|
||||
|
||||
/// Supported operation types. See class description.
|
||||
enum GSOp {ADD, MUL, MIN, MAX};
|
||||
|
||||
/// Update the identifiers used for the gather-scatter operator.
|
||||
/// Same @a ids get grouped together and id == 0 does not participate.
|
||||
/// See class description.
|
||||
void UpdateIdentifiers(const Array<long long> &ids);
|
||||
|
||||
/// Gather-Scatter operation on senddata. Must match length of unique
|
||||
/// identifiers used in the constructor. See class description.
|
||||
void GS(Vector &senddata, GSOp op);
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_GSLIB
|
||||
|
||||
+6
-5
@@ -18,6 +18,7 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
void HyperbolicFormIntegrator::AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
const Vector &elfun,
|
||||
@@ -28,7 +29,7 @@ void HyperbolicFormIntegrator::AssembleElementVector(const FiniteElement &el,
|
||||
const int dof = el.GetDof();
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
// Local storage for element integration
|
||||
// Local storages for element integration
|
||||
|
||||
// shape function value at an integration point
|
||||
Vector shape(dof);
|
||||
@@ -61,7 +62,7 @@ void HyperbolicFormIntegrator::AssembleElementVector(const FiniteElement &el,
|
||||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
// loop over integration points
|
||||
// loop over interation points
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
@@ -91,7 +92,7 @@ void HyperbolicFormIntegrator::AssembleFaceVector(
|
||||
const int dof2 = el2.GetDof();
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
// Local storage for element integration
|
||||
// Local storages for element integration
|
||||
|
||||
// shape function value at an integration point - first elem
|
||||
Vector shape1(dof1);
|
||||
@@ -121,7 +122,7 @@ void HyperbolicFormIntegrator::AssembleFaceVector(
|
||||
DenseMatrix elvect2_mat(elvect.GetData() + dof1 * num_equations, dof2,
|
||||
num_equations);
|
||||
|
||||
// Obtain integration rule. If integration is rule is given, then use it.
|
||||
// obtain integration rule. If integration is rule is given, then use it.
|
||||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (!ir)
|
||||
@@ -148,7 +149,7 @@ void HyperbolicFormIntegrator::AssembleFaceVector(
|
||||
if (nor.Size() == 1) // if 1D, use 1 or -1.
|
||||
{
|
||||
// This assume the 1D integration point is in (0,1). This may not work
|
||||
// if this changes.
|
||||
// if this chages.
|
||||
nor(0) = (Tr.GetElement1IntPoint().x - 0.5) * 2.0;
|
||||
}
|
||||
else
|
||||
|
||||
+34
-27
@@ -18,36 +18,43 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// This file contains general hyperbolic conservation element/face form
|
||||
// integrators. HyperbolicFormIntegrator and RiemannSolver are defined.
|
||||
// MFEM Hyperbolic Conservation Laws
|
||||
//
|
||||
// HyperbolicFormIntegrator is a NonlinearFormIntegrator that implements
|
||||
// element weak divergence and interface flux
|
||||
// Description:
|
||||
//
|
||||
// ∫_T F(u):∇v, -∫_e F̂(u)⋅[[v]]
|
||||
// This file contains general hyperbolic conservation element/face form
|
||||
// integrators.
|
||||
//
|
||||
// Here, T is an element, e is an edge, and [[⋅]] is jump. This form integrator
|
||||
// is coupled with RiemannSolver that implements the numerical flux F̂. For
|
||||
// RiemannSolver, the Rusanov flux, also known as local Lax-Friedrichs flux, is
|
||||
// provided.
|
||||
// HyperbolicFormIntegrator and RiemannSolver are defined.
|
||||
// HyperbolicFormIntegrator is a NonlinearFormIntegrator that implements
|
||||
// element weak divergence and interface flux
|
||||
//
|
||||
// To implement a specific hyperbolic conservation laws, users can create
|
||||
// derived classes from FluxFunction with overloaded ComputeFlux. One can
|
||||
// optionally overload ComputeFluxDotN to avoid creating dense matrix when
|
||||
// computing normal flux. Several example equations are also defined including:
|
||||
// advection, Burgers', shallow water, and Euler equations. Users can control
|
||||
// the quadrature rule by either providing the integration rule, or integration
|
||||
// order offset. Integration will use 2*p + IntOrderOffset order quadrature
|
||||
// rule.
|
||||
// ∫_T F(u):∇v, -∫_e F̂(u)⋅[[v]]
|
||||
//
|
||||
// At each call of HyperbolicFormIntegrator::AssembleElementVector
|
||||
// HyperbolicFormIntegrator::AssembleFaceVector, the maximum characteristic
|
||||
// speed will be updated. This will not be reinitialized automatically. To
|
||||
// reinitialize, use HyperbolicFormIntegrator::ResetMaxCharSpeed. See, ex18.hpp.
|
||||
// Here, T is an element, e is an edge, and [[⋅]] is jump. This form
|
||||
// integrator is coupled with RiemannSolver that implements the numerical
|
||||
// flux F̂. For RiemannSolver, the Rusanov flux, also known as local
|
||||
// Lax-Friedrichs flux, is provided.
|
||||
//
|
||||
// To implement a specific hyperbolic conservation laws, users can create
|
||||
// derived classes from FluxFunction with overloaded ComputeFlux. One can
|
||||
// optionally overload ComputeFluxDotN to avoid creating dense matrix when
|
||||
// computing normal flux. Several example equations are also defined
|
||||
// including: advection, Burgers', shallow water, and Euler equations. Users
|
||||
// can control the quadrature rule by either providing the integration rule,
|
||||
// or integration order offset. Integration will use 2*p + IntOrderOffset
|
||||
// order quadrature rule.
|
||||
//
|
||||
// At each call of HyperbolicFormIntegrator::AssembleElementVector
|
||||
// HyperbolicFormIntegrator::AssembleFaceVector, the maximum characteristic
|
||||
// speed will be updated. This will not be reinitialized automatically.
|
||||
// To reinitialize, use HyperbolicFormIntegrator::ResetMaxCharSpeed. See,
|
||||
// ex18.hpp.
|
||||
//
|
||||
// Note: To avoid communication overhead, we update the maximum
|
||||
// characteristic speed within each process. Use a proper MPI routine to
|
||||
// gather the information.
|
||||
//
|
||||
// Note: To avoid communication overhead, we update the maximum characteristic
|
||||
// speed within each MPI process only. Use the appropriate MPI routine to gather
|
||||
// the information.
|
||||
|
||||
/**
|
||||
* @brief Abstract class for hyperbolic flux for a system of hyperbolic
|
||||
@@ -81,7 +88,7 @@ public:
|
||||
virtual real_t ComputeFlux(const Vector &state, ElementTransformation &Tr,
|
||||
DenseMatrix &flux) const = 0;
|
||||
/**
|
||||
* @brief Compute normal flux. Optionally overloaded in the
|
||||
* @brief Compute normal flux. Optionally overloadded in the
|
||||
* derived class to avoid creating full dense matrix for flux.
|
||||
*
|
||||
* @param[in] state state at the current integration point
|
||||
@@ -161,13 +168,13 @@ protected:
|
||||
class HyperbolicFormIntegrator : public NonlinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
// The maximum characteristic speed, updated during element/face vector assembly
|
||||
// The maximum characterstic speed, updated during element/face vector assembly
|
||||
real_t max_char_speed;
|
||||
const RiemannSolver &rsolver; // Numerical flux that maps F(u±,x) to hat(F)
|
||||
const FluxFunction &fluxFunction;
|
||||
const int IntOrderOffset; // integration order offset, 2*p + IntOrderOffset.
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
// Local storage for element integration
|
||||
// Local storages for element integration
|
||||
Vector shape; // shape function value at an integration point
|
||||
Vector state; // state value at an integration point
|
||||
DenseMatrix flux; // flux value at an integration point
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -563,7 +563,7 @@ void DiffusionIntegrator::AssemblePatchMatrix_fullQuadrature(
|
||||
cdofs.SetSize(maxw[0], maxw[1], maxw[2]);
|
||||
|
||||
// Compute sparsity of the sparse matrix
|
||||
smati = Memory<int>(ndof+1);
|
||||
smati = new int[ndof+1];
|
||||
smati[0] = 0;
|
||||
|
||||
for (int dof_j=0; dof_j<ndof; ++dof_j)
|
||||
@@ -586,8 +586,8 @@ void DiffusionIntegrator::AssemblePatchMatrix_fullQuadrature(
|
||||
nnz += ndd;
|
||||
}
|
||||
|
||||
smatj = Memory<int>(nnz);
|
||||
smata = Memory<real_t>(nnz);
|
||||
smatj = new int[nnz];
|
||||
smata = new real_t[nnz];
|
||||
|
||||
for (int i=0; i<nnz; ++i)
|
||||
{
|
||||
@@ -973,7 +973,7 @@ void DiffusionIntegrator::AssemblePatchMatrix_reducedQuadrature(
|
||||
cdofs.SetSize(maxw[0], maxw[1], maxw[2]);
|
||||
|
||||
// Compute sparsity of the sparse matrix
|
||||
smati = Memory<int>(ndof+1);
|
||||
smati = new int[ndof+1];
|
||||
smati[0] = 0;
|
||||
|
||||
for (int dof_j=0; dof_j<ndof; ++dof_j)
|
||||
@@ -996,8 +996,8 @@ void DiffusionIntegrator::AssemblePatchMatrix_reducedQuadrature(
|
||||
nnz += ndd;
|
||||
}
|
||||
|
||||
smatj = Memory<int>(nnz);
|
||||
smata = Memory<real_t>(nnz);
|
||||
smatj = new int[nnz];
|
||||
smata = new real_t[nnz];
|
||||
|
||||
for (int i=0; i<nnz; ++i)
|
||||
{
|
||||
|
||||
@@ -157,7 +157,7 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
static constexpr int aSize = aUpper-aLower;
|
||||
static constexpr bool isComponent = (i_block >= 0);
|
||||
|
||||
// Assuming all elements are the same
|
||||
//Assuming all elements are the same
|
||||
const auto &ir = QVec.GetIntRule(0);
|
||||
const QuadratureInterpolator *E_To_Q_Map = fespace.GetQuadratureInterpolator(
|
||||
ir);
|
||||
@@ -180,7 +180,7 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
auto invJ = inv(make_tensor<d, d>(
|
||||
[&](int i, int j) { return J(p, i, j, e); }));
|
||||
tensor<real_t, aSize, d> gradx;
|
||||
// load grad(x) into gradx
|
||||
//load grad(x) into gradx
|
||||
if (isComponent)
|
||||
{
|
||||
for (int i = 0; i < d; i++)
|
||||
@@ -198,11 +198,11 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
}
|
||||
}
|
||||
}
|
||||
// compute divergence
|
||||
//compute divergence
|
||||
real_t div = 0.;
|
||||
for (int i = aLower; i < aUpper; i++)
|
||||
{
|
||||
// take size of gradx into account
|
||||
//take size of gradx into account
|
||||
const int iIndex = isComponent ? 0 : i;
|
||||
div += gradx(iIndex,i);
|
||||
}
|
||||
@@ -211,11 +211,11 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
{
|
||||
for (int q = qLower; q < qUpper; q++)
|
||||
{
|
||||
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
// this contraction could be made slightly cheaper using Voigt
|
||||
// notation, but repeated entries are summed for simplicity.
|
||||
//compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
//this contraction could be made slightly cheaper using Voigt
|
||||
//notation, but repeated entries are summed for simplicity.
|
||||
real_t contraction = 0.;
|
||||
// not sure how to combine cases
|
||||
//not sure how to combine cases
|
||||
if (isComponent)
|
||||
{
|
||||
for (int a = 0; a < d; a++)
|
||||
@@ -276,7 +276,7 @@ void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
const CoefficientVector &mu, const GeometricFactors &geom,
|
||||
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
|
||||
{
|
||||
// Assuming all elements are the same
|
||||
//Assuming all elements are the same
|
||||
const auto &ir = QVec.GetIntRule(0);
|
||||
static constexpr int d = dim;
|
||||
const int numPoints = ir.GetNPoints();
|
||||
@@ -299,9 +299,9 @@ void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
{
|
||||
for (int q = 0; q < d; q++)
|
||||
{
|
||||
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
// this contraction could be made slightly cheaper using Voigt
|
||||
// notation, but repeated entries are summed for simplicity.
|
||||
//compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
//this contraction could be made slightly cheaper using Voigt
|
||||
//notation, but repeated entries are summed for simplicity.
|
||||
real_t contraction = 0.;
|
||||
for (int a = 0; a < d; a++)
|
||||
{
|
||||
@@ -321,7 +321,7 @@ void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
}
|
||||
});
|
||||
|
||||
// Reduce quadrature function to an E-Vector
|
||||
//Reduce quadrature function to an E-Vector
|
||||
const auto QRead = Reshape(QVec.Read(), numPoints, d, d, d, numEls);
|
||||
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
|
||||
const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
|
||||
@@ -348,7 +348,7 @@ void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
});
|
||||
}
|
||||
|
||||
// Templated implementation of ElasticityAssembleEA.
|
||||
//Templated implementation of ElasticityAssembleEA.
|
||||
template<int dim>
|
||||
void ElasticityAssembleEA_(const int i_block,
|
||||
const int j_block,
|
||||
@@ -360,7 +360,7 @@ void ElasticityAssembleEA_(const int i_block,
|
||||
const DofToQuad &maps,
|
||||
Vector &emat)
|
||||
{
|
||||
// Assuming all elements are the same
|
||||
//Assuming all elements are the same
|
||||
static constexpr int d = dim;
|
||||
const int numPoints = ir.GetNPoints();
|
||||
const int numEls = lambda.Size()/numPoints;
|
||||
@@ -386,7 +386,7 @@ void ElasticityAssembleEA_(const int i_block,
|
||||
{
|
||||
for (int m = 0; m < d; m++)
|
||||
{
|
||||
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
//compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
real_t contraction = 0.;
|
||||
for (int a = 0; a < d; a++)
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ void MomentFittingIntRules::InitVolume(int order, Coefficient& levelset,
|
||||
}
|
||||
}
|
||||
|
||||
// assemble the matrix
|
||||
// assamble the matrix
|
||||
DenseMatrix Mat(nBasisVolume, ir.GetNPoints());
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
{
|
||||
@@ -118,7 +118,7 @@ void MomentFittingIntRules::InitVolume(int order, Coefficient& levelset,
|
||||
Mat.SetCol(ip, shape);
|
||||
}
|
||||
|
||||
// compute the SVD for the matrix
|
||||
// compute the svd for the matrix
|
||||
VolumeSVD = new DenseMatrixSVD(Mat, 'A', 'A');
|
||||
VolumeSVD->Eval(Mat);
|
||||
}
|
||||
@@ -1239,7 +1239,7 @@ void MomentFittingIntRules::OrthoBasis2D(const IntegrationPoint& ip,
|
||||
|
||||
shape.SetSize(nBasis, 2);
|
||||
|
||||
// evaluate basis in the point
|
||||
// evaluate basis inthe point
|
||||
DenseMatrix preshape(nBasis, 2);
|
||||
DivFreeBasis2D(ip, shape);
|
||||
|
||||
@@ -1597,6 +1597,6 @@ void MomentFittingIntRules::GetSurfaceWeights(ElementTransformation& Tr,
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_LAPACK
|
||||
#endif //MFEM_USE_LAPACK
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -291,7 +291,7 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
const auto ltdof_ldof = HypreRead(R->GetMemoryJ());
|
||||
|
||||
// Go from E-vector format directly to T-vector format
|
||||
mfem::hypre_forall(ntdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
MFEM_HYPRE_FORALL(i, ntdofs,
|
||||
{
|
||||
const int j = d_offsets[ltdof_ldof[i]];
|
||||
for (int c = 0; c < sdim; ++c)
|
||||
|
||||
+15
-15
@@ -269,13 +269,13 @@ void BatchedLOR_H1::Assemble3D()
|
||||
real_t vx[8], vy[8], vz[8];
|
||||
LORVertexCoordinates3D<ORDER>(X, iel_ho, kx, ky, kz, vx, vy, vz);
|
||||
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iqz=0; iqz<2; ++iqz)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
const real_t x = iqx;
|
||||
@@ -307,21 +307,21 @@ void BatchedLOR_H1::Assemble3D()
|
||||
}
|
||||
}
|
||||
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int jz=0; jz<2; ++jz)
|
||||
{
|
||||
// Note loop starts at iz=jz here, taking advantage of
|
||||
// symmetries.
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iz=jz; iz<2; ++iz)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iqz=0; iqz<2; ++iqz)
|
||||
{
|
||||
const real_t mq = const_mq ? MQ(0,0,0,0) : MQ(kx+iqx, ky+iqy, kz+iqz, iel_ho);
|
||||
@@ -356,10 +356,10 @@ void BatchedLOR_H1::Assemble3D()
|
||||
real_t wdetJ = Q(6,iqz,iqy,iqx);
|
||||
mass_A(iqy,iz,jz,iqx) += mq*wdetJ*biz*bjz;
|
||||
}
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int jy=0; jy<2; ++jy)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iy=0; iy<2; ++iy)
|
||||
{
|
||||
const real_t biy = (iy == iqy) ? 1.0 : 0.0;
|
||||
@@ -382,16 +382,16 @@ void BatchedLOR_H1::Assemble3D()
|
||||
}
|
||||
}
|
||||
}
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int jy=0; jy<2; ++jy)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int jx=0; jx<2; ++jx)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int iy=0; iy<2; ++iy)
|
||||
{
|
||||
// MFEM_UNROLL(2)
|
||||
//MFEM_UNROLL(2)
|
||||
for (int ix=0; ix<2; ++ix)
|
||||
{
|
||||
const real_t bix = (ix == iqx) ? 1.0 : 0.0;
|
||||
@@ -431,7 +431,7 @@ void BatchedLOR_H1::Assemble3D()
|
||||
// Assemble the local matrix into the macro-element sparse matrix
|
||||
// in a format similar to coordinate format. The (I,J) arrays
|
||||
// are implicit (not stored explicitly).
|
||||
// MFEM_UNROLL(8)
|
||||
//MFEM_UNROLL(8)
|
||||
for (int ii_loc=0; ii_loc<nv; ++ii_loc)
|
||||
{
|
||||
const int ix = ii_loc%2;
|
||||
|
||||
@@ -1,828 +0,0 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "normal_deriv_restriction.hpp"
|
||||
#include "fespace.hpp"
|
||||
#include "pgridfunc.hpp"
|
||||
#include "fe/face_map_utils.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Compute the face index to volume index map "face_to_vol" in 2D
|
||||
static void NormalDerivativeSetupFaceIndexMap2D(
|
||||
int nf, int d, const Array<int>& face_to_elem, Array<int>& face_to_vol)
|
||||
{
|
||||
const auto f2e = Reshape(face_to_elem.HostRead(), 2, 2, nf);
|
||||
auto f2v = Reshape(face_to_vol.HostWrite(), d, 2, nf);
|
||||
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
const int fid0 = f2e(0, 1, f);
|
||||
const int fid1 = f2e(1, 1, f);
|
||||
for (int side = 0; side < 2; ++side)
|
||||
{
|
||||
const int el = f2e(side, 0, f);
|
||||
|
||||
if (el < 0)
|
||||
{
|
||||
for (int p = 0; p < d; ++p)
|
||||
{
|
||||
f2v(p, side, f) = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int p = 0; p < d; ++p)
|
||||
{
|
||||
int i, j;
|
||||
internal::FaceIdxToVolIdx2D(p, d, fid0, fid1, side, i, j);
|
||||
|
||||
f2v(p, side, f) = i + d * j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the face index to volume index map "face_to_vol" in 3D
|
||||
static void NormalDerivativeSetupFaceIndexMap3D(
|
||||
int nf, int d, const Array<int>& face_to_elem, Array<int>& face_to_vol)
|
||||
{
|
||||
const auto f2e = Reshape(face_to_elem.HostRead(), 2, 3, nf);
|
||||
auto f2v = Reshape(face_to_vol.HostWrite(), d*d, 2, nf);
|
||||
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
const int fid0 = f2e(0, 1, f);
|
||||
const int fid1 = f2e(1, 1, f);
|
||||
for (int side = 0; side < 2; ++side)
|
||||
{
|
||||
const int el = f2e(side, 0, f);
|
||||
const int orientation = f2e(side, 2, f);
|
||||
|
||||
if (el < 0)
|
||||
{
|
||||
for (int p = 0; p < d*d; ++p)
|
||||
{
|
||||
f2v(p, side, f) = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int p = 0; p < d*d; ++p)
|
||||
{
|
||||
int i, j, k; // 3D lexicographic index of quad point p
|
||||
internal::FaceIdxToVolIdx3D(p, d, fid0, fid1, side, orientation, i, j, k);
|
||||
|
||||
f2v(p, side, f) = i + d * (j + d * k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
L2NormalDerivativeFaceRestriction::L2NormalDerivativeFaceRestriction(
|
||||
const FiniteElementSpace &fes_,
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType face_type_)
|
||||
: fes(fes_),
|
||||
face_type(face_type_),
|
||||
dim(fes.GetMesh()->Dimension()),
|
||||
nf(fes.GetNFbyType(face_type)),
|
||||
ne(fes.GetNE())
|
||||
{
|
||||
MFEM_VERIFY(f_ordering == ElementDofOrdering::LEXICOGRAPHIC,
|
||||
"Non-lexicographic ordering not currently supported in "
|
||||
"L2NormalDerivativeFaceRestriction.");
|
||||
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
|
||||
const FiniteElement &fe = *fes.GetFE(0);
|
||||
const int d = fe.GetDofToQuad(fe.GetNodes(), DofToQuad::TENSOR).ndof;
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
// (el0, el1, fid0, fid1)
|
||||
face_to_elem.SetSize(nf * 4);
|
||||
face_to_vol.SetSize(2 * nf * d);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
// (el0, el1, fid0, fid1, or0, or1)
|
||||
face_to_elem.SetSize(nf * 6);
|
||||
face_to_vol.SetSize(2 * nf * d * d);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported dimension.");
|
||||
}
|
||||
auto f2e = Reshape(face_to_elem.HostWrite(), 2, (dim == 2) ? 2 : 3, nf);
|
||||
|
||||
// Populate the face_to_elem array. The elem_indicator will be used to count
|
||||
// the number of elements that are adjacent to faces of the given type.
|
||||
Array<int> elem_indicator(ne);
|
||||
elem_indicator = 0;
|
||||
|
||||
int f_ind = 0;
|
||||
for (int f = 0; f < fes.GetNF(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
|
||||
|
||||
if (face.IsOfFaceType(face_type))
|
||||
{
|
||||
f2e(0, 0, f_ind) = face.element[0].index;
|
||||
f2e(0, 1, f_ind) = face.element[0].local_face_id;
|
||||
if (dim == 3)
|
||||
{
|
||||
f2e(0, 2, f_ind) = face.element[0].orientation;
|
||||
}
|
||||
|
||||
elem_indicator[face.element[0].index] = 1;
|
||||
|
||||
if (face_type == FaceType::Interior)
|
||||
{
|
||||
const int el_idx_1 = face.element[1].index;
|
||||
if (face.IsShared())
|
||||
{
|
||||
// Indicate shared face by index >= ne
|
||||
f2e(1, 0, f_ind) = ne + el_idx_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Face is not shared
|
||||
f2e(1, 0, f_ind) = el_idx_1;
|
||||
elem_indicator[el_idx_1] = 1;
|
||||
}
|
||||
f2e(1, 1, f_ind) = face.element[1].local_face_id;
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
f2e(1, 2, f_ind) = face.element[1].orientation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
f2e(1, 0, f_ind) = -1;
|
||||
f2e(1, 1, f_ind) = -1;
|
||||
|
||||
if (dim == 3)
|
||||
{
|
||||
f2e(1, 2, f_ind) = -1;
|
||||
}
|
||||
}
|
||||
|
||||
f_ind++;
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate face to vol map
|
||||
if (dim == 2)
|
||||
{
|
||||
NormalDerivativeSetupFaceIndexMap2D(nf, d, face_to_elem, face_to_vol);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
NormalDerivativeSetupFaceIndexMap3D(nf, d, face_to_elem, face_to_vol);
|
||||
}
|
||||
|
||||
// Number of elements adjacent to faces of face_type
|
||||
ne_type = elem_indicator.Sum();
|
||||
|
||||
// In 2D: (el, f0,f1,f2,f3, s0,s1,s2,s3)
|
||||
// In 3D: (el, f0,f1,f2,f3,f4,f5, s0,s1,s2,s3,s4,s5)
|
||||
const int elem_data_sz = (dim == 2) ? 9 : 13;
|
||||
|
||||
elem_to_face.SetSize(elem_data_sz * ne_type);
|
||||
elem_to_face = -1;
|
||||
|
||||
auto e2f = Reshape(elem_to_face.HostWrite(), elem_data_sz, ne_type);
|
||||
elem_indicator.PartialSum();
|
||||
|
||||
const int nsides = (face_type == FaceType::Interior) ? 2 : 1;
|
||||
const int side_begin = (dim == 2) ? 5 : 7;
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
for (int side = 0; side < nsides; ++side)
|
||||
{
|
||||
const int el = f2e(side, 0, f);
|
||||
// Skip shared faces
|
||||
if (el < ne)
|
||||
{
|
||||
const int face_id = f2e(side, 1, f);
|
||||
|
||||
const int e = elem_indicator[el] - 1;
|
||||
e2f(0, e) = el;
|
||||
e2f(1 + face_id, e) = f;
|
||||
e2f(side_begin + face_id, e) = side;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void L2NormalDerivativeFaceRestriction::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (nf == 0) { return; }
|
||||
switch (dim)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
const int d1d = fes.GetElementOrder(0) + 1;
|
||||
switch (d1d)
|
||||
{
|
||||
case 1: Mult2D<1>(x, y); break;
|
||||
case 2: Mult2D<2>(x, y); break;
|
||||
case 3: Mult2D<3>(x, y); break;
|
||||
case 4: Mult2D<4>(x, y); break;
|
||||
case 5: Mult2D<5>(x, y); break;
|
||||
case 6: Mult2D<6>(x, y); break;
|
||||
case 7: Mult2D<7>(x, y); break;
|
||||
case 8: Mult2D<8>(x, y); break;
|
||||
default: Mult2D(x, y); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
const int d1d = fes.GetElementOrder(0) + 1;
|
||||
switch (d1d)
|
||||
{
|
||||
case 1: Mult3D<1>(x, y); break;
|
||||
case 2: Mult3D<2>(x, y); break;
|
||||
case 3: Mult3D<3>(x, y); break;
|
||||
case 4: Mult3D<4>(x, y); break;
|
||||
case 5: Mult3D<5>(x, y); break;
|
||||
case 6: Mult3D<6>(x, y); break;
|
||||
case 7: Mult3D<7>(x, y); break;
|
||||
case 8: Mult3D<8>(x, y); break;
|
||||
default: Mult3D(x, y); break; // fallback
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: MFEM_ABORT("Dimension not supported."); break;
|
||||
}
|
||||
}
|
||||
|
||||
void L2NormalDerivativeFaceRestriction::AddMultTranspose(
|
||||
const Vector &x, Vector &y, const real_t a) const
|
||||
{
|
||||
if (nf == 0) { return; }
|
||||
switch (dim)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
const int d1d = fes.GetElementOrder(0) + 1;
|
||||
switch (d1d)
|
||||
{
|
||||
case 1: AddMultTranspose2D<1>(x, y, a); break;
|
||||
case 2: AddMultTranspose2D<2>(x, y, a); break;
|
||||
case 3: AddMultTranspose2D<3>(x, y, a); break;
|
||||
case 4: AddMultTranspose2D<4>(x, y, a); break;
|
||||
case 5: AddMultTranspose2D<5>(x, y, a); break;
|
||||
case 6: AddMultTranspose2D<6>(x, y, a); break;
|
||||
case 7: AddMultTranspose2D<7>(x, y, a); break;
|
||||
case 8: AddMultTranspose2D<8>(x, y, a); break;
|
||||
default: AddMultTranspose2D(x, y, a); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
const int d1d = fes.GetElementOrder(0) + 1;
|
||||
switch (d1d)
|
||||
{
|
||||
case 1: AddMultTranspose3D<1>(x, y, a); break;
|
||||
case 2: AddMultTranspose3D<2>(x, y, a); break;
|
||||
case 3: AddMultTranspose3D<3>(x, y, a); break;
|
||||
case 4: AddMultTranspose3D<4>(x, y, a); break;
|
||||
case 5: AddMultTranspose3D<5>(x, y, a); break;
|
||||
case 6: AddMultTranspose3D<6>(x, y, a); break;
|
||||
case 7: AddMultTranspose3D<7>(x, y, a); break;
|
||||
case 8: AddMultTranspose3D<8>(x, y, a); break;
|
||||
default: AddMultTranspose3D(x, y, a); break; // fallback
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: MFEM_ABORT("Not yet implemented"); break;
|
||||
}
|
||||
}
|
||||
|
||||
template <int T_D1D>
|
||||
void L2NormalDerivativeFaceRestriction::Mult2D(const Vector &x, Vector &y) const
|
||||
{
|
||||
const int vd = fes.GetVDim();
|
||||
const bool t = fes.GetOrdering() == Ordering::byVDIM;
|
||||
const int num_elem = ne;
|
||||
|
||||
const FiniteElement &fe = *fes.GetFE(0);
|
||||
const DofToQuad &maps = fe.GetDofToQuad(fe.GetNodes(), DofToQuad::TENSOR);
|
||||
|
||||
const int q = maps.nqpt;
|
||||
const int d = maps.ndof;
|
||||
|
||||
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, face_type);
|
||||
const int ne_shared = face_nbr_data.Size() / d / d / vd;
|
||||
|
||||
MFEM_VERIFY(q == d, "");
|
||||
MFEM_VERIFY(T_D1D == d || T_D1D == 0, "");
|
||||
|
||||
// derivative of 1D basis function
|
||||
const auto G_ = Reshape(maps.G.Read(), q, d);
|
||||
// (el0, el1, fid0, fid1)
|
||||
const auto f2e = Reshape(face_to_elem.Read(), 2, 2, nf);
|
||||
|
||||
const auto f2v = Reshape(face_to_vol.Read(), q, 2, nf);
|
||||
|
||||
// if byvdim, d_x has shape (vdim, nddof, nddof, ne)
|
||||
// otherwise, d_x has shape (nddof, nddof, ne, vdim)
|
||||
const auto d_x = Reshape(x.Read(), t?vd:d, d, t?d:ne, t?ne:vd);
|
||||
const auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:d, d, t?d:ne_shared, t?ne_shared:vd);
|
||||
auto d_y = Reshape(y.Write(), q, vd, 2, nf);
|
||||
|
||||
mfem::forall_2D(nf, 2, q, [=] MFEM_HOST_DEVICE (int f) -> void
|
||||
{
|
||||
constexpr int MD = (T_D1D) ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t G_s[MD*MD];
|
||||
DeviceMatrix G(G_s, q, d);
|
||||
|
||||
MFEM_SHARED int E[2];
|
||||
MFEM_SHARED int FID[2];
|
||||
MFEM_SHARED int F2V[2][MD];
|
||||
|
||||
if (MFEM_THREAD_ID(x) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j, y, d)
|
||||
{
|
||||
for (int i = 0; i < q; ++i)
|
||||
{
|
||||
G(i, j) = G_(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(side, x, 2)
|
||||
{
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
E[side] = f2e(side, 0, f);
|
||||
FID[side] = f2e(side, 1, f);
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(j, y, d)
|
||||
{
|
||||
F2V[side][j] = f2v(j, side, f);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(side, x, 2)
|
||||
{
|
||||
const int el = E[side];
|
||||
const bool shared = (el >= num_elem);
|
||||
const auto &d_x_e = shared ? d_x_shared : d_x;
|
||||
const int el_idx = shared ? el - num_elem : el;
|
||||
|
||||
const int face_id = FID[side];
|
||||
|
||||
MFEM_FOREACH_THREAD(p, y, q)
|
||||
{
|
||||
if (el < 0)
|
||||
{
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(p, c, side, f) = 0.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int ij = F2V[side][p];
|
||||
const int i = ij % q;
|
||||
const int j = ij / q;
|
||||
|
||||
for (int c=0; c < vd; ++c)
|
||||
{
|
||||
real_t grad_n = 0;
|
||||
for (int kk=0; kk < d; ++kk)
|
||||
{
|
||||
const int k = (face_id == 0 || face_id == 2) ? i : kk;
|
||||
const int l = (face_id == 0 || face_id == 2) ? kk : j;
|
||||
const real_t g = (face_id == 0 || face_id == 2) ? G(j,l) : G(i,k);
|
||||
grad_n += g * d_x_e(t?c:k, t?k:l, t?l:el_idx, t?el_idx:c);
|
||||
}
|
||||
d_y(p, c, side, f) = grad_n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_D1D>
|
||||
void L2NormalDerivativeFaceRestriction::Mult3D(const Vector &x, Vector &y) const
|
||||
{
|
||||
const int vd = fes.GetVDim();
|
||||
const bool t = fes.GetOrdering() == Ordering::byVDIM;
|
||||
const int num_elem = ne;
|
||||
|
||||
const FiniteElement &fe = *fes.GetFE(0);
|
||||
const DofToQuad &maps = fe.GetDofToQuad(fe.GetNodes(), DofToQuad::TENSOR);
|
||||
|
||||
const int q = maps.nqpt;
|
||||
const int d = maps.ndof;
|
||||
const int q2d = q * q;
|
||||
|
||||
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, face_type);
|
||||
const int ne_shared = face_nbr_data.Size() / d / d / d / vd;
|
||||
|
||||
MFEM_VERIFY(q == d, "");
|
||||
MFEM_VERIFY(T_D1D == d || T_D1D == 0, "");
|
||||
|
||||
const auto G_ = Reshape(maps.G.Read(), q, d);
|
||||
// (el0, el1, fid0, fid1, or0, or1)
|
||||
const auto f2e = Reshape(face_to_elem.Read(), 2, 3, nf);
|
||||
const auto f2v = Reshape(face_to_vol.Read(), q2d, 2, nf);
|
||||
|
||||
// t ? (vdim, d, d, d, ne) : (d, d, d, ne, vdim)
|
||||
const auto d_x = Reshape(x.Read(), t?vd:d, d, d, t?d:ne, t?ne:vd);
|
||||
const auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:d, d, d, t?d:ne_shared, t?ne_shared:vd);
|
||||
auto d_y = Reshape(y.Write(), q2d, vd, 2, nf);
|
||||
|
||||
mfem::forall_2D(nf, q2d, 2, [=] MFEM_HOST_DEVICE (int f) -> void
|
||||
{
|
||||
static constexpr int MD = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t G_s[MD*MD];
|
||||
DeviceMatrix G(G_s, d, q);
|
||||
|
||||
MFEM_SHARED int E[2];
|
||||
MFEM_SHARED int FID[2];
|
||||
MFEM_SHARED int F2V[2][MD*MD];
|
||||
|
||||
// Load G matrix into shared memory
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j, x, d*q)
|
||||
{
|
||||
const int p = j % q;
|
||||
const int k = j / q;
|
||||
G(k, p) = G_(p, k);
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(side, y, 2)
|
||||
{
|
||||
if (MFEM_THREAD_ID(x) == 0)
|
||||
{
|
||||
E[side] = f2e(side, 0, f);
|
||||
FID[side] = f2e(side, 1, f);
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j, x, q2d)
|
||||
{
|
||||
F2V[side][j] = f2v(j, side, f);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(side, y, 2)
|
||||
{
|
||||
const int el = E[side];
|
||||
const bool shared = (el >= num_elem);
|
||||
const auto &d_x_e = shared ? d_x_shared : d_x;
|
||||
const int el_idx = shared ? el - num_elem : el;
|
||||
|
||||
const int face_id = FID[side];
|
||||
|
||||
// Is this face parallel to the x-y plane in reference coordinates?
|
||||
const bool xy_plane = (face_id == 0 || face_id == 5);
|
||||
const bool xz_plane = (face_id == 1 || face_id == 3);
|
||||
const bool yz_plane = (face_id == 2 || face_id == 4);
|
||||
|
||||
MFEM_FOREACH_THREAD(p, x, q2d)
|
||||
{
|
||||
if (el_idx < 0)
|
||||
{
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(p, c, side, f) = 0.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int ijk = F2V[side][p];
|
||||
const int k = ijk / q2d;
|
||||
const int i = ijk % q;
|
||||
const int j = (ijk - q2d*k) / q;
|
||||
|
||||
// the fixed 1D index of the normal component of the face
|
||||
// quadrature point
|
||||
const int g_row = yz_plane ? i : xz_plane ? j : k;
|
||||
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
real_t grad_n = 0.0;
|
||||
|
||||
for (int kk = 0; kk < d; ++kk)
|
||||
{
|
||||
// (l, m, n) 3D lexicographic index of interior points used
|
||||
// in evaluating normal derivatives
|
||||
const int l = yz_plane ? kk : i;
|
||||
const int m = xz_plane ? kk : j;
|
||||
const int n = xy_plane ? kk : k;
|
||||
|
||||
const real_t g = G(kk, g_row);
|
||||
|
||||
grad_n += g * d_x_e(t?c:l, t?l:m, t?m:n, t?n:el_idx, t?el_idx:c);
|
||||
}
|
||||
d_y(p, c, side, f) = grad_n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_D1D>
|
||||
void L2NormalDerivativeFaceRestriction::AddMultTranspose2D(
|
||||
const Vector &y, Vector &x, const real_t a) const
|
||||
{
|
||||
const int vd = fes.GetVDim();
|
||||
const bool t = fes.GetOrdering() == Ordering::byVDIM;
|
||||
|
||||
const FiniteElement &fe = *fes.GetFE(0);
|
||||
const DofToQuad &maps = fe.GetDofToQuad(fe.GetNodes(), DofToQuad::TENSOR);
|
||||
|
||||
const int q = maps.nqpt;
|
||||
const int d = maps.ndof;
|
||||
|
||||
// derivative of 1D basis function
|
||||
auto G_ = Reshape(maps.G.Read(), q, d);
|
||||
|
||||
// entries of e2f: (el,f0,f1,f2,f3,s0,s1,s2,s3)
|
||||
auto e2f = Reshape(elem_to_face.Read(), 9, ne_type);
|
||||
|
||||
auto f2v = Reshape(face_to_vol.Read(), d, 2, nf);
|
||||
|
||||
// if byvdim, d_x has shape (vdim, nddof, nddof, ne)
|
||||
// otherwise, d_x has shape (nddof, nddof, ne, vdim)
|
||||
auto d_x = Reshape(x.ReadWrite(), t?vd:d, d, t?d:ne, t?ne:vd);
|
||||
auto d_y = Reshape(y.Read(), q, vd, 2, nf);
|
||||
|
||||
mfem::forall_2D(ne_type, d, d, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
constexpr int MD = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
|
||||
MFEM_SHARED real_t y_s[MD];
|
||||
MFEM_SHARED int pp[MD];
|
||||
MFEM_SHARED int jj;
|
||||
if (MFEM_THREAD_ID(x) == 0 && MFEM_THREAD_ID(y) == 0) { jj = 0; }
|
||||
|
||||
MFEM_SHARED real_t BG[MD*MD];
|
||||
DeviceMatrix G(BG, q, d);
|
||||
|
||||
MFEM_SHARED real_t x_s[MD*MD];
|
||||
DeviceMatrix xx(x_s, d, d);
|
||||
|
||||
MFEM_SHARED int el; // global element index
|
||||
MFEM_SHARED int faces[4];
|
||||
MFEM_SHARED int sides[4];
|
||||
|
||||
MFEM_FOREACH_THREAD(i,x,d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(p,y,q)
|
||||
{
|
||||
G(p,i) = a * G_(p,i);
|
||||
xx(p,i) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
if (MFEM_THREAD_ID(x) == 0)
|
||||
{
|
||||
el = e2f(0, e);
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(i, x, 4)
|
||||
{
|
||||
faces[i] = e2f(1 + i, e);
|
||||
sides[i] = e2f(5 + i, e);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int face_id=0; face_id < 4; ++face_id)
|
||||
{
|
||||
const int f = faces[face_id];
|
||||
|
||||
if (f < 0) { continue; }
|
||||
|
||||
const int side = sides[face_id];
|
||||
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(p,x,d)
|
||||
{
|
||||
y_s[p] = d_y(p, 0, side, f);
|
||||
|
||||
const int ij = f2v(p, side, f);
|
||||
const int i = ij % q;
|
||||
const int j = ij / q;
|
||||
|
||||
pp[(face_id == 0 || face_id == 2) ? i : j] = p;
|
||||
if (MFEM_THREAD_ID(x) == 0)
|
||||
{
|
||||
jj = (face_id == 0 || face_id == 2) ? j : i;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(k,x,d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(l,y,d)
|
||||
{
|
||||
const int p = (face_id == 0 || face_id == 2) ? pp[k] : pp[l];
|
||||
const int kk = (face_id == 0 || face_id == 2) ? l : k;
|
||||
const real_t g = G(jj, kk);
|
||||
xx(k,l) += g * y_s[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(k,x,d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(l,y,d)
|
||||
{
|
||||
const int c = 0;
|
||||
d_x(t?c:k, t?k:l, t?l:el, t?el:c) += xx(k,l);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_D1D>
|
||||
void L2NormalDerivativeFaceRestriction::AddMultTranspose3D(
|
||||
const Vector &y, Vector &x, const real_t a) const
|
||||
{
|
||||
const int vd = fes.GetVDim();
|
||||
const bool t = fes.GetOrdering() == Ordering::byVDIM;
|
||||
|
||||
MFEM_VERIFY(vd == 1, "vdim > 1 not supported.");
|
||||
|
||||
const FiniteElement &fe = *fes.GetFE(0);
|
||||
const DofToQuad &maps = fe.GetDofToQuad(fe.GetNodes(), DofToQuad::TENSOR);
|
||||
|
||||
const int q = maps.nqpt;
|
||||
const int d = maps.ndof;
|
||||
const int q2d = q * q;
|
||||
|
||||
MFEM_VERIFY(q == d, "");
|
||||
MFEM_VERIFY(T_D1D == d || T_D1D == 0, "");
|
||||
|
||||
auto G_ = Reshape(maps.G.Read(), q, d);
|
||||
|
||||
// (el, f0,f1,f2,f3,f4,f5, s0,s1,s2,s3,s4,s5)
|
||||
auto e2f = Reshape(elem_to_face.Read(), 13, ne_type);
|
||||
|
||||
auto f2v = Reshape(face_to_vol.Read(), q2d, 2, nf);
|
||||
|
||||
auto d_x = Reshape(x.ReadWrite(), t?vd:d, d, d, t?d:ne, t?ne:vd);
|
||||
const auto d_y = Reshape(y.Read(), q2d, vd, 2, nf);
|
||||
|
||||
mfem::forall_2D(ne_type, q, q, [=] MFEM_HOST_DEVICE (int e) -> void
|
||||
{
|
||||
static constexpr int MD = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
|
||||
MFEM_SHARED int pp[MD][MD];
|
||||
MFEM_SHARED real_t y_s[MD*MD];
|
||||
MFEM_SHARED int jj;
|
||||
if (MFEM_THREAD_ID(x) == 0 && MFEM_THREAD_ID(y) == 0) { jj = 0; }
|
||||
|
||||
MFEM_SHARED real_t xx_s[MD*MD*MD];
|
||||
auto xx = Reshape(xx_s, d, d, d);
|
||||
|
||||
MFEM_SHARED real_t G_s[MD*MD];
|
||||
DeviceMatrix G(G_s, q, d);
|
||||
|
||||
MFEM_SHARED int el;
|
||||
MFEM_SHARED int faces[6];
|
||||
MFEM_SHARED int sides[6];
|
||||
|
||||
// Load G into shared memory
|
||||
MFEM_FOREACH_THREAD(j, x, d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i, y, q)
|
||||
{
|
||||
G(i, j) = a * G_(i, j);
|
||||
G(i, j) = a * G_(i, j);
|
||||
G(i, j) = a * G_(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
if (MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
if (MFEM_THREAD_ID(x) == 0)
|
||||
{
|
||||
el = e2f(0, e); // global element index
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(i, x, 6)
|
||||
{
|
||||
faces[i] = e2f(1 + i, e);
|
||||
sides[i] = e2f(7 + i, e);
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(k, x, d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j, y, d)
|
||||
{
|
||||
for (int i = 0; i < d; ++i)
|
||||
{
|
||||
xx(i, j, k) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int face_id = 0; face_id < 6; ++face_id)
|
||||
{
|
||||
const int f = faces[face_id];
|
||||
|
||||
if (f < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const int side = sides[face_id];
|
||||
|
||||
// is this face parallel to the x-y plane in reference coordinates?
|
||||
const bool xy_plane = (face_id == 0 || face_id == 5);
|
||||
const bool xz_plane = (face_id == 1 || face_id == 3);
|
||||
|
||||
MFEM_FOREACH_THREAD(p1, x, q)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(p2, y, q)
|
||||
{
|
||||
const int p = p1 + q * p2;
|
||||
y_s[p] = d_y(p, 0, side, f);
|
||||
|
||||
const int ijk = f2v(p, side, f);
|
||||
const int k = ijk / q2d;
|
||||
const int i = ijk % q;
|
||||
const int j = (ijk - q2d*k) / q;
|
||||
|
||||
pp[(xy_plane || xz_plane) ? i : j][(xy_plane) ? j : k] = p;
|
||||
if (MFEM_THREAD_ID(x) == 0 && MFEM_THREAD_ID(y) == 0)
|
||||
{
|
||||
jj = (xy_plane) ? k : (xz_plane) ? j : i;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(n, x, d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(m, y, d)
|
||||
{
|
||||
for (int l = 0; l < d; ++l)
|
||||
{
|
||||
const int p = (xy_plane) ? pp[l][m] : (xz_plane) ? pp[l][n] : pp[m][n];
|
||||
const int kk = (xy_plane) ? n : (xz_plane) ? m : l;
|
||||
const real_t g = G(jj, kk);
|
||||
xx(l, m, n) += g * y_s[p];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// map back to global array
|
||||
MFEM_FOREACH_THREAD(n, x, d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(m, y, d)
|
||||
{
|
||||
for (int l = 0; l < d; ++l)
|
||||
{
|
||||
const int c = 0;
|
||||
d_x(t?c:l, t?l:m, t?m:n, t?n:el, t?el:c) += xx(l, m, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_NORMAL_DERIV_RESTRICTION
|
||||
#define MFEM_NORMAL_DERIV_RESTRICTION
|
||||
|
||||
#include "../mesh/mesh.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class FiniteElementSpace;
|
||||
enum class ElementDofOrdering;
|
||||
|
||||
/// @brief Class to compute face normal derivatives (in reference coordinate) of
|
||||
/// an L2 grid function (used internally by L2FaceRestriction).
|
||||
class L2NormalDerivativeFaceRestriction
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace &fes; ///< The L2 finite element space.
|
||||
const FaceType face_type; ///< Face type: either boundary or interior.
|
||||
const int dim; ///< Dimension of the mesh.
|
||||
const int nf; ///< Number of faces of the given @a face_type.
|
||||
const int ne; ///< Number of elements.
|
||||
int ne_type; ///< Number of elements with faces of type face type
|
||||
|
||||
Array<int> face_to_elem; ///< Face-wise information array.
|
||||
Array<int> elem_to_face; ///< Element-wise information array.
|
||||
Array<int> face_to_vol; ///< maps face index to volume index
|
||||
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
/// @param[in] fes_ The associated FiniteElementSpace (should be L2/DG).
|
||||
/// @param[in] f_ordering Request a specific face dof ordering. Currently
|
||||
/// only ElementDofOrdering::LEXICOGRAPHIC is supported.
|
||||
/// @param[in] face_type_ Type of faces to compute restriction (interior or boundary).
|
||||
L2NormalDerivativeFaceRestriction(const FiniteElementSpace &fes_,
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType face_type_);
|
||||
|
||||
/// @brief Computes the normal derivatives on the @a face_type faces of the mesh.
|
||||
/// @param[in] x The L-vector degrees of freedom.
|
||||
/// @param[out] y The face E(like)-vector degrees of freedom of the format
|
||||
/// (face_dofs x vdim x 2 x nf) where nf is the number of faces of type @a
|
||||
/// face_type. The face_dofs are ordered according to @a f_ordering specified
|
||||
/// in the constructor.
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
/// @brief Computes the transpose of the action of Mult(), accumulating into
|
||||
/// @a y with coefficient @a a.
|
||||
/// @param x Face E-vector layout (face_dofs x vdim x 2 x nf).
|
||||
/// @param y L-vector layout.
|
||||
/// @param a Optional coefficient (y = y + a*R^t*x)
|
||||
void AddMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const;
|
||||
|
||||
/// @name Internal compute kernels. Public because of nvcc restriction.
|
||||
///@{
|
||||
|
||||
template <int T_D1D = 0>
|
||||
void Mult2D(const Vector &x, Vector &y) const;
|
||||
|
||||
template <int T_D1D = 0>
|
||||
void AddMultTranspose2D(const Vector &x, Vector &y, const real_t a) const;
|
||||
|
||||
template <int T_D1D = 0>
|
||||
void Mult3D(const Vector &x, Vector &y) const;
|
||||
|
||||
template <int T_D1D = 0>
|
||||
void AddMultTranspose3D(const Vector &x, Vector &y, const real_t a) const;
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MFEM_RESTRICTION
|
||||
@@ -368,72 +368,6 @@ const
|
||||
y.Add(a, Ytmp);
|
||||
}
|
||||
|
||||
real_t ParBilinearForm::ParInnerProduct(const ParGridFunction &x,
|
||||
const ParGridFunction &y) const
|
||||
{
|
||||
MFEM_ASSERT(mat != NULL, "local matrix must be assembled");
|
||||
|
||||
real_t loc = InnerProduct(x, y);
|
||||
real_t glob = 0.;
|
||||
|
||||
MPI_Allreduce(&loc, &glob, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
|
||||
pfes->GetComm());
|
||||
|
||||
return glob;
|
||||
}
|
||||
|
||||
real_t ParBilinearForm::TrueInnerProduct(const ParGridFunction &x,
|
||||
const ParGridFunction &y) const
|
||||
{
|
||||
MFEM_ASSERT(x.ParFESpace() == pfes, "the parallel spaces must match");
|
||||
MFEM_ASSERT(y.ParFESpace() == pfes, "the parallel spaces must match");
|
||||
|
||||
HypreParVector *x_p = x.ParallelProject();
|
||||
HypreParVector *y_p = y.ParallelProject();
|
||||
|
||||
real_t res = TrueInnerProduct(*x_p, *y_p);
|
||||
|
||||
delete x_p;
|
||||
delete y_p;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
real_t ParBilinearForm::TrueInnerProduct(HypreParVector &x,
|
||||
HypreParVector &y) const
|
||||
{
|
||||
MFEM_VERIFY(p_mat.Ptr() != NULL, "parallel matrix must be assembled");
|
||||
|
||||
if (p_mat->GetType() != Operator::Hypre_ParCSR)
|
||||
{
|
||||
return TrueInnerProduct((const Vector&)x, (const Vector&)y);
|
||||
}
|
||||
|
||||
HypreParVector *Ax = new HypreParVector(pfes);
|
||||
HypreParMatrix *A = p_mat.As<HypreParMatrix>();
|
||||
|
||||
A->Mult(x, *Ax);
|
||||
|
||||
real_t res = mfem::InnerProduct(y, *Ax);
|
||||
|
||||
delete Ax;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
real_t ParBilinearForm::TrueInnerProduct(const Vector &x,
|
||||
const Vector &y) const
|
||||
{
|
||||
MFEM_VERIFY(p_mat.Ptr() != NULL, "parallel matrix must be assembled");
|
||||
|
||||
Vector Ax(pfes->GetTrueVSize());
|
||||
p_mat->Mult(x, Ax);
|
||||
|
||||
real_t res = mfem::InnerProduct(pfes->GetComm(), y, Ax);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void ParBilinearForm::FormLinearSystem(
|
||||
const Array<int> &ess_tdof_list, Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B, int copy_interior)
|
||||
|
||||
@@ -173,37 +173,6 @@ public:
|
||||
vectors on the true dofs. */
|
||||
void TrueAddMult(const Vector &x, Vector &y, const real_t a = 1.0) const;
|
||||
|
||||
/// Compute $ y^T M x $
|
||||
/** @warning The calculation is performed on local dofs, assuming that
|
||||
the local vectors are consistent with the prolongations of the true
|
||||
vectors (see ParGridFunction::Distribute()). If this is not the case,
|
||||
use TrueInnerProduct(const ParGridFunction &, const ParGridFunction &)
|
||||
instead.
|
||||
@note It is assumed that the local matrix is assembled and it has
|
||||
not been replaced by the parallel matrix through FormSystemMatrix().
|
||||
@see TrueInnerProduct(const ParGridFunction&, const ParGridFunction&) */
|
||||
real_t ParInnerProduct(const ParGridFunction &x,
|
||||
const ParGridFunction &y) const;
|
||||
|
||||
/// Compute $ y^T M x $ on true dofs (grid function version)
|
||||
/** @note The ParGridFunction%s are restricted to the true-vectors for
|
||||
for calculation.
|
||||
@note It is assumed that the parallel system matrix is assembled,
|
||||
see FormSystemMatrix().
|
||||
@see ParInnerProduct(const ParGridFunction&, const ParGridFunction&) */
|
||||
real_t TrueInnerProduct(const ParGridFunction &x,
|
||||
const ParGridFunction &y) const;
|
||||
|
||||
/// Compute $ y^T M x $ on true dofs (Hypre vector version)
|
||||
/** @note It is assumed that the parallel system matrix is assembled,
|
||||
see FormSystemMatrix(). */
|
||||
real_t TrueInnerProduct(HypreParVector &x, HypreParVector &y) const;
|
||||
|
||||
/// Compute $ y^T M x $ on true dofs (true-vector version)
|
||||
/** @note It is assumed that the parallel system matrix is assembled,
|
||||
see FormSystemMatrix(). */
|
||||
real_t TrueInnerProduct(const Vector &x, const Vector &y) const;
|
||||
|
||||
/// Return the parallel FE space associated with the ParBilinearForm.
|
||||
ParFiniteElementSpace *ParFESpace() const { return pfes; }
|
||||
|
||||
|
||||
+7
-7
@@ -861,17 +861,17 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
|
||||
}
|
||||
}
|
||||
|
||||
HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
|
||||
real_t *d_diag = Memory<real_t>(ltdof);
|
||||
HYPRE_Int *i_diag = new HYPRE_Int[ldof+1];
|
||||
HYPRE_Int *j_diag = new HYPRE_Int[ltdof];
|
||||
real_t *d_diag = new real_t[ltdof];
|
||||
int diag_counter;
|
||||
|
||||
HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_offd = Memory<HYPRE_Int>(nnz_offd);
|
||||
real_t *d_offd = Memory<real_t>(nnz_offd);
|
||||
HYPRE_Int *i_offd = new HYPRE_Int[ldof+1];
|
||||
HYPRE_Int *j_offd = new HYPRE_Int[nnz_offd];
|
||||
real_t *d_offd = new real_t[nnz_offd];
|
||||
int offd_counter;
|
||||
|
||||
HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(ldof-ltdof);
|
||||
HYPRE_BigInt *cmap = new HYPRE_BigInt[ldof-ltdof];
|
||||
|
||||
HYPRE_BigInt *col_starts = GetTrueDofOffsets();
|
||||
HYPRE_BigInt *row_starts = GetDofOffsets();
|
||||
|
||||
+5
-7
@@ -249,8 +249,6 @@ void ParGridFunction::ExchangeFaceNbrData()
|
||||
auto send_data_ptr = mpi_gpu_aware ? send_data.Read() : send_data.HostRead();
|
||||
auto face_nbr_data_ptr = mpi_gpu_aware ? face_nbr_data.Write() :
|
||||
face_nbr_data.HostWrite();
|
||||
// Wait for the kernel to be done since it updates what's sent and it may be async
|
||||
if (mpi_gpu_aware) { MFEM_STREAM_SYNC; }
|
||||
for (int fn = 0; fn < num_face_nbrs; fn++)
|
||||
{
|
||||
int nbr_rank = pmesh->GetFaceNbrRank(fn);
|
||||
@@ -520,7 +518,7 @@ void ParGridFunction::CountElementsPerVDof(Array<int> &elem_per_vdof) const
|
||||
}
|
||||
|
||||
void ParGridFunction::GetDerivative(int comp, int der_comp,
|
||||
ParGridFunction &der) const
|
||||
ParGridFunction &der)
|
||||
{
|
||||
Array<int> overlap;
|
||||
AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap);
|
||||
@@ -715,10 +713,10 @@ void ParGridFunction::ProjectBdrCoefficient(
|
||||
}
|
||||
}
|
||||
}
|
||||
gcomm.Bcast<int>(values_counter.HostReadWrite());
|
||||
for (int i = 0; i < values_counter.Size(); i++)
|
||||
{
|
||||
MFEM_ASSERT(bool(values_counter[i]) == bool(ess_vdofs_marker[i]),
|
||||
MFEM_ASSERT(pfes->GetLocalTDofNumber(i) == -1 ||
|
||||
bool(values_counter[i]) == bool(ess_vdofs_marker[i]),
|
||||
"internal error");
|
||||
}
|
||||
#endif
|
||||
@@ -755,10 +753,10 @@ void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
#ifdef MFEM_DEBUG
|
||||
Array<int> ess_vdofs_marker;
|
||||
pfes->GetEssentialVDofs(bdr_attr, ess_vdofs_marker);
|
||||
gcomm.Bcast<int>(values_counter.HostReadWrite());
|
||||
for (int i = 0; i < values_counter.Size(); i++)
|
||||
{
|
||||
MFEM_ASSERT(bool(values_counter[i]) == bool(ess_vdofs_marker[i]),
|
||||
MFEM_ASSERT(pfes->GetLocalTDofNumber(i) == -1 ||
|
||||
bool(values_counter[i]) == bool(ess_vdofs_marker[i]),
|
||||
"internal error: " << pfes->GetLocalTDofNumber(i) << ' ' << bool(
|
||||
values_counter[i]));
|
||||
}
|
||||
|
||||
+2
-1
@@ -231,7 +231,7 @@ public:
|
||||
void CountElementsPerVDof(Array<int> &elem_per_vdof) const override;
|
||||
|
||||
/// Parallel version of GridFunction::GetDerivative(); see its documentation.
|
||||
void GetDerivative(int comp, int der_comp, ParGridFunction &der) const;
|
||||
void GetDerivative(int comp, int der_comp, ParGridFunction &der);
|
||||
|
||||
/** Sets the output vector @a dof_vals to the values of the degrees of
|
||||
freedom of element @a el. If @a el is greater than or equal to the number
|
||||
@@ -262,6 +262,7 @@ public:
|
||||
const Array<int> &attr) override
|
||||
{ ProjectBdrCoefficient(coeff, NULL, attr); }
|
||||
|
||||
// Only the values in the master are guaranteed to be correct!
|
||||
void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr) override;
|
||||
|
||||
|
||||
+31
-17
@@ -18,7 +18,6 @@
|
||||
#include "pgridfunc.hpp"
|
||||
#include "pfespace.hpp"
|
||||
#include "fespace.hpp"
|
||||
#include "fe/face_map_utils.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
@@ -278,22 +277,21 @@ void ParNCH1FaceRestriction::ComputeGatherIndices(
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &pfes_,
|
||||
ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
|
||||
ElementDofOrdering f_ordering,
|
||||
FaceType type,
|
||||
L2FaceValues m,
|
||||
bool build)
|
||||
: L2FaceRestriction(pfes_, f_ordering, type, m, false),
|
||||
pfes(pfes_)
|
||||
: L2FaceRestriction(fes, f_ordering, type, m, false)
|
||||
{
|
||||
if (!build) { return; }
|
||||
if (nf==0) { return; }
|
||||
|
||||
CheckFESpace();
|
||||
CheckFESpace(f_ordering);
|
||||
|
||||
ComputeScatterIndicesAndOffsets();
|
||||
ComputeScatterIndicesAndOffsets(f_ordering, type);
|
||||
|
||||
ComputeGatherIndices();
|
||||
ComputeGatherIndices(f_ordering, type);
|
||||
}
|
||||
|
||||
ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
|
||||
@@ -309,8 +307,14 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
MFEM_ASSERT(
|
||||
m == L2FaceValues::DoubleValued,
|
||||
"This method should be called when m == L2FaceValues::DoubleValued.");
|
||||
|
||||
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
|
||||
const ParFiniteElementSpace &pfes =
|
||||
static_cast<const ParFiniteElementSpace&>(this->fes);
|
||||
ParGridFunction x_gf;
|
||||
x_gf.MakeRef(const_cast<ParFiniteElementSpace*>(&pfes),
|
||||
const_cast<Vector&>(x), 0);
|
||||
// Face-neighbor information is only needed for interior faces. For boundary
|
||||
// faces, no communication is required.
|
||||
if (type == FaceType::Interior) { x_gf.ExchangeFaceNbrData(); }
|
||||
|
||||
// Early return only after calling ParGridFunction::ExchangeFaceNbrData,
|
||||
// otherwise MPI communication can hang.
|
||||
@@ -325,7 +329,7 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
auto d_indices1 = scatter_indices1.Read();
|
||||
auto d_indices2 = scatter_indices2.Read();
|
||||
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
auto d_x_shared = Reshape(x_gf.FaceNbrData().Read(),
|
||||
t?vd:nsdofs, t?nsdofs:vd);
|
||||
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
|
||||
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
@@ -563,9 +567,13 @@ void ParL2FaceRestriction::FillJAndData(const Vector &ea_data,
|
||||
});
|
||||
}
|
||||
|
||||
void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
const ParFiniteElementSpace &pfes =
|
||||
static_cast<const ParFiniteElementSpace&>(this->fes);
|
||||
|
||||
// Initialization of the offsets
|
||||
for (int i = 0; i <= ndofs; ++i)
|
||||
@@ -614,7 +622,9 @@ void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
|
||||
|
||||
void ParL2FaceRestriction::ComputeGatherIndices()
|
||||
void ParL2FaceRestriction::ComputeGatherIndices(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
|
||||
@@ -656,11 +666,11 @@ ParNCL2FaceRestriction::ParNCL2FaceRestriction(const ParFiniteElementSpace &fes,
|
||||
if (nf==0) { return; }
|
||||
x_interp.UseDevice(true);
|
||||
|
||||
CheckFESpace();
|
||||
CheckFESpace(f_ordering);
|
||||
|
||||
ComputeScatterIndicesAndOffsets();
|
||||
ComputeScatterIndicesAndOffsets(f_ordering, type);
|
||||
|
||||
ComputeGatherIndices();
|
||||
ComputeGatherIndices(f_ordering, type);
|
||||
}
|
||||
|
||||
void ParNCL2FaceRestriction::SingleValuedNonconformingMult(
|
||||
@@ -969,7 +979,9 @@ void ParNCL2FaceRestriction::FillJAndData(const Vector &ea_data,
|
||||
MFEM_ABORT("Not yet implemented.");
|
||||
}
|
||||
|
||||
void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
|
||||
@@ -1052,7 +1064,9 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void ParNCL2FaceRestriction::ComputeGatherIndices()
|
||||
void ParNCL2FaceRestriction::ComputeGatherIndices(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
|
||||
|
||||
+22
-8
@@ -139,11 +139,9 @@ public: // For nvcc
|
||||
class ParL2FaceRestriction : virtual public L2FaceRestriction
|
||||
{
|
||||
protected:
|
||||
const ParFiniteElementSpace &pfes;
|
||||
|
||||
/** @brief Constructs an ParL2FaceRestriction.
|
||||
|
||||
@param[in] pfes_ The ParFiniteElementSpace on which this operates
|
||||
@param[in] fes The ParFiniteElementSpace on which this operates
|
||||
@param[in] f_ordering Request a specific face dof ordering
|
||||
@param[in] type Request internal or boundary faces dofs
|
||||
@param[in] m Request the face dofs for elem1, or both elem1 and
|
||||
@@ -151,7 +149,7 @@ protected:
|
||||
@param[in] build Request the ParL2FaceRestriction to compute the
|
||||
scatter/gather indices. False should only be used
|
||||
when inheriting from ParL2FaceRestriction. */
|
||||
ParL2FaceRestriction(const ParFiniteElementSpace &pfes_,
|
||||
ParL2FaceRestriction(const ParFiniteElementSpace& fes,
|
||||
ElementDofOrdering f_ordering,
|
||||
FaceType type,
|
||||
L2FaceValues m,
|
||||
@@ -231,14 +229,22 @@ public:
|
||||
private:
|
||||
/** @brief Compute the scatter indices: L-vector to E-vector, and the offsets
|
||||
for the gathering: E-vector to L-vector.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeScatterIndicesAndOffsets();
|
||||
void ComputeScatterIndicesAndOffsets(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
/** @brief Compute the gather indices: E-vector to L-vector.
|
||||
|
||||
Note: Requires the gather offsets to be computed.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeGatherIndices();
|
||||
void ComputeGatherIndices(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
public:
|
||||
/** @brief Scatter the degrees of freedom, i.e. goes from L-Vector to
|
||||
@@ -375,14 +381,22 @@ private:
|
||||
/** @brief Compute the scatter indices: L-vector to E-vector, the offsets
|
||||
for the gathering: E-vector to L-vector, and the interpolators from
|
||||
coarse to fine face for master non-comforming faces.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeScatterIndicesAndOffsets();
|
||||
void ComputeScatterIndicesAndOffsets(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
/** @brief Compute the gather indices: E-vector to L-vector.
|
||||
|
||||
Note: Requires the gather offsets to be computed.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeGatherIndices();
|
||||
void ComputeGatherIndices(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
public:
|
||||
/** @brief Scatter the degrees of freedom, i.e. goes from L-Vector to
|
||||
|
||||
@@ -215,10 +215,6 @@ public:
|
||||
/// quadrature point, oriented relative to "element 1".
|
||||
int GetPermutedIndex(int idx, int iq) const override;
|
||||
|
||||
/// @brief Get the face index (in the standard Mesh numbering) associated
|
||||
/// with face @a idx in the FaceQuadratureSpace.
|
||||
int GetMeshFaceIndex(int idx) const { return face_indices[idx]; }
|
||||
|
||||
/// @brief Returns the index associated with the face described by @a T.
|
||||
///
|
||||
/// The index may differ from the mesh face or boundary element index
|
||||
|
||||
+143
-78
@@ -10,16 +10,17 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "restriction.hpp"
|
||||
#include "normal_deriv_restriction.hpp"
|
||||
#include "gridfunc.hpp"
|
||||
#include "fespace.hpp"
|
||||
#include "pgridfunc.hpp"
|
||||
#include "qspace.hpp"
|
||||
#include "fe/face_map_utils.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pfespace.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -906,6 +907,112 @@ void ConformingFaceRestriction::SetFaceDofsGatherIndices(
|
||||
}
|
||||
}
|
||||
|
||||
static int ToLexOrdering2D(const int face_id, const int size1d, const int i)
|
||||
{
|
||||
if (face_id==2 || face_id==3)
|
||||
{
|
||||
return size1d-1-i;
|
||||
}
|
||||
else
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
static int PermuteFace2D(const int face_id1, const int face_id2,
|
||||
const int orientation,
|
||||
const int size1d, const int index)
|
||||
{
|
||||
int new_index;
|
||||
// Convert from lex ordering
|
||||
if (face_id1==2 || face_id1==3)
|
||||
{
|
||||
new_index = size1d-1-index;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_index = index;
|
||||
}
|
||||
// Permute based on face orientations
|
||||
if (orientation==1)
|
||||
{
|
||||
new_index = size1d-1-new_index;
|
||||
}
|
||||
return ToLexOrdering2D(face_id2, size1d, new_index);
|
||||
}
|
||||
|
||||
static int ToLexOrdering3D(const int face_id, const int size1d, const int i,
|
||||
const int j)
|
||||
{
|
||||
if (face_id==2 || face_id==1 || face_id==5)
|
||||
{
|
||||
return i + j*size1d;
|
||||
}
|
||||
else if (face_id==3 || face_id==4)
|
||||
{
|
||||
return (size1d-1-i) + j*size1d;
|
||||
}
|
||||
else // face_id==0
|
||||
{
|
||||
return i + (size1d-1-j)*size1d;
|
||||
}
|
||||
}
|
||||
|
||||
static int PermuteFace3D(const int face_id1, const int face_id2,
|
||||
const int orientation,
|
||||
const int size1d, const int index)
|
||||
{
|
||||
int i=0, j=0, new_i=0, new_j=0;
|
||||
i = index%size1d;
|
||||
j = index/size1d;
|
||||
// Convert from lex ordering
|
||||
if (face_id1==3 || face_id1==4)
|
||||
{
|
||||
i = size1d-1-i;
|
||||
}
|
||||
else if (face_id1==0)
|
||||
{
|
||||
j = size1d-1-j;
|
||||
}
|
||||
// Permute based on face orientations
|
||||
switch (orientation)
|
||||
{
|
||||
case 0:
|
||||
new_i = i;
|
||||
new_j = j;
|
||||
break;
|
||||
case 1:
|
||||
new_i = j;
|
||||
new_j = i;
|
||||
break;
|
||||
case 2:
|
||||
new_i = j;
|
||||
new_j = (size1d-1-i);
|
||||
break;
|
||||
case 3:
|
||||
new_i = (size1d-1-i);
|
||||
new_j = j;
|
||||
break;
|
||||
case 4:
|
||||
new_i = (size1d-1-i);
|
||||
new_j = (size1d-1-j);
|
||||
break;
|
||||
case 5:
|
||||
new_i = (size1d-1-j);
|
||||
new_j = (size1d-1-i);
|
||||
break;
|
||||
case 6:
|
||||
new_i = (size1d-1-j);
|
||||
new_j = i;
|
||||
break;
|
||||
case 7:
|
||||
new_i = i;
|
||||
new_j = (size1d-1-j);
|
||||
break;
|
||||
}
|
||||
return ToLexOrdering3D(face_id2, size1d, new_i, new_j);
|
||||
}
|
||||
|
||||
// Permute dofs or quads on a face for e2 to match with the ordering of e1
|
||||
int PermuteFaceL2(const int dim, const int face_id1,
|
||||
const int face_id2, const int orientation,
|
||||
@@ -916,9 +1023,9 @@ int PermuteFaceL2(const int dim, const int face_id1,
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return internal::PermuteFace2D(face_id1, face_id2, orientation, size1d, index);
|
||||
return PermuteFace2D(face_id1, face_id2, orientation, size1d, index);
|
||||
case 3:
|
||||
return internal::PermuteFace3D(face_id1, face_id2, orientation, size1d, index);
|
||||
return PermuteFace3D(face_id1, face_id2, orientation, size1d, index);
|
||||
default:
|
||||
MFEM_ABORT("Unsupported dimension.");
|
||||
return 0;
|
||||
@@ -931,7 +1038,6 @@ L2FaceRestriction::L2FaceRestriction(const FiniteElementSpace &fes,
|
||||
const L2FaceValues m,
|
||||
bool build)
|
||||
: fes(fes),
|
||||
ordering(f_ordering),
|
||||
nf(fes.GetNFbyType(type)),
|
||||
ne(fes.GetNE()),
|
||||
vdim(fes.GetVDim()),
|
||||
@@ -954,9 +1060,11 @@ L2FaceRestriction::L2FaceRestriction(const FiniteElementSpace &fes,
|
||||
width = fes.GetVSize();
|
||||
if (!build) { return; }
|
||||
|
||||
CheckFESpace();
|
||||
ComputeScatterIndicesAndOffsets();
|
||||
ComputeGatherIndices();
|
||||
CheckFESpace(f_ordering);
|
||||
|
||||
ComputeScatterIndicesAndOffsets(f_ordering,type);
|
||||
|
||||
ComputeGatherIndices(f_ordering, type);
|
||||
}
|
||||
|
||||
L2FaceRestriction::L2FaceRestriction(const FiniteElementSpace &fes,
|
||||
@@ -1219,7 +1327,7 @@ void L2FaceRestriction::AddFaceMatricesToElementMatrices(const Vector &fea_data,
|
||||
}
|
||||
}
|
||||
|
||||
void L2FaceRestriction::CheckFESpace()
|
||||
void L2FaceRestriction::CheckFESpace(const ElementDofOrdering f_ordering)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
@@ -1243,7 +1351,7 @@ void L2FaceRestriction::CheckFESpace()
|
||||
"Only Gauss-Lobatto and Bernstein basis are supported in "
|
||||
"L2FaceRestriction.");
|
||||
if (nf==0) { return; }
|
||||
const bool dof_reorder = (ordering == ElementDofOrdering::LEXICOGRAPHIC);
|
||||
const bool dof_reorder = (f_ordering == ElementDofOrdering::LEXICOGRAPHIC);
|
||||
if (!dof_reorder)
|
||||
{
|
||||
MFEM_ABORT("Non-Tensor L2FaceRestriction not yet implemented.");
|
||||
@@ -1263,7 +1371,9 @@ void L2FaceRestriction::CheckFESpace()
|
||||
#endif
|
||||
}
|
||||
|
||||
void L2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
void L2FaceRestriction::ComputeScatterIndicesAndOffsets(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType face_type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
// Initialization of the offsets
|
||||
@@ -1279,16 +1389,16 @@ void L2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
|
||||
MFEM_ASSERT(!face.IsShared(),
|
||||
"Unexpected shared face in L2FaceRestriction.");
|
||||
if ( face.IsOfFaceType(type) )
|
||||
if ( face.IsOfFaceType(face_type) )
|
||||
{
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
if ( type==FaceType::Interior && face.IsInterior() )
|
||||
if ( face_type==FaceType::Interior && face.IsInterior() )
|
||||
{
|
||||
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
|
||||
}
|
||||
else if ( type==FaceType::Boundary && face.IsBoundary() )
|
||||
else if ( face_type==FaceType::Boundary && face.IsBoundary() )
|
||||
{
|
||||
SetBoundaryDofsScatterIndices2(face,f_ind);
|
||||
}
|
||||
@@ -1305,7 +1415,9 @@ void L2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
}
|
||||
|
||||
void L2FaceRestriction::ComputeGatherIndices()
|
||||
void L2FaceRestriction::ComputeGatherIndices(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType face_type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
// Computation of gather_indices
|
||||
@@ -1315,11 +1427,11 @@ void L2FaceRestriction::ComputeGatherIndices()
|
||||
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
|
||||
MFEM_ASSERT(!face.IsShared(),
|
||||
"Unexpected shared face in L2FaceRestriction.");
|
||||
if ( face.IsOfFaceType(type) )
|
||||
if ( face.IsOfFaceType(face_type) )
|
||||
{
|
||||
SetFaceDofsGatherIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued &&
|
||||
type==FaceType::Interior &&
|
||||
face_type==FaceType::Interior &&
|
||||
face.IsLocal())
|
||||
{
|
||||
PermuteAndSetFaceDofsGatherIndices2(face,f_ind);
|
||||
@@ -1486,28 +1598,6 @@ void L2FaceRestriction::PermuteAndSetFaceDofsGatherIndices2(
|
||||
}
|
||||
}
|
||||
|
||||
void L2FaceRestriction::NormalDerivativeMult(const Vector &x, Vector &y) const
|
||||
{
|
||||
EnsureNormalDerivativeRestriction();
|
||||
normal_deriv_restr->Mult(x, y);
|
||||
}
|
||||
|
||||
void L2FaceRestriction::NormalDerivativeAddMultTranspose(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
EnsureNormalDerivativeRestriction();
|
||||
normal_deriv_restr->AddMultTranspose(x, y);
|
||||
}
|
||||
|
||||
void L2FaceRestriction::EnsureNormalDerivativeRestriction() const
|
||||
{
|
||||
if (!normal_deriv_restr)
|
||||
{
|
||||
normal_deriv_restr.reset(
|
||||
new L2NormalDerivativeFaceRestriction(fes, ordering, type));
|
||||
}
|
||||
}
|
||||
|
||||
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes,
|
||||
ElementDofOrdering ordering,
|
||||
FaceType type)
|
||||
@@ -1685,11 +1775,11 @@ NCL2FaceRestriction::NCL2FaceRestriction(const FiniteElementSpace &fes,
|
||||
if (!build) { return; }
|
||||
x_interp.UseDevice(true);
|
||||
|
||||
CheckFESpace();
|
||||
CheckFESpace(f_ordering);
|
||||
|
||||
ComputeScatterIndicesAndOffsets();
|
||||
ComputeScatterIndicesAndOffsets(f_ordering, type);
|
||||
|
||||
ComputeGatherIndices();
|
||||
ComputeGatherIndices(f_ordering, type);
|
||||
}
|
||||
|
||||
NCL2FaceRestriction::NCL2FaceRestriction(const FiniteElementSpace &fes,
|
||||
@@ -2169,16 +2259,18 @@ int ToLexOrdering(const int dim, const int face_id, const int size1d,
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return internal::ToLexOrdering2D(face_id, size1d, index);
|
||||
return ToLexOrdering2D(face_id, size1d, index);
|
||||
case 3:
|
||||
return internal::ToLexOrdering3D(face_id, size1d, index%size1d, index/size1d);
|
||||
return ToLexOrdering3D(face_id, size1d, index%size1d, index/size1d);
|
||||
default:
|
||||
MFEM_ABORT("Unsupported dimension.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
|
||||
@@ -2242,7 +2334,9 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void NCL2FaceRestriction::ComputeGatherIndices()
|
||||
void NCL2FaceRestriction::ComputeGatherIndices(
|
||||
const ElementDofOrdering f_ordering,
|
||||
const FaceType type)
|
||||
{
|
||||
Mesh &mesh = *fes.GetMesh();
|
||||
// Computation of gather_indices
|
||||
@@ -2282,33 +2376,4 @@ void NCL2FaceRestriction::ComputeGatherIndices()
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
Vector GetLVectorFaceNbrData(
|
||||
const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (ftype == FaceType::Interior)
|
||||
{
|
||||
if (auto *pfes = const_cast<ParFiniteElementSpace*>
|
||||
(dynamic_cast<const ParFiniteElementSpace*>(&fes)))
|
||||
{
|
||||
if (auto *x_gf = const_cast<ParGridFunction*>
|
||||
(dynamic_cast<const ParGridFunction*>(&x)))
|
||||
{
|
||||
Vector &gf_face_nbr = x_gf->FaceNbrData();
|
||||
if (gf_face_nbr.Size() == 0) { x_gf->ExchangeFaceNbrData(); }
|
||||
gf_face_nbr.Read();
|
||||
return Vector(gf_face_nbr, 0, gf_face_nbr.Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
ParGridFunction gf(pfes, const_cast<Vector&>(x));
|
||||
gf.ExchangeFaceNbrData();
|
||||
return std::move(gf.FaceNbrData());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return Vector();
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+24
-88
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "../linalg/operator.hpp"
|
||||
#include "../mesh/mesh.hpp"
|
||||
#include "normal_deriv_restriction.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -22,8 +21,6 @@ namespace mfem
|
||||
class FiniteElementSpace;
|
||||
enum class ElementDofOrdering;
|
||||
|
||||
class FaceQuadratureSpace;
|
||||
|
||||
/// Abstract base class that defines an interface for element restrictions.
|
||||
class ElementRestrictionOperator : public Operator
|
||||
{
|
||||
@@ -223,45 +220,6 @@ public:
|
||||
y = 0.0;
|
||||
AddMultTranspose(x, y);
|
||||
}
|
||||
|
||||
/** @brief For each face, sets @a y to the partial derivative of @a x with
|
||||
respect to the reference coordinate whose direction is
|
||||
perpendicular to the face on the reference element.
|
||||
|
||||
@details This is not the normal derivative in physical coordinates, but can
|
||||
be mapped to the physical normal derivative using the element
|
||||
Jacobian and the tangential derivatives (in reference coordinates)
|
||||
which can be computed from the face values (provided by Mult).
|
||||
|
||||
Note that due to the polynomial degree of the element mapping, the
|
||||
physical normal derivative may be a higher degree polynomial than
|
||||
the restriction of the values to the face. However, the normal
|
||||
derivative in reference coordinates has degree-1, and therefore can
|
||||
be exactly represented with the degrees of freedom of a face
|
||||
E-vector.
|
||||
|
||||
@param[in] x The L-vector degrees of freedom.
|
||||
@param[in,out] y The reference normal derivative degrees of freedom. Is
|
||||
E-vector like.
|
||||
*/
|
||||
virtual void NormalDerivativeMult(const Vector &x, Vector &y) const
|
||||
{
|
||||
MFEM_ABORT("Not implemented for this restriction operator.");
|
||||
}
|
||||
|
||||
/** @brief Add the face reference-normal derivative degrees of freedom in @a
|
||||
x to the element degrees of freedom in @a y.
|
||||
|
||||
@details see NormalDerivativeMult.
|
||||
|
||||
@param[in] x The degrees of freedom of the face reference-normal
|
||||
derivative. Is E-vector like.
|
||||
@param[in,out] y The L-vector degrees of freedom.
|
||||
*/
|
||||
virtual void NormalDerivativeAddMultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
MFEM_ABORT("Not implemented for this restriction operator.");
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Operator that extracts face degrees of freedom for H1, ND, or RT
|
||||
@@ -407,7 +365,6 @@ class L2FaceRestriction : public FaceRestriction
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace &fes;
|
||||
const ElementDofOrdering ordering;
|
||||
const int nf; // Number of faces of the requested type
|
||||
const int ne; // Number of elements
|
||||
const int vdim; // vdim
|
||||
@@ -422,7 +379,6 @@ protected:
|
||||
Array<int> scatter_indices2; // Scattering indices for element 2 on each face
|
||||
Array<int> gather_offsets; // offsets for the gathering indices of each dof
|
||||
Array<int> gather_indices; // gathering indices for each dof
|
||||
mutable std::unique_ptr<L2NormalDerivativeFaceRestriction> normal_deriv_restr;
|
||||
|
||||
/** @brief Constructs an L2FaceRestriction.
|
||||
|
||||
@@ -531,49 +487,34 @@ public:
|
||||
virtual void AddFaceMatricesToElementMatrices(const Vector &fea_data,
|
||||
Vector &ea_data) const;
|
||||
|
||||
/** @brief Scatter the degrees of freedom, i.e. goes from L-Vector to
|
||||
face E-Vector.
|
||||
|
||||
@param[in] x The L-vector degrees of freedom.
|
||||
@param[out] y The face E-Vector degrees of freedom with the given format:
|
||||
(face_dofs x vdim x 2 x nf) where nf is the number of
|
||||
interior or boundary faces requested by @a type in the
|
||||
constructor. The face_dofs are ordered according to the
|
||||
given ElementDofOrdering. */
|
||||
void NormalDerivativeMult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/** @brief Add the face reference-normal derivative degrees of freedom in @a
|
||||
x to the element degrees of freedom in @a y.
|
||||
|
||||
@details see NormalDerivativeMult.
|
||||
|
||||
@param[in] x The degrees of freedom of the face reference-normal
|
||||
derivative. Is E-vector like.
|
||||
@param[in,out] y The L-vector degrees of freedom.
|
||||
*/
|
||||
void NormalDerivativeAddMultTranspose(const Vector &x,
|
||||
Vector &y) const override;
|
||||
private:
|
||||
/** @brief Compute the scatter indices: L-vector to E-vector, and the offsets
|
||||
for the gathering: E-vector to L-vector.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeScatterIndicesAndOffsets();
|
||||
void ComputeScatterIndicesAndOffsets(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
/** @brief Compute the gather indices: E-vector to L-vector.
|
||||
|
||||
Note: Requires the gather offsets to be computed.
|
||||
*/
|
||||
void ComputeGatherIndices();
|
||||
|
||||
/// Create the internal normal derivative restriction operator if needed.
|
||||
void EnsureNormalDerivativeRestriction() const;
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeGatherIndices(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
protected:
|
||||
mutable Array<int> face_map; // Used in the computation of GetFaceDofs
|
||||
|
||||
/** @brief Verify that L2FaceRestriction is built from an L2 FESpace.
|
||||
|
||||
@param[in] f_ordering The requested face dof ordering.
|
||||
*/
|
||||
void CheckFESpace();
|
||||
void CheckFESpace(const ElementDofOrdering f_ordering);
|
||||
|
||||
/** @brief Set the scattering indices of elem1, and increment the offsets for
|
||||
the face described by the @a face. The ordering of the face dofs of elem1
|
||||
@@ -997,14 +938,22 @@ private:
|
||||
/** @brief Compute the scatter indices: L-vector to E-vector, the offsets
|
||||
for the gathering: E-vector to L-vector, and the interpolators from
|
||||
coarse to fine face for master non-comforming faces.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeScatterIndicesAndOffsets();
|
||||
void ComputeScatterIndicesAndOffsets(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
/** @brief Compute the gather indices: E-vector to L-vector.
|
||||
|
||||
Note: Requires the gather offsets to be computed.
|
||||
|
||||
@param[in] f_ordering Request a specific face dof ordering.
|
||||
@param[in] type Request internal or boundary faces dofs.
|
||||
*/
|
||||
void ComputeGatherIndices();
|
||||
void ComputeGatherIndices(const ElementDofOrdering f_ordering,
|
||||
const FaceType type);
|
||||
|
||||
public:
|
||||
/** @brief Scatter the degrees of freedom, i.e. goes from L-Vector to
|
||||
@@ -1066,6 +1015,7 @@ public:
|
||||
void DoubleValuedNonconformingTransposeInterpolationInPlace(Vector& x) const;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Convert a dof face index from Native ordering to lexicographic
|
||||
ordering for quads and hexes.
|
||||
|
||||
@@ -1094,20 +1044,6 @@ int PermuteFaceL2(const int dim, const int face_id1,
|
||||
const int face_id2, const int orientation,
|
||||
const int size1d, const int index);
|
||||
|
||||
/// @brief Return the face-neighbor data given the L-vector @a x.
|
||||
///
|
||||
/// If the input vector @a x is a ParGridFunction with non-empty face-neighbor
|
||||
/// data, return an alias to ParGridFunction::FaceNbrData() (avoiding an
|
||||
/// unneeded call to ParGridFunction::ExchangeFaceNbrData).
|
||||
///
|
||||
/// Otherwise, create a temporary ParGridFunction, exchange the face-neighbor
|
||||
/// data, and return the resulting vector.
|
||||
///
|
||||
/// If @a fes is not a parallel space, or if @a ftype is not FaceType::Interior,
|
||||
/// return an empty vector.
|
||||
Vector GetLVectorFaceNbrData(
|
||||
const FiniteElementSpace &fes, const Vector &x, FaceType ftype);
|
||||
|
||||
}
|
||||
|
||||
#endif // MFEM_RESTRICTION
|
||||
|
||||
+124
-238
@@ -2949,15 +2949,6 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
|
||||
MFEM_VERIFY(surf_fit_pos == NULL,
|
||||
"Using both fitting approaches is not supported.");
|
||||
|
||||
const int dim = s0.FESpace()->GetMesh()->Dimension();
|
||||
Mesh *mesh = s0.FESpace()->GetMesh();
|
||||
MFEM_VERIFY(mesh->GetNodes()->Size() == dim*s0.Size(),
|
||||
"Mesh and level-set polynomial order must be the same.");
|
||||
const H1_FECollection *fec = dynamic_cast<const H1_FECollection *>
|
||||
(s0.FESpace()->FEColl());
|
||||
MFEM_VERIFY(fec, "Only H1_FECollection is supported for the surface fitting "
|
||||
"grid function.");
|
||||
|
||||
delete surf_fit_gf;
|
||||
surf_fit_gf = new GridFunction(s0);
|
||||
surf_fit_gf->CountElementsPerVDof(surf_fit_dof_count);
|
||||
@@ -2996,24 +2987,12 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &pos,
|
||||
void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
const Array<bool> &smarker,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
AdaptivityEvaluator *aegrad,
|
||||
AdaptivityEvaluator *aehess)
|
||||
AdaptivityEvaluator &ae)
|
||||
{
|
||||
// To have both we must duplicate the markers.
|
||||
MFEM_VERIFY(surf_fit_pos == NULL,
|
||||
"Using both fitting approaches is not supported.");
|
||||
|
||||
const int dim = s0.FESpace()->GetMesh()->Dimension();
|
||||
ParMesh *pmesh = s0.ParFESpace()->GetParMesh();
|
||||
MFEM_VERIFY(pmesh->GetNodes()->Size() == dim*s0.Size(),
|
||||
"Mesh and level-set polynomial order must be the same.");
|
||||
const H1_FECollection *fec = dynamic_cast<const H1_FECollection *>
|
||||
(s0.FESpace()->FEColl());
|
||||
MFEM_VERIFY(fec, "Only H1_FECollection is supported for the surface fitting "
|
||||
"grid function.");
|
||||
|
||||
|
||||
delete surf_fit_gf;
|
||||
surf_fit_gf = new GridFunction(s0);
|
||||
s0.CountElementsPerVDof(surf_fit_dof_count);
|
||||
@@ -3021,80 +3000,11 @@ void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
surf_fit_coeff = &coeff;
|
||||
surf_fit_eval = &ae;
|
||||
|
||||
surf_fit_eval->SetParMetaInfo(*pmesh, *s0.ParFESpace());
|
||||
surf_fit_eval->SetParMetaInfo(*s0.ParFESpace()->GetParMesh(),
|
||||
*s0.ParFESpace());
|
||||
surf_fit_eval->SetInitialField
|
||||
(*surf_fit_gf->FESpace()->GetMesh()->GetNodes(), *surf_fit_gf);
|
||||
|
||||
if (!aegrad) { return; }
|
||||
|
||||
MFEM_VERIFY(aehess, "AdaptivityEvaluator for Hessians must be provided too.");
|
||||
|
||||
ParFiniteElementSpace *fes = s0.ParFESpace();
|
||||
|
||||
// FE space for gradients.
|
||||
delete surf_fit_grad;
|
||||
H1_FECollection *fec_grad = new H1_FECollection(fec->GetOrder(), dim,
|
||||
fec->GetBasisType());
|
||||
ParFiniteElementSpace *fes_grad = new ParFiniteElementSpace(pmesh, fec_grad,
|
||||
dim);
|
||||
// Initial gradients.
|
||||
surf_fit_grad = new GridFunction(fes_grad);
|
||||
surf_fit_grad->MakeOwner(fec_grad);
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
ParGridFunction surf_fit_grad_comp(fes, surf_fit_grad->GetData()+d*s0.Size());
|
||||
s0.GetDerivative(1, d, surf_fit_grad_comp);
|
||||
}
|
||||
surf_fit_eval_grad = aegrad;
|
||||
surf_fit_eval_grad->SetParMetaInfo(*pmesh, *fes_grad);
|
||||
surf_fit_eval_grad->SetInitialField(*pmesh->GetNodes(), *surf_fit_grad);
|
||||
|
||||
// FE space for Hessians.
|
||||
delete surf_fit_hess;
|
||||
H1_FECollection *fec_hess = new H1_FECollection(fec->GetOrder(), dim,
|
||||
fec->GetBasisType());
|
||||
ParFiniteElementSpace *fes_hess = new ParFiniteElementSpace(pmesh, fec_hess,
|
||||
dim*dim);
|
||||
// Initial Hessians.
|
||||
surf_fit_hess = new GridFunction(fes_hess);
|
||||
surf_fit_hess->MakeOwner(fec_hess);
|
||||
int id = 0;
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int idir = 0; idir < dim; idir++)
|
||||
{
|
||||
ParGridFunction surf_fit_grad_comp(fes,
|
||||
surf_fit_grad->GetData()+d*s0.Size());
|
||||
ParGridFunction surf_fit_hess_comp(fes,
|
||||
surf_fit_hess->GetData()+id*s0.Size());
|
||||
surf_fit_grad_comp.GetDerivative(1, idir, surf_fit_hess_comp);
|
||||
id++;
|
||||
}
|
||||
}
|
||||
surf_fit_eval_hess = aehess;
|
||||
surf_fit_eval_hess->SetParMetaInfo(*pmesh, *fes_hess);
|
||||
surf_fit_eval_hess->SetInitialField(*pmesh->GetNodes(), *surf_fit_hess);
|
||||
|
||||
// Store DOF indices that are marked for fitting. Used to reduce work for
|
||||
// transferring information between source/background and current mesh.
|
||||
surf_fit_marker_dof_index.SetSize(0);
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
if (dynamic_cast<InterpolatorFP *>(surf_fit_eval) &&
|
||||
dynamic_cast<InterpolatorFP *>(surf_fit_eval_grad) &&
|
||||
dynamic_cast<InterpolatorFP *>(surf_fit_eval_hess))
|
||||
{
|
||||
for (int i = 0; i < surf_fit_marker->Size(); i++)
|
||||
{
|
||||
if ((*surf_fit_marker)[i] == true)
|
||||
{
|
||||
surf_fit_marker_dof_index.Append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*surf_fit_grad = 0.0;
|
||||
*surf_fit_hess = 0.0;
|
||||
surf_fit_gf_bg = false;
|
||||
}
|
||||
|
||||
void TMOP_Integrator::EnableSurfaceFittingFromSource(
|
||||
@@ -3112,17 +3022,16 @@ void TMOP_Integrator::EnableSurfaceFittingFromSource(
|
||||
// Setup for level set function
|
||||
delete surf_fit_gf;
|
||||
surf_fit_gf = new GridFunction(s0);
|
||||
*surf_fit_gf = 0.0;
|
||||
surf_fit_marker = &smarker;
|
||||
surf_fit_coeff = &coeff;
|
||||
surf_fit_eval = &ae;
|
||||
|
||||
surf_fit_gf_bg = true;
|
||||
surf_fit_eval->SetParMetaInfo(*s_bg.ParFESpace()->GetParMesh(),
|
||||
*s_bg.ParFESpace());
|
||||
surf_fit_eval->SetInitialField
|
||||
(*s_bg.FESpace()->GetMesh()->GetNodes(), s_bg);
|
||||
GridFunction *nodes = s0.FESpace()->GetMesh()->GetNodes();
|
||||
surf_fit_eval->ComputeAtNewPosition(*nodes, *surf_fit_gf,
|
||||
nodes->FESpace()->GetOrdering());
|
||||
|
||||
// Setup for gradient on background mesh
|
||||
MFEM_VERIFY(s_bg_grad.ParFESpace()->GetOrdering() ==
|
||||
@@ -3132,11 +3041,11 @@ void TMOP_Integrator::EnableSurfaceFittingFromSource(
|
||||
delete surf_fit_grad;
|
||||
surf_fit_grad = new GridFunction(s0_grad);
|
||||
*surf_fit_grad = 0.0;
|
||||
surf_fit_eval_grad = &age;
|
||||
surf_fit_eval_hess = &ahe;
|
||||
surf_fit_eval_grad->SetParMetaInfo(*s_bg_grad.ParFESpace()->GetParMesh(),
|
||||
*s_bg_grad.ParFESpace());
|
||||
surf_fit_eval_grad->SetInitialField
|
||||
surf_fit_eval_bg_grad = &age;
|
||||
surf_fit_eval_bg_hess = &ahe;
|
||||
surf_fit_eval_bg_grad->SetParMetaInfo(*s_bg_grad.ParFESpace()->GetParMesh(),
|
||||
*s_bg_grad.ParFESpace());
|
||||
surf_fit_eval_bg_grad->SetInitialField
|
||||
(*s_bg_grad.FESpace()->GetMesh()->GetNodes(), s_bg_grad);
|
||||
|
||||
// Setup for Hessian on background mesh
|
||||
@@ -3147,9 +3056,9 @@ void TMOP_Integrator::EnableSurfaceFittingFromSource(
|
||||
delete surf_fit_hess;
|
||||
surf_fit_hess = new GridFunction(s0_hess);
|
||||
*surf_fit_hess = 0.0;
|
||||
surf_fit_eval_hess->SetParMetaInfo(*s_bg_hess.ParFESpace()->GetParMesh(),
|
||||
*s_bg_hess.ParFESpace());
|
||||
surf_fit_eval_hess->SetInitialField
|
||||
surf_fit_eval_bg_hess->SetParMetaInfo(*s_bg_hess.ParFESpace()->GetParMesh(),
|
||||
*s_bg_hess.ParFESpace());
|
||||
surf_fit_eval_bg_hess->SetInitialField
|
||||
(*s_bg_hess.FESpace()->GetMesh()->GetNodes(), s_bg_hess);
|
||||
|
||||
// Count number of zones that share each of the DOFs
|
||||
@@ -3954,7 +3863,7 @@ void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x,
|
||||
|
||||
Vector sigma_e(dof_s);
|
||||
DenseMatrix surf_fit_grad_e(dof_s, dim);
|
||||
if (surf_fit_gf)
|
||||
if (surf_fit_gf || surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_gf->GetSubVector(vdofs, sigma_e);
|
||||
|
||||
@@ -3962,7 +3871,7 @@ void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x,
|
||||
// The FE coefficients of the gradient go in surf_fit_grad_e.
|
||||
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
if (surf_fit_grad)
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_grad->GetSubVector(dofs, grad_ptr);
|
||||
@@ -4036,7 +3945,7 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
Vector sigma_e(dof_s);
|
||||
DenseMatrix surf_fit_grad_e(dof_s, dim);
|
||||
DenseMatrix surf_fit_hess_e(dof_s, dim*dim);
|
||||
if (surf_fit_gf)
|
||||
if (surf_fit_gf || surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_gf->GetSubVector(vdofs, sigma_e);
|
||||
|
||||
@@ -4044,7 +3953,7 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
// The FE coefficients of the gradient go in surf_fit_grad_e.
|
||||
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
|
||||
DenseMatrix grad_phys; // This will be (dof x dim, dof).
|
||||
if (surf_fit_grad)
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_grad->GetSubVector(dofs, grad_ptr);
|
||||
@@ -4058,7 +3967,7 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
// Project the Hessian of sigma in the same space.
|
||||
// The FE coefficients of the Hessian go in surf_fit_hess_e.
|
||||
Vector hess_ptr(surf_fit_hess_e.GetData(), dof_s*dim*dim);
|
||||
if (surf_fit_hess)
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
surf_fit_hess->FESpace()->GetElementVDofs(el_id, dofs);
|
||||
surf_fit_hess->GetSubVector(dofs, hess_ptr);
|
||||
@@ -4085,7 +3994,7 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
|
||||
Tpr.SetIntPoint(&ip);
|
||||
real_t w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip);
|
||||
|
||||
if (surf_fit_gf)
|
||||
if (surf_fit_gf || surf_fit_gf_bg)
|
||||
{
|
||||
Vector gg_ptr(surf_fit_hess_s.GetData(), dim * dim);
|
||||
surf_fit_hess_e.GetRow(s, gg_ptr);
|
||||
@@ -4467,130 +4376,6 @@ void TMOP_Integrator::ComputeMinJac(const Vector &x,
|
||||
dx = detv_avg_min / dxscale;
|
||||
}
|
||||
|
||||
void TMOP_Integrator::RemapSurfaceFittingLevelSetAtNodes(const Vector &new_x,
|
||||
int new_x_ordering)
|
||||
{
|
||||
if (!surf_fit_gf) { return; }
|
||||
|
||||
if (surf_fit_marker_dof_index.Size())
|
||||
{
|
||||
// Interpolate information only at DOFs marked for fitting.
|
||||
const int dim = surf_fit_gf->FESpace()->GetMesh()->Dimension();
|
||||
const int cnt = surf_fit_marker_dof_index.Size();
|
||||
const int total_cnt = new_x.Size()/dim;
|
||||
Vector new_x_sorted(cnt*dim);
|
||||
if (new_x_ordering == 0)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
new_x_sorted(i + d*cnt) = new_x(dof_index + d*total_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
new_x_sorted(d + i*dim) = new_x(d + dof_index*dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolate values of the LS.
|
||||
Vector surf_fit_gf_int, surf_fit_grad_int, surf_fit_hess_int;
|
||||
surf_fit_eval->ComputeAtNewPosition(new_x_sorted, surf_fit_gf_int,
|
||||
new_x_ordering);
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
(*surf_fit_gf)[dof_index] = surf_fit_gf_int(i);
|
||||
}
|
||||
|
||||
// Interpolate gradients of the LS.
|
||||
surf_fit_eval_grad->ComputeAtNewPosition(new_x_sorted, surf_fit_grad_int,
|
||||
new_x_ordering);
|
||||
// Assumes surf_fit_grad and surf_fit_gf share the same space
|
||||
const int grad_dim = surf_fit_grad->VectorDim();
|
||||
const int grad_cnt = surf_fit_grad->Size()/grad_dim;
|
||||
if (surf_fit_grad->FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
for (int d = 0; d < grad_dim; d++)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
(*surf_fit_grad)[dof_index + d*grad_cnt] =
|
||||
surf_fit_grad_int(i + d*cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
for (int d = 0; d < grad_dim; d++)
|
||||
{
|
||||
(*surf_fit_grad)[dof_index*grad_dim + d] =
|
||||
surf_fit_grad_int(i*grad_dim + d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolate Hessians of the LS.
|
||||
surf_fit_eval_hess->ComputeAtNewPosition(new_x_sorted, surf_fit_hess_int,
|
||||
new_x_ordering);
|
||||
// Assumes surf_fit_hess and surf_fit_gf share the same space
|
||||
const int hess_dim = surf_fit_hess->VectorDim();
|
||||
const int hess_cnt = surf_fit_hess->Size()/hess_dim;
|
||||
if (surf_fit_hess->FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
for (int d = 0; d < hess_dim; d++)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
(*surf_fit_hess)[dof_index + d*hess_cnt] =
|
||||
surf_fit_hess_int(i + d*cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
for (int d = 0; d < hess_dim; d++)
|
||||
{
|
||||
(*surf_fit_hess)[dof_index*hess_dim + d] =
|
||||
surf_fit_hess_int(i*hess_dim + d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
surf_fit_eval->ComputeAtNewPosition(new_x, *surf_fit_gf, new_x_ordering);
|
||||
if (surf_fit_eval_grad)
|
||||
{
|
||||
surf_fit_eval_grad->ComputeAtNewPosition(new_x, *surf_fit_grad,
|
||||
new_x_ordering);
|
||||
}
|
||||
if (surf_fit_eval_hess)
|
||||
{
|
||||
surf_fit_eval_hess->ComputeAtNewPosition(new_x, *surf_fit_hess,
|
||||
new_x_ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TMOP_Integrator::
|
||||
UpdateAfterMeshPositionChange(const Vector &x_new,
|
||||
const FiniteElementSpace &x_fes)
|
||||
@@ -4621,11 +4406,112 @@ UpdateAfterMeshPositionChange(const Vector &x_new,
|
||||
adapt_lim_eval->ComputeAtNewPosition(x_new, *adapt_lim_gf, ordering);
|
||||
}
|
||||
|
||||
// Update surf_fit_gf (and optionally its gradients) if surface
|
||||
// fitting is enabled.
|
||||
// Update surf_fit_gf if surface fitting is enabled.
|
||||
if (surf_fit_gf)
|
||||
{
|
||||
RemapSurfaceFittingLevelSetAtNodes(x_new, ordering);
|
||||
if (surf_fit_gf_bg)
|
||||
{
|
||||
// Interpolate information for only DOFs marked for fitting.
|
||||
const int dim = surf_fit_gf->FESpace()->GetMesh()->Dimension();
|
||||
const int cnt = surf_fit_marker_dof_index.Size();
|
||||
const int total_cnt = x_new.Size()/dim;
|
||||
Vector new_x_sorted(cnt*dim);
|
||||
if (ordering == 0)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
new_x_sorted(i + d*cnt) = x_new(dof_index + d*total_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
new_x_sorted(d + i*dim) = x_new(d + dof_index*dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector surf_fit_gf_int, surf_fit_grad_int, surf_fit_hess_int;
|
||||
surf_fit_eval->ComputeAtNewPosition(
|
||||
new_x_sorted, surf_fit_gf_int, ordering);
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
(*surf_fit_gf)[dof_index] = surf_fit_gf_int(i);
|
||||
}
|
||||
|
||||
surf_fit_eval_bg_grad->ComputeAtNewPosition(
|
||||
new_x_sorted, surf_fit_grad_int, ordering);
|
||||
// Assumes surf_fit_grad and surf_fit_gf share the same space
|
||||
const int grad_dim = surf_fit_grad->VectorDim();
|
||||
const int grad_cnt = surf_fit_grad->Size()/grad_dim;
|
||||
if (surf_fit_grad->FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
for (int d = 0; d < grad_dim; d++)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
(*surf_fit_grad)[dof_index + d*grad_cnt] =
|
||||
surf_fit_grad_int(i + d*cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
for (int d = 0; d < grad_dim; d++)
|
||||
{
|
||||
(*surf_fit_grad)[dof_index*dim + d] =
|
||||
surf_fit_grad_int(i*dim + d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
surf_fit_eval_bg_hess->ComputeAtNewPosition(
|
||||
new_x_sorted, surf_fit_hess_int, ordering);
|
||||
// Assumes surf_fit_hess and surf_fit_gf share the same space
|
||||
const int hess_dim = surf_fit_hess->VectorDim();
|
||||
const int hess_cnt = surf_fit_hess->Size()/hess_dim;
|
||||
if (surf_fit_hess->FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
for (int d = 0; d < hess_dim; d++)
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
(*surf_fit_hess)[dof_index + d*hess_cnt] =
|
||||
surf_fit_hess_int(i + d*cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
int dof_index = surf_fit_marker_dof_index[i];
|
||||
for (int d = 0; d < hess_dim; d++)
|
||||
{
|
||||
(*surf_fit_hess)[dof_index*dim + d] =
|
||||
surf_fit_hess_int(i*dim + d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
surf_fit_eval->ComputeAtNewPosition(x_new, *surf_fit_gf, ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-18
@@ -1784,11 +1784,12 @@ protected:
|
||||
// Fitting to given physical positions.
|
||||
TMOP_QuadraticLimiter *surf_fit_limiter; // Owned. Created internally.
|
||||
const GridFunction *surf_fit_pos; // Not owned. Positions to fit.
|
||||
real_t surf_fit_normal; // Normalization factor.
|
||||
GridFunction *surf_fit_grad, *surf_fit_hess; // Owned. Created internally.
|
||||
AdaptivityEvaluator *surf_fit_eval_grad, *surf_fit_eval_hess; // Not owned.
|
||||
Array<int> surf_fit_dof_count; // Number of dofs per node.
|
||||
Array<int> surf_fit_marker_dof_index; // Indices of nodes to fit.
|
||||
real_t surf_fit_normal;
|
||||
bool surf_fit_gf_bg;
|
||||
GridFunction *surf_fit_grad, *surf_fit_hess;
|
||||
AdaptivityEvaluator *surf_fit_eval_bg_grad, *surf_fit_eval_bg_hess;
|
||||
Array<int> surf_fit_dof_count;
|
||||
Array<int> surf_fit_marker_dof_index;
|
||||
|
||||
DiscreteAdaptTC *discr_tc;
|
||||
|
||||
@@ -1984,10 +1985,6 @@ protected:
|
||||
real_t ComputeUntanglerMaxMuBarrier(const Vector &x,
|
||||
const FiniteElementSpace &fes);
|
||||
|
||||
// Remaps the internal surface fitting gridfunction object at provided
|
||||
// locations.
|
||||
void RemapSurfaceFittingLevelSetAtNodes(const Vector &new_x,
|
||||
int new_x_ordering);
|
||||
public:
|
||||
/** @param[in] m TMOP_QualityMetric for r-adaptivity (not owned).
|
||||
@param[in] tc Target-matrix construction algorithm to use (not owned).
|
||||
@@ -2003,8 +2000,9 @@ public:
|
||||
surf_fit_marker(NULL), surf_fit_coeff(NULL),
|
||||
surf_fit_gf(NULL), surf_fit_eval(NULL),
|
||||
surf_fit_limiter(NULL), surf_fit_pos(NULL),
|
||||
surf_fit_normal(1.0), surf_fit_grad(NULL), surf_fit_hess(NULL),
|
||||
surf_fit_eval_grad(NULL), surf_fit_eval_hess(NULL),
|
||||
surf_fit_normal(1.0),
|
||||
surf_fit_gf_bg(false), surf_fit_grad(NULL), surf_fit_hess(NULL),
|
||||
surf_fit_eval_bg_grad(NULL), surf_fit_eval_bg_hess(NULL),
|
||||
discr_tc(dynamic_cast<DiscreteAdaptTC *>(tc)),
|
||||
fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false)
|
||||
{ PA.enabled = false; }
|
||||
@@ -2105,15 +2103,9 @@ public:
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for surface fitting to the zero level set of a function.
|
||||
/// Here, we add two optional inputs: @a aegrad and @a aehess. When provided,
|
||||
/// the first and second derivative of the input level set are computed on
|
||||
/// the initial mesh, and @a aegrad and @a aehess are used to remap grad_s(x)
|
||||
/// from grad_s0(x0) and hess_s(x) from hess_s0(x0), respectively.
|
||||
void EnableSurfaceFitting(const ParGridFunction &s0,
|
||||
const Array<bool> &smarker, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
AdaptivityEvaluator *aegrad = NULL,
|
||||
AdaptivityEvaluator *aehess = NULL);
|
||||
AdaptivityEvaluator &ae);
|
||||
|
||||
/** @brief Fitting of certain DOFs in the current mesh to the zero level set
|
||||
of a function defined on another (finer) source mesh.
|
||||
|
||||
+36
-88
@@ -429,13 +429,11 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
#endif
|
||||
|
||||
real_t scale = 1.0;
|
||||
bool fitting = IsSurfaceFittingEnabled();
|
||||
real_t init_fit_avg_err, init_fit_max_err = 0.0;
|
||||
if (fitting && surf_fit_converge_error)
|
||||
real_t avg_surf_fit_err, max_surf_fit_err = 0.0;
|
||||
if (surf_fit_max_threshold > 0.0)
|
||||
{
|
||||
GetSurfaceFittingError(x_out_loc, init_fit_avg_err, init_fit_max_err);
|
||||
// Check for convergence
|
||||
if (init_fit_max_err < surf_fit_max_err_limit)
|
||||
GetSurfaceFittingError(x_out_loc, avg_surf_fit_err, max_surf_fit_err);
|
||||
if (max_surf_fit_err < surf_fit_max_threshold)
|
||||
{
|
||||
if (print_options.iterations)
|
||||
{
|
||||
@@ -446,12 +444,11 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
||||
if (surf_fit_adapt_count >= surf_fit_adapt_count_limit)
|
||||
if (adapt_inc_count >= max_adapt_inc_count)
|
||||
{
|
||||
if (print_options.iterations)
|
||||
{
|
||||
mfem::out << "TMOPNewtonSolver terminated "
|
||||
mfem::out << "TMOPNewtonSolver converged "
|
||||
"based on max number of times surface fitting weight can"
|
||||
"be increased. \n";
|
||||
}
|
||||
@@ -470,7 +467,7 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
// reference to detect deteriorations.
|
||||
MFEM_VERIFY(min_det_ptr != NULL, " Initial mesh was valid, but"
|
||||
" intermediate mesh is invalid. Contact TMOP Developers.");
|
||||
MFEM_VERIFY(min_detJ_limit == 0.0,
|
||||
MFEM_VERIFY(min_detJ_threshold == 0.0,
|
||||
"This setup is not supported. Contact TMOP Developers.");
|
||||
*min_det_ptr = untangle_factor * min_detT_in;
|
||||
}
|
||||
@@ -481,7 +478,6 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
bool x_out_ok = false;
|
||||
real_t energy_out = 0.0, min_detT_out;
|
||||
const real_t norm_in = Norm(r);
|
||||
real_t avg_fit_err, max_fit_err = 0.0;
|
||||
|
||||
const real_t detJ_factor = (solver_type == 1) ? 0.25 : 0.5;
|
||||
compute_metric_quantile_flag = false;
|
||||
@@ -492,9 +488,6 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
// Perform the line search.
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
avg_fit_err = 0.0;
|
||||
max_fit_err = 0.0;
|
||||
|
||||
// Update the mesh and get the L-vector in x_out_loc.
|
||||
add(x, -scale, c, x_out);
|
||||
if (serial)
|
||||
@@ -509,7 +502,7 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
|
||||
// Check the changes in detJ.
|
||||
min_detT_out = ComputeMinDet(x_out_loc, *fes);
|
||||
if (untangling == false && min_detT_out <= min_detJ_limit)
|
||||
if (untangling == false && min_detT_out <= min_detJ_threshold)
|
||||
{
|
||||
// No untangling, and detJ got negative (or small) -- no good.
|
||||
if (print_options.iterations)
|
||||
@@ -536,19 +529,18 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
// Check the changes in total energy.
|
||||
ProcessNewState(x_out);
|
||||
|
||||
// Ensure sufficient decrease in fitting error if we are trying to
|
||||
// converge based on error.
|
||||
if (fitting && surf_fit_converge_error)
|
||||
real_t avg_fit_err, max_fit_err = 0.0;
|
||||
if (surf_fit_max_threshold > 0.0)
|
||||
{
|
||||
GetSurfaceFittingError(x_out_loc, avg_fit_err, max_fit_err);
|
||||
if (max_fit_err >= 1.2*init_fit_max_err)
|
||||
}
|
||||
if (surf_fit_max_threshold > 0.0 && max_fit_err >= 1.2*max_surf_fit_err)
|
||||
{
|
||||
if (print_options.iterations)
|
||||
{
|
||||
if (print_options.iterations)
|
||||
{
|
||||
mfem::out << "Scale = " << scale << " Surf fit err increased.\n";
|
||||
}
|
||||
scale *= 0.5; continue;
|
||||
mfem::out << "Scale = " << scale << " Surf fit err increased.\n";
|
||||
}
|
||||
scale *= 0.5; continue;
|
||||
}
|
||||
|
||||
if (serial)
|
||||
@@ -622,7 +614,7 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
|
||||
if (x_out_ok == false) { scale = 0.0; }
|
||||
|
||||
if (surf_fit_scale_factor > 0.0) { surf_fit_coeff_update = true; }
|
||||
if (surf_fit_scale_factor > 0.0) { update_surf_fit_coeff = true; }
|
||||
compute_metric_quantile_flag = true;
|
||||
|
||||
return scale;
|
||||
@@ -665,7 +657,7 @@ void TMOPNewtonSolver::GetSurfaceFittingWeight(Array<real_t> &weights) const
|
||||
for (int i = 0; i < integs.Size(); i++)
|
||||
{
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti && ti->IsSurfaceFittingEnabled())
|
||||
if (ti)
|
||||
{
|
||||
weight = ti->GetSurfaceFittingWeight();
|
||||
weights.Append(weight);
|
||||
@@ -676,11 +668,8 @@ void TMOPNewtonSolver::GetSurfaceFittingWeight(Array<real_t> &weights) const
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
if (ati[j]->IsSurfaceFittingEnabled())
|
||||
{
|
||||
weight = ati[j]->GetSurfaceFittingWeight();
|
||||
weights.Append(weight);
|
||||
}
|
||||
weight = ati[j]->GetSurfaceFittingWeight();
|
||||
weights.Append(weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,39 +716,6 @@ void TMOPNewtonSolver::GetSurfaceFittingError(const Vector &x_loc,
|
||||
}
|
||||
}
|
||||
|
||||
bool TMOPNewtonSolver::IsSurfaceFittingEnabled() const
|
||||
{
|
||||
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
|
||||
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
|
||||
TMOP_Integrator *ti = NULL;
|
||||
TMOPComboIntegrator *co = NULL;
|
||||
|
||||
for (int i = 0; i < integs.Size(); i++)
|
||||
{
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
if (ti->IsSurfaceFittingEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
|
||||
if (co)
|
||||
{
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
if (ati[j]->IsSurfaceFittingEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
{
|
||||
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
|
||||
@@ -845,46 +801,38 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
// adaptive surface fitting is enabled. The idea is to increase the
|
||||
// coefficient if the surface fitting error does not sufficiently
|
||||
// decrease between subsequent TMOPNewtonSolver iterations.
|
||||
if (surf_fit_coeff_update)
|
||||
if (update_surf_fit_coeff)
|
||||
{
|
||||
// Get surface fitting errors.
|
||||
GetSurfaceFittingError(x_loc, surf_fit_avg_err, surf_fit_max_err);
|
||||
GetSurfaceFittingError(x_loc, surf_fit_err_avg, surf_fit_err_max);
|
||||
// Get array with surface fitting weights.
|
||||
Array<real_t> fitweights;
|
||||
GetSurfaceFittingWeight(fitweights);
|
||||
Array<real_t> weights;
|
||||
GetSurfaceFittingWeight(weights);
|
||||
|
||||
if (print_options.iterations)
|
||||
{
|
||||
mfem::out << "Avg/Max surface fitting error: " <<
|
||||
surf_fit_avg_err << " " <<
|
||||
surf_fit_max_err << "\n";
|
||||
surf_fit_err_avg << " " <<
|
||||
surf_fit_err_max << "\n";
|
||||
mfem::out << "Min/Max surface fitting weight: " <<
|
||||
fitweights.Min() << " " << fitweights.Max() << "\n";
|
||||
weights.Min() << " " << weights.Max() << "\n";
|
||||
}
|
||||
|
||||
real_t change_surf_fit_err = surf_fit_avg_err_prvs-surf_fit_avg_err;
|
||||
real_t rel_change_surf_fit_err = change_surf_fit_err/surf_fit_avg_err_prvs;
|
||||
|
||||
real_t change_surf_fit_err = surf_fit_err_avg_prvs-surf_fit_err_avg;
|
||||
real_t rel_change_surf_fit_err = change_surf_fit_err/surf_fit_err_avg_prvs;
|
||||
// Increase the surface fitting coefficient if the surface fitting error
|
||||
// does not decrease sufficiently. If we are converging based on residual,
|
||||
// also make sure we have not reached the maximum fitting weight and
|
||||
// error threshold.
|
||||
if (rel_change_surf_fit_err < surf_fit_err_rel_change_limit &&
|
||||
(surf_fit_converge_error ||
|
||||
(fitweights.Max() < surf_fit_weight_limit &&
|
||||
surf_fit_max_err > surf_fit_max_err_limit)))
|
||||
// does not decrease sufficiently.
|
||||
if (rel_change_surf_fit_err < surf_fit_rel_change_threshold)
|
||||
{
|
||||
real_t scale_factor = std::min(surf_fit_scale_factor,
|
||||
surf_fit_weight_limit/fitweights.Max());
|
||||
UpdateSurfaceFittingWeight(scale_factor);
|
||||
surf_fit_adapt_count += 1;
|
||||
UpdateSurfaceFittingWeight(surf_fit_scale_factor);
|
||||
adapt_inc_count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
surf_fit_adapt_count = 0;
|
||||
adapt_inc_count = 0;
|
||||
}
|
||||
surf_fit_avg_err_prvs = surf_fit_avg_err;
|
||||
surf_fit_coeff_update = false;
|
||||
surf_fit_err_avg_prvs = surf_fit_err_avg;
|
||||
update_surf_fit_coeff = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-87
@@ -134,20 +134,18 @@ protected:
|
||||
int solver_type;
|
||||
bool parallel;
|
||||
|
||||
// Line search step is rejected if min(detJ) <= min_detJ_limit.
|
||||
real_t min_detJ_limit = 0.0;
|
||||
// Line search step is rejected if min(detJ) <= min_detJ_threshold.
|
||||
real_t min_detJ_threshold = 0.0;
|
||||
|
||||
// Surface fitting variables.
|
||||
mutable real_t surf_fit_avg_err_prvs = 10000.0;
|
||||
mutable real_t surf_fit_avg_err, surf_fit_max_err;
|
||||
mutable bool surf_fit_coeff_update = false;
|
||||
real_t surf_fit_max_err_limit = -1.0;
|
||||
real_t surf_fit_err_rel_change_limit = 0.001;
|
||||
mutable real_t surf_fit_err_avg_prvs = 10000.0;
|
||||
mutable real_t surf_fit_err_avg, surf_fit_err_max;
|
||||
mutable bool update_surf_fit_coeff = false;
|
||||
real_t surf_fit_max_threshold = -1.0;
|
||||
real_t surf_fit_rel_change_threshold = 0.001;
|
||||
real_t surf_fit_scale_factor = 0.0;
|
||||
mutable int surf_fit_adapt_count = 0;
|
||||
mutable int surf_fit_adapt_count_limit = 10;
|
||||
mutable real_t surf_fit_weight_limit = 1e10;
|
||||
bool surf_fit_converge_error = false;
|
||||
mutable int adapt_inc_count = 0;
|
||||
mutable int max_adapt_inc_count = 10;
|
||||
|
||||
// Minimum determinant over the whole mesh. Used for mesh untangling.
|
||||
real_t *min_det_ptr = nullptr;
|
||||
@@ -193,9 +191,6 @@ protected:
|
||||
void GetSurfaceFittingWeight(Array<real_t> &weights) const;
|
||||
///@}
|
||||
|
||||
/// Check if surface fitting is enabled.
|
||||
bool IsSurfaceFittingEnabled() const;
|
||||
|
||||
public:
|
||||
#ifdef MFEM_USE_MPI
|
||||
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule, int type = 0)
|
||||
@@ -229,94 +224,38 @@ public:
|
||||
/// (ii) surface fitting weight.
|
||||
virtual void ProcessNewState(const Vector &x) const;
|
||||
|
||||
/** @name Methods for adaptive surface fitting.
|
||||
\brief These methods control the behavior of the weight and the
|
||||
termination of the solver. (Experimental)
|
||||
|
||||
Adaptive fitting weight: The weight is modified after each
|
||||
TMOPNewtonSolver iteration as:
|
||||
w_{k+1} = w_{k} * \ref surf_fit_scale_factor if the relative
|
||||
change in average fitting error < \ref surf_fit_err_rel_change_limit.
|
||||
When converging based on the residual, we enforce the fitting weight
|
||||
to be at-most \ref surf_fit_weight_limit, and increase it only if the
|
||||
fitting error is below user prescribed threshold
|
||||
(\ref surf_fit_max_err_limit).
|
||||
See \ref SetAdaptiveSurfaceFittingScalingFactor and
|
||||
\ref SetAdaptiveSurfaceFittingRelativeChangeThreshold.
|
||||
|
||||
Note that the solver stops if the maximum surface fitting error
|
||||
does not sufficiently decrease for \ref surf_fit_adapt_count_limit (default 10)
|
||||
consecutive increments of the fitting weight during weight adaptation.
|
||||
This typically occurs when the mesh cannot align with the level-set
|
||||
without degrading element quality.
|
||||
See \ref SetMaxNumberofIncrementsForAdaptiveFitting.
|
||||
|
||||
Convergence criterion: There are two modes, residual- and error-based,
|
||||
which can be toggled using \ref SetSurfaceFittingConvergenceBasedOnError.
|
||||
|
||||
(i) Residual based (default): Stop when the norm of the gradient of the
|
||||
TMOP objective reaches the prescribed tolerance. This method is best used
|
||||
with a reasonable value for \ref surf_fit_weight_limit when the
|
||||
adaptive surface fitting scheme is used. See method
|
||||
\ref SetSurfaceFittingWeightLimit.
|
||||
|
||||
(ii) Error based: Stop when the maximum fitting error
|
||||
reaches the user-prescribed threshold, \ref surf_fit_max_err_limit.
|
||||
In this case, \ref surf_fit_weight_limit is ignored during weight
|
||||
adaptation.
|
||||
*/
|
||||
///@{
|
||||
/** @name Methods for adaptive surface fitting weight. (Experimental) */
|
||||
/// Enable/Disable adaptive surface fitting weight.
|
||||
/// The weight is modified after each TMOPNewtonSolver iteration as:
|
||||
/// w_{k+1} = w_{k} * @a surf_fit_scale_factor if relative change in
|
||||
/// max surface fitting error < @a surf_fit_rel_change_threshold.
|
||||
/// The solver terminates if the maximum surface fitting error does
|
||||
/// not sufficiently decrease for @a max_adapt_inc_count consecutive
|
||||
/// solver iterations or if the max error falls below @a surf_fit_max_threshold.
|
||||
void EnableAdaptiveSurfaceFitting()
|
||||
{
|
||||
surf_fit_scale_factor = 10.0;
|
||||
surf_fit_rel_change_threshold = 0.001;
|
||||
}
|
||||
void SetAdaptiveSurfaceFittingScalingFactor(real_t factor)
|
||||
{
|
||||
MFEM_VERIFY(factor > 1.0, "Scaling factor must be greater than 1.");
|
||||
surf_fit_scale_factor = factor;
|
||||
}
|
||||
void SetAdaptiveSurfaceFittingRelativeChangeThreshold(real_t threshold)
|
||||
{
|
||||
surf_fit_err_rel_change_limit = threshold;
|
||||
surf_fit_rel_change_threshold = threshold;
|
||||
}
|
||||
/// Used for stopping based on the number of consecutive failed weight
|
||||
/// adaptation iterations.
|
||||
// TODO: Rename to SetMaxNumberofIncrementsForAdaptiveSurfaceFitting
|
||||
// in future.
|
||||
void SetMaxNumberofIncrementsForAdaptiveFitting(int count)
|
||||
{
|
||||
surf_fit_adapt_count_limit = count;
|
||||
max_adapt_inc_count = count;
|
||||
}
|
||||
/// Used for error-based surface fitting termination.
|
||||
void SetTerminationWithMaxSurfaceFittingError(real_t max_error)
|
||||
{
|
||||
surf_fit_max_err_limit = max_error;
|
||||
surf_fit_converge_error = true;
|
||||
surf_fit_max_threshold = max_error;
|
||||
}
|
||||
/// Could be used with both error-based or residual-based convergence.
|
||||
void SetSurfaceFittingMaxErrorLimit(real_t max_error)
|
||||
{
|
||||
surf_fit_max_err_limit = max_error;
|
||||
}
|
||||
/// Used for residual-based surface fitting termination.
|
||||
void SetSurfaceFittingWeightLimit(real_t weight)
|
||||
{
|
||||
surf_fit_weight_limit = weight;
|
||||
}
|
||||
/// Toggle convergence based on residual or error.
|
||||
void SetSurfaceFittingConvergenceBasedOnError(bool mode)
|
||||
{
|
||||
surf_fit_converge_error = mode;
|
||||
if (surf_fit_converge_error)
|
||||
{
|
||||
MFEM_VERIFY(surf_fit_max_err_limit >= 0,
|
||||
"Fitting error based convergence requires the user to "
|
||||
"first set the error threshold."
|
||||
"See SetTerminationWithMaxSurfaceFittingError");
|
||||
}
|
||||
}
|
||||
///@}
|
||||
|
||||
/// Set minimum determinant enforced during line-search.
|
||||
void SetMinimumDeterminantThreshold(real_t threshold)
|
||||
{
|
||||
min_detJ_limit = threshold;
|
||||
min_detJ_threshold = threshold;
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &b, Vector &x) const
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ T Array<T>::Sum()
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int Array<T>::IsSorted() const
|
||||
int Array<T>::IsSorted()
|
||||
{
|
||||
T val_prev = operator[](0), val;
|
||||
for (int i = 1; i < size; i++)
|
||||
|
||||
+9
-28
@@ -74,14 +74,10 @@ public:
|
||||
inline Array(int asize, MemoryType mt)
|
||||
: size(asize) { asize > 0 ? data.New(asize, mt) : data.Reset(mt); }
|
||||
|
||||
/** @brief Creates array using an externally allocated host pointer @a data_
|
||||
to @a asize elements. If @a own_data is true, the array takes ownership
|
||||
of the pointer.
|
||||
|
||||
When @a own_data is true, the pointer @a data_ must be allocated with
|
||||
MemoryType given by MemoryManager::GetHostMemoryType(). */
|
||||
inline Array(T *data_, int asize, bool own_data = false)
|
||||
{ data.Wrap(data_, asize, own_data); size = asize; }
|
||||
/** @brief Creates array using an externally allocated pointer @a data_ to
|
||||
@a asize elements. The data pointer will not be deleted by Array. */
|
||||
inline Array(T *data_, int asize)
|
||||
{ data.Wrap(data_, asize, false); size = asize; }
|
||||
|
||||
/// Copy constructor: deep copy from @a src
|
||||
/** This method supports source arrays using any MemoryType. */
|
||||
@@ -209,14 +205,7 @@ public:
|
||||
inline void Copy(Array ©) const;
|
||||
|
||||
/// Make this Array a reference to a pointer.
|
||||
/** When @a own_data is true, the pointer @a data_ must be allocated with
|
||||
MemoryType given by MemoryManager::GetHostMemoryType(). */
|
||||
inline void MakeRef(T *data_, int size_, bool own_data = false);
|
||||
|
||||
/// Make this Array a reference to a pointer.
|
||||
/** When @a own_data is true, the pointer @a data_ must be allocated with
|
||||
MemoryType given by @a mt. */
|
||||
inline void MakeRef(T *data_, int size, MemoryType mt, bool own_data);
|
||||
inline void MakeRef(T *, int);
|
||||
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
@@ -273,7 +262,7 @@ public:
|
||||
}
|
||||
|
||||
/// Return 1 if the array is sorted from lowest to highest. Otherwise return 0.
|
||||
int IsSorted() const;
|
||||
int IsSorted();
|
||||
|
||||
/// Fill the entries of the array with the cumulative sum of the entries.
|
||||
void PartialSum();
|
||||
@@ -879,19 +868,11 @@ inline void Array<T>::Copy(Array ©) const
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(T *data_, int size_, bool own_data)
|
||||
inline void Array<T>::MakeRef(T *p, int s)
|
||||
{
|
||||
data.Delete();
|
||||
data.Wrap(data_, size_, own_data);
|
||||
size = size_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(T *data_, int size_, MemoryType mt, bool own_data)
|
||||
{
|
||||
data.Delete();
|
||||
data.Wrap(data_, size_, mt, own_data);
|
||||
size = size_;
|
||||
data.Wrap(p, s, false);
|
||||
size = s;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
||||
@@ -288,3 +288,4 @@ void ArraysByName<T>::Load(std::istream &in)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ void GroupTopology::Save(ostream &os) const
|
||||
os << "\ncommunication_groups\n";
|
||||
os << "number_of_groups " << NGroups() << "\n\n";
|
||||
|
||||
os << "# number of entities in each group, followed by ranks in group\n";
|
||||
os << "# number of entities in each group, followed by group ids in group\n";
|
||||
for (int group_id = 0; group_id < NGroups(); ++group_id)
|
||||
{
|
||||
int group_size = GetGroupSize(group_id);
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
#ifdef MFEM_USE_CEED
|
||||
#include "../fem/ceed/interface/util.hpp"
|
||||
#endif
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "../linalg/hypre.hpp"
|
||||
#endif
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
@@ -253,10 +250,6 @@ void Device::Configure(const std::string &device, const int device_id)
|
||||
|
||||
// Only '*this' will call the MemoryManager::Destroy() method.
|
||||
destroy_mm = true;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
Hypre::InitDevice();
|
||||
#endif
|
||||
}
|
||||
|
||||
// static method
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#include "device.hpp"
|
||||
#include "mem_manager.hpp"
|
||||
#include "../linalg/dtensor.hpp"
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include <_hypre_utilities.h>
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -783,63 +780,6 @@ inline void forall_3D_grid(int N, int X, int Y, int Z, int G, lambda &&body)
|
||||
ForallWrap<3>(true, N, body, X, Y, Z, G);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
// Function mfem::hypre_forall_cpu() similar to mfem::forall, but it always
|
||||
// executes on the CPU using sequential or OpenMP-parallel execution based on
|
||||
// the hypre build time configuration.
|
||||
template<typename lambda>
|
||||
inline void hypre_forall_cpu(int N, lambda &&body)
|
||||
{
|
||||
#ifdef HYPRE_USING_OPENMP
|
||||
#pragma omp parallel for HYPRE_SMP_SCHEDULE
|
||||
#endif
|
||||
for (int i = 0; i < N; i++) { body(i); }
|
||||
}
|
||||
|
||||
// Function mfem::hypre_forall_gpu() similar to mfem::forall, but it always
|
||||
// executes on the GPU device that hypre was configured with at build time.
|
||||
#if defined(HYPRE_USING_GPU)
|
||||
template<typename lambda>
|
||||
inline void hypre_forall_gpu(int N, lambda &&body)
|
||||
{
|
||||
#if defined(HYPRE_USING_CUDA)
|
||||
CuWrap1D(N, body);
|
||||
#elif defined(HYPRE_USING_HIP)
|
||||
HipWrap1D(N, body);
|
||||
#else
|
||||
#error Unknown HYPRE GPU backend!
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Function mfem::hypre_forall() similar to mfem::forall, but it executes on the
|
||||
// device, CPU or GPU, that hypre was configured with at build time (when the
|
||||
// HYPRE version is < 2.31.0) or at runtime (when HYPRE was configured with GPU
|
||||
// support at build time and HYPRE's version is >= 2.31.0). This selection is
|
||||
// generally independent of what device was selected in MFEM's runtime
|
||||
// configuration.
|
||||
template<typename lambda>
|
||||
inline void hypre_forall(int N, lambda &&body)
|
||||
{
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
hypre_forall_cpu(N, body);
|
||||
#elif MFEM_HYPRE_VERSION < 23100
|
||||
hypre_forall_gpu(N, body);
|
||||
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
|
||||
if (!HypreUsingGPU())
|
||||
{
|
||||
hypre_forall_cpu(N, body);
|
||||
}
|
||||
else
|
||||
{
|
||||
hypre_forall_gpu(N, body);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_FORALL_HPP
|
||||
|
||||
@@ -1154,10 +1154,6 @@ void MemoryManager::Copy_(void *dst_h_ptr, const void *src_h_ptr,
|
||||
// dest d | h2d d2d d2d
|
||||
// hd | h2h d2d d2d
|
||||
|
||||
MFEM_ASSERT(bytes != 0, "this method should not be called with bytes = 0");
|
||||
MFEM_ASSERT(dst_h_ptr != nullptr, "invalid dst_h_ptr = nullptr");
|
||||
MFEM_ASSERT(src_h_ptr != nullptr, "invalid src_h_ptr = nullptr");
|
||||
|
||||
const bool dst_on_host =
|
||||
(dst_flags & Mem::VALID_HOST) &&
|
||||
(!(dst_flags & Mem::VALID_DEVICE) ||
|
||||
@@ -1233,10 +1229,6 @@ void MemoryManager::Copy_(void *dst_h_ptr, const void *src_h_ptr,
|
||||
void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
size_t bytes, unsigned src_flags)
|
||||
{
|
||||
MFEM_ASSERT(bytes != 0, "this method should not be called with bytes = 0");
|
||||
MFEM_ASSERT(dest_h_ptr != nullptr, "invalid dest_h_ptr = nullptr");
|
||||
MFEM_ASSERT(src_h_ptr != nullptr, "invalid src_h_ptr = nullptr");
|
||||
|
||||
const bool src_on_host = src_flags & Mem::VALID_HOST;
|
||||
if (src_on_host)
|
||||
{
|
||||
@@ -1263,10 +1255,6 @@ void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
size_t bytes, unsigned &dest_flags)
|
||||
{
|
||||
MFEM_ASSERT(bytes != 0, "this method should not be called with bytes = 0");
|
||||
MFEM_ASSERT(dest_h_ptr != nullptr, "invalid dest_h_ptr = nullptr");
|
||||
MFEM_ASSERT(src_h_ptr != nullptr, "invalid src_h_ptr = nullptr");
|
||||
|
||||
const bool dest_on_host = dest_flags & Mem::VALID_HOST;
|
||||
if (dest_on_host)
|
||||
{
|
||||
|
||||
+8
-57
@@ -18,14 +18,8 @@
|
||||
#include <cstring> // std::memcpy
|
||||
#include <type_traits> // std::is_const
|
||||
#include <cstddef> // std::max_align_t
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Enable internal hypre timing routines
|
||||
#define HYPRE_TIMING
|
||||
#include <HYPRE_utilities.h> // for HYPRE_GetMemoryLocation() and others
|
||||
#if (21400 <= MFEM_HYPRE_VERSION) && (MFEM_HYPRE_VERSION < 21900)
|
||||
#include <_hypre_utilities.h> // for HYPRE_MEMORY_HOST and others
|
||||
#endif
|
||||
#include <HYPRE_config.h> // HYPRE_USING_GPU
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
@@ -875,45 +869,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#if MFEM_HYPRE_VERSION < 21400
|
||||
#define HYPRE_MEMORY_DEVICE (0)
|
||||
#define HYPRE_MEMORY_HOST (1)
|
||||
#endif
|
||||
#if MFEM_HYPRE_VERSION < 21900
|
||||
typedef int HYPRE_MemoryLocation;
|
||||
#endif
|
||||
|
||||
/// Return the configured HYPRE_MemoryLocation
|
||||
inline HYPRE_MemoryLocation GetHypreMemoryLocation()
|
||||
{
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
return HYPRE_MEMORY_HOST;
|
||||
#elif MFEM_HYPRE_VERSION < 23100
|
||||
return HYPRE_MEMORY_DEVICE;
|
||||
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
|
||||
HYPRE_MemoryLocation loc;
|
||||
HYPRE_GetMemoryLocation(&loc);
|
||||
return loc;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Return true if HYPRE is configured to use GPU
|
||||
inline bool HypreUsingGPU()
|
||||
{
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
return false;
|
||||
#elif MFEM_HYPRE_VERSION < 23100
|
||||
return true;
|
||||
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
|
||||
return GetHypreMemoryLocation() != HYPRE_MEMORY_HOST;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
|
||||
// Inline methods
|
||||
|
||||
template <typename T>
|
||||
@@ -1049,12 +1004,10 @@ inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
|
||||
// If the following condition is true then MemoryManager::Exists()
|
||||
// should also be true:
|
||||
IsDeviceMemory(MemoryManager::GetDeviceMemoryType())
|
||||
#elif MFEM_HYPRE_VERSION < 23100
|
||||
// When HYPRE_USING_GPU is defined and HYPRE < 2.31.0, we always
|
||||
// register the 'base' if the MemoryManager::Exists():
|
||||
#else
|
||||
// When HYPRE_USING_GPU is defined we always register the 'base' if
|
||||
// the MemoryManager::Exists():
|
||||
MemoryManager::Exists()
|
||||
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
|
||||
MemoryManager::Exists() && HypreUsingGPU()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
@@ -1260,10 +1213,9 @@ template <typename T>
|
||||
inline void Memory<T>::CopyFrom(const Memory &src, int size)
|
||||
{
|
||||
MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
|
||||
if (size <= 0) { return; }
|
||||
if (!(flags & Registered) && !(src.flags & Registered))
|
||||
{
|
||||
if (h_ptr != src.h_ptr)
|
||||
if (h_ptr != src.h_ptr && size != 0)
|
||||
{
|
||||
MFEM_ASSERT(h_ptr + size <= src.h_ptr || src.h_ptr + size <= h_ptr,
|
||||
"data overlaps!");
|
||||
@@ -1281,10 +1233,9 @@ template <typename T>
|
||||
inline void Memory<T>::CopyFromHost(const T *src, int size)
|
||||
{
|
||||
MFEM_VERIFY(capacity>=size, "Incorrect size");
|
||||
if (size <= 0) { return; }
|
||||
if (!(flags & Registered))
|
||||
{
|
||||
if (h_ptr != src)
|
||||
if (h_ptr != src && size != 0)
|
||||
{
|
||||
MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
|
||||
"data overlaps!");
|
||||
@@ -1301,6 +1252,7 @@ inline void Memory<T>::CopyFromHost(const T *src, int size)
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyTo(Memory &dest, int size) const
|
||||
{
|
||||
MFEM_VERIFY(capacity>=size, "Incorrect size");
|
||||
dest.CopyFrom(*this, size);
|
||||
}
|
||||
|
||||
@@ -1308,10 +1260,9 @@ template <typename T>
|
||||
inline void Memory<T>::CopyToHost(T *dest, int size) const
|
||||
{
|
||||
MFEM_VERIFY(capacity>=size, "Incorrect size");
|
||||
if (size <= 0) { return; }
|
||||
if (!(flags & Registered))
|
||||
{
|
||||
if (h_ptr != dest)
|
||||
if (h_ptr != dest && size != 0)
|
||||
{
|
||||
MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
|
||||
"data overlaps!");
|
||||
|
||||
@@ -134,7 +134,7 @@ int socketbuf::open(const char hostname[], int port)
|
||||
{
|
||||
closesocket(socket_descriptor);
|
||||
socket_descriptor = -2;
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -148,7 +148,7 @@ int socketbuf::open(const char hostname[], int port)
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return (socket_descriptor < 0) ? -1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socketbuf::close()
|
||||
|
||||
+2
-7
@@ -95,7 +95,7 @@ public:
|
||||
not called, it returns the number of possible connections established
|
||||
by the used constructor. Otherwise, it is exactly the number of
|
||||
established connections before calling Finalize(). */
|
||||
inline int Size_of_connections() const { HostReadI(); return I[size]; }
|
||||
inline int Size_of_connections() const { return I[size]; }
|
||||
|
||||
/** Returns index of the connection between element i of TYPE I and
|
||||
element j of TYPE II. If there is no connection between element i
|
||||
@@ -207,12 +207,7 @@ template <> inline void Swap<Table>(Table &a, Table &b)
|
||||
void Transpose (const Table &A, Table &At, int ncols_A_ = -1);
|
||||
Table * Transpose (const Table &A);
|
||||
|
||||
/// @brief Transpose an Array<int>.
|
||||
///
|
||||
/// The array @a A represents a table where each row @a i has exactly one
|
||||
/// connection to the column (TYPE II) index specified by @a A[i].
|
||||
///
|
||||
/// @note The column (TYPE II) indices in each row of @a At will be sorted.
|
||||
/// Transpose an Array<int>
|
||||
void Transpose(const Array<int> &A, Table &At, int ncols_A_ = -1);
|
||||
|
||||
/// C = A * B (as boolean matrices)
|
||||
|
||||
@@ -400,9 +400,6 @@ inline double StopWatch::SystTime()
|
||||
|
||||
StopWatch::StopWatch() : M(new internal::StopWatch) { }
|
||||
|
||||
StopWatch::StopWatch(const StopWatch &sw)
|
||||
: M(new internal::StopWatch(*(sw.M))) { }
|
||||
|
||||
void StopWatch::Clear()
|
||||
{
|
||||
M->Clear();
|
||||
|
||||
@@ -40,7 +40,6 @@ private:
|
||||
public:
|
||||
/// Creates a new (stopped) StopWatch object.
|
||||
StopWatch();
|
||||
StopWatch(const StopWatch &);
|
||||
|
||||
/// Clear the elapsed time on the stopwatch and restart it if it's running.
|
||||
void Clear();
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
#include "amgxsolver.hpp"
|
||||
#ifdef MFEM_USE_AMGX
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "../general/communication.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ void BlockOperator::Mult (const Vector & x, Vector & y) const
|
||||
tmp.SetSize(row_offsets[iRow+1] - row_offsets[iRow]);
|
||||
for (int jCol=0; jCol < nColBlocks; ++jCol)
|
||||
{
|
||||
if (op(iRow,jCol) && coef(iRow,jCol) != 0.)
|
||||
if (op(iRow,jCol))
|
||||
{
|
||||
op(iRow,jCol)->Mult(xblock.GetBlock(jCol), tmp);
|
||||
yblock.GetBlock(iRow).Add(coef(iRow,jCol), tmp);
|
||||
@@ -112,7 +112,7 @@ void BlockOperator::MultTranspose (const Vector & x, Vector & y) const
|
||||
tmp.SetSize(col_offsets[iRow+1] - col_offsets[iRow]);
|
||||
for (int jCol=0; jCol < nRowBlocks; ++jCol)
|
||||
{
|
||||
if (op(jCol,iRow) && coef(jCol,iRow) != 0.)
|
||||
if (op(jCol,iRow))
|
||||
{
|
||||
op(jCol,iRow)->MultTranspose(xblock.GetBlock(jCol), tmp);
|
||||
yblock.GetBlock(iRow).Add(coef(jCol,iRow), tmp);
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "cpardiso.hpp"
|
||||
#include "hypre.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
@@ -532,69 +532,6 @@ MatrixInverse *DenseMatrix::Inverse() const
|
||||
return new DenseMatrixInverse(*this);
|
||||
}
|
||||
|
||||
void DenseMatrix::Exponential()
|
||||
{
|
||||
MFEM_ASSERT(Height() == Width() && Height() <= 2,
|
||||
"The matrix must be square and "
|
||||
<< "of size less than or equal to 2."
|
||||
<< " Height() = " << Height()
|
||||
<< ", Width() = " << Width());
|
||||
|
||||
switch (Height())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
data[0] = std::exp(data[0]);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
/// Formulas from Corollary 2.4 of doi:10.1109/9.233156
|
||||
/// Note typo in the paper, in the prefactor in the equation under (i).
|
||||
const real_t a = data[0];
|
||||
const real_t b = data[1];
|
||||
const real_t c = data[2];
|
||||
const real_t d = data[3];
|
||||
const real_t e = (a - d)*(a - d) + 4*b*c;
|
||||
const real_t f = std::exp((a + d)/2.0);
|
||||
const real_t g = std::sqrt(std::abs(e)) / 2.0;
|
||||
|
||||
if (e == 0)
|
||||
{
|
||||
data[0] = 1.0 + (a - d)/2.0;
|
||||
data[3] = 1.0 - (a - d)/2.0;
|
||||
}
|
||||
else if (e > 0)
|
||||
{
|
||||
data[0] = std::cosh(g) + (a - d)/2 * std::sinh(g) / g;
|
||||
data[1] = b * std::sinh(g) / g;
|
||||
data[2] = c * std::sinh(g) / g;
|
||||
data[3] = std::cosh(g) - (a - d)/2 * std::sinh(g) / g;
|
||||
}
|
||||
else
|
||||
{
|
||||
data[0] = std::cos(g) + (a - d)/2 * std::sin(g) / g;
|
||||
data[1] = b * std::sin(g) / g;
|
||||
data[2] = c * std::sin(g) / g;
|
||||
data[3] = std::cos(g) - (a - d)/2 * std::sin(g) / g;
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
data[i] *= f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
MFEM_ABORT("3x3 matrices are not currently supported");
|
||||
}
|
||||
default:
|
||||
{
|
||||
MFEM_ABORT("Only 1x1 and 2x2 matrices are currently supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
real_t DenseMatrix::Det() const
|
||||
{
|
||||
MFEM_ASSERT(Height() == Width() && Height() > 0,
|
||||
@@ -3280,93 +3217,6 @@ void MultAtB(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &AtB)
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddMultAtB(const DenseMatrix &A, const DenseMatrix &B,
|
||||
DenseMatrix &AtB)
|
||||
{
|
||||
MFEM_ASSERT(AtB.Height() == A.Width() && AtB.Width() == B.Width() &&
|
||||
A.Height() == B.Height(), "incompatible dimensions");
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
static char transa = 'T', transb = 'N';
|
||||
static real_t alpha = 1.0, beta = 1.0;
|
||||
int m = A.Width(), n = B.Width(), k = A.Height();
|
||||
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
sgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k,
|
||||
#elif defined MFEM_USE_DOUBLE
|
||||
dgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k,
|
||||
#endif
|
||||
B.Data(), &k, &beta, AtB.Data(), &m);
|
||||
#else
|
||||
const int ah = A.Height();
|
||||
const int aw = A.Width();
|
||||
const int bw = B.Width();
|
||||
const real_t *ad = A.Data();
|
||||
const real_t *bd = B.Data();
|
||||
real_t *cd = AtB.Data();
|
||||
|
||||
for (int j = 0; j < bw; j++)
|
||||
{
|
||||
const real_t *ap = ad;
|
||||
for (int i = 0; i < aw; i++)
|
||||
{
|
||||
real_t d = 0.0;
|
||||
for (int k = 0; k < ah; k++)
|
||||
{
|
||||
d += ap[k] * bd[k];
|
||||
}
|
||||
*(cd++) += d;
|
||||
ap += ah;
|
||||
}
|
||||
bd += ah;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddMult_a_AtB(real_t a, const DenseMatrix &A, const DenseMatrix &B,
|
||||
DenseMatrix &AtB)
|
||||
{
|
||||
MFEM_ASSERT(AtB.Height() == A.Width() && AtB.Width() == B.Width() &&
|
||||
A.Height() == B.Height(), "incompatible dimensions");
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
static char transa = 'T', transb = 'N';
|
||||
real_t alpha = a;
|
||||
static real_t beta = 1.0;
|
||||
int m = A.Width(), n = B.Width(), k = A.Height();
|
||||
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
sgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k,
|
||||
#elif defined MFEM_USE_DOUBLE
|
||||
dgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k,
|
||||
#endif
|
||||
B.Data(), &k, &beta, AtB.Data(), &m);
|
||||
#else
|
||||
const int ah = A.Height();
|
||||
const int aw = A.Width();
|
||||
const int bw = B.Width();
|
||||
const real_t *ad = A.Data();
|
||||
const real_t *bd = B.Data();
|
||||
real_t *cd = AtB.Data();
|
||||
|
||||
for (int j = 0; j < bw; j++)
|
||||
{
|
||||
const real_t *ap = ad;
|
||||
for (int i = 0; i < aw; i++)
|
||||
{
|
||||
real_t d = 0.0;
|
||||
for (int k = 0; k < ah; k++)
|
||||
{
|
||||
d += ap[k] * bd[k];
|
||||
}
|
||||
*(cd++) += a * d;
|
||||
ap += ah;
|
||||
}
|
||||
bd += ah;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddMult_a_AAt(real_t a, const DenseMatrix &A, DenseMatrix &AAt)
|
||||
{
|
||||
real_t d;
|
||||
|
||||
@@ -207,10 +207,6 @@ public:
|
||||
/// Replaces the current matrix with its square root inverse
|
||||
void SquareRootInverse();
|
||||
|
||||
/// Replaces the current matrix with its exponential
|
||||
/// (currently only supports 2x2 matrices)
|
||||
void Exponential();
|
||||
|
||||
/// Calculates the determinant of the matrix
|
||||
/// (optimized for 2x2, 3x3, and 4x4 matrices)
|
||||
real_t Det() const;
|
||||
@@ -584,13 +580,6 @@ void AddMult_a_ABt(real_t a, const DenseMatrix &A, const DenseMatrix &B,
|
||||
/// Multiply the transpose of a matrix A with a matrix B: At*B
|
||||
void MultAtB(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &AtB);
|
||||
|
||||
/// AtB += A^t * B
|
||||
void AddMultAtB(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &AtB);
|
||||
|
||||
/// AtB += a * A^t * B
|
||||
void AddMult_a_AtB(real_t a, const DenseMatrix &A, const DenseMatrix &B,
|
||||
DenseMatrix &AtB);
|
||||
|
||||
/// AAt += a * A * A^t
|
||||
void AddMult_a_AAt(real_t a, const DenseMatrix &A, DenseMatrix &AAt);
|
||||
|
||||
|
||||
+260
-371
File diff suppressed because it is too large
Load Diff
+49
-139
@@ -16,19 +16,17 @@
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "../general/globals.hpp"
|
||||
#include "sparsemat.hpp"
|
||||
#include "hypre_parcsr.hpp"
|
||||
#include <mpi.h>
|
||||
|
||||
// Enable internal hypre timing routines
|
||||
#define HYPRE_TIMING
|
||||
|
||||
// hypre header files
|
||||
#include <seq_mv.h>
|
||||
#include <temp_multivector.h>
|
||||
#include <_hypre_parcsr_mv.h>
|
||||
#include <_hypre_parcsr_ls.h>
|
||||
#include "seq_mv.h"
|
||||
#include "_hypre_parcsr_mv.h"
|
||||
#include "_hypre_parcsr_ls.h"
|
||||
#include "temp_multivector.h"
|
||||
#include "../general/globals.hpp"
|
||||
|
||||
#ifdef HYPRE_COMPLEX
|
||||
#error "MFEM does not work with HYPRE's complex numbers support"
|
||||
@@ -53,6 +51,22 @@
|
||||
#error "MFEM_USE_HIP=YES is required when HYPRE is built with HIP!"
|
||||
#endif
|
||||
|
||||
// MFEM_HYPRE_FORALL is a macro similar to mfem::forall, but it executes on the
|
||||
// device that hypre was configured with (no matter what device was selected
|
||||
// in MFEM's runtime configuration).
|
||||
#if defined(HYPRE_USING_CUDA)
|
||||
#define MFEM_HYPRE_FORALL(i, N,...) CuWrap1D(N, [=] MFEM_DEVICE \
|
||||
(int i) {__VA_ARGS__})
|
||||
#elif defined(HYPRE_USING_HIP)
|
||||
#define MFEM_HYPRE_FORALL(i, N,...) HipWrap1D(N, [=] MFEM_DEVICE \
|
||||
(int i) {__VA_ARGS__})
|
||||
#else
|
||||
#define MFEM_HYPRE_FORALL(i, N,...) for (int i = 0; i < N; i++) { __VA_ARGS__ }
|
||||
#endif
|
||||
|
||||
#include "sparsemat.hpp"
|
||||
#include "hypre_parcsr.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -73,22 +87,6 @@ public:
|
||||
/// Calling HYPRE_Finalize() manually is not compatible with this class.
|
||||
static void Init() { Instance(); }
|
||||
|
||||
/// @brief Configure HYPRE's compute and memory policy.
|
||||
///
|
||||
/// By default HYPRE will be configured with the same policy as MFEM unless
|
||||
/// `Hypre::configure_runtime_policy_from_mfem` is false, in which case
|
||||
/// HYPRE's default will be used; if HYPRE is built for the GPU and the
|
||||
/// aforementioned variable is false then HYPRE will use the GPU even if MFEM
|
||||
/// is not.
|
||||
///
|
||||
/// This function is no-op if HYPRE is built without GPU support or the HYPRE
|
||||
/// version is less than 2.31.0.
|
||||
///
|
||||
/// This function is NOT called by Init(). Instead it is called by
|
||||
/// Device::Configure() (when MFEM_USE_MPI=YES) after the MFEM device
|
||||
/// configuration is complete.
|
||||
static void InitDevice();
|
||||
|
||||
/// @brief Finalize hypre (called automatically at program exit if
|
||||
/// Hypre::Init() has been called).
|
||||
///
|
||||
@@ -96,13 +94,6 @@ public:
|
||||
/// called manually to more precisely control when hypre is finalized.
|
||||
static void Finalize();
|
||||
|
||||
/// @brief Use MFEM's device policy to configure HYPRE's device policy, true
|
||||
/// by default. This variable is used by InitDevice().
|
||||
///
|
||||
/// This value is not used if HYPRE is build without GPU support or the HYPRE
|
||||
/// version is less than 2.31.0.
|
||||
static bool configure_runtime_policy_from_mfem;
|
||||
|
||||
private:
|
||||
/// Calls HYPRE_Init() when the singleton is constructed.
|
||||
Hypre();
|
||||
@@ -151,28 +142,15 @@ inline int to_int(HYPRE_Int i)
|
||||
|
||||
|
||||
/// The MemoryClass used by Hypre objects.
|
||||
inline MemoryClass GetHypreMemoryClass()
|
||||
inline constexpr MemoryClass GetHypreMemoryClass()
|
||||
{
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
return MemoryClass::HOST;
|
||||
#elif MFEM_HYPRE_VERSION < 23100
|
||||
#if defined(HYPRE_USING_UNIFIED_MEMORY)
|
||||
#elif defined(HYPRE_USING_UNIFIED_MEMORY)
|
||||
return MemoryClass::MANAGED;
|
||||
#else
|
||||
return MemoryClass::DEVICE;
|
||||
#endif
|
||||
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
|
||||
if (GetHypreMemoryLocation() == HYPRE_MEMORY_HOST)
|
||||
{
|
||||
return MemoryClass::HOST;
|
||||
}
|
||||
// Return the actual memory location, see hypre_GetActualMemLocation():
|
||||
#if defined(HYPRE_USING_UNIFIED_MEMORY)
|
||||
return MemoryClass::MANAGED;
|
||||
#else
|
||||
return MemoryClass::DEVICE;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// The MemoryType used by MFEM when allocating arrays for Hypre objects.
|
||||
@@ -180,27 +158,13 @@ inline MemoryType GetHypreMemoryType()
|
||||
{
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
return Device::GetHostMemoryType();
|
||||
#elif MFEM_HYPRE_VERSION < 23100
|
||||
#if defined(HYPRE_USING_UNIFIED_MEMORY)
|
||||
#elif defined(HYPRE_USING_UNIFIED_MEMORY)
|
||||
return MemoryType::MANAGED;
|
||||
#else
|
||||
return MemoryType::DEVICE;
|
||||
#endif
|
||||
#else // HYPRE_USING_GPU is defined and MFEM_HYPRE_VERSION >= 23100
|
||||
if (GetHypreMemoryLocation() == HYPRE_MEMORY_HOST)
|
||||
{
|
||||
return Device::GetHostMemoryType();
|
||||
}
|
||||
// Return the actual memory location, see hypre_GetActualMemLocation():
|
||||
#if defined(HYPRE_USING_UNIFIED_MEMORY)
|
||||
return MemoryType::MANAGED;
|
||||
#else
|
||||
return MemoryType::DEVICE;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Wrapper for hypre's parallel vector class
|
||||
class HypreParVector : public Vector
|
||||
{
|
||||
@@ -364,10 +328,10 @@ public:
|
||||
HYPRE_Int Randomize(HYPRE_Int seed);
|
||||
|
||||
/// Prints the locally owned rows in parallel
|
||||
void Print(const std::string &fname) const;
|
||||
void Print(const char *fname) const;
|
||||
|
||||
/// Reads a HypreParVector from files saved with HypreParVector::Print
|
||||
void Read(MPI_Comm comm, const std::string &fname);
|
||||
void Read(MPI_Comm comm, const char *fname);
|
||||
|
||||
/// Calls hypre's destroy function
|
||||
~HypreParVector();
|
||||
@@ -919,14 +883,12 @@ public:
|
||||
const Memory<HYPRE_Int> &GetDiagMemoryJ() const { return mem_diag.J; }
|
||||
const Memory<real_t> &GetDiagMemoryData() const { return mem_diag.data; }
|
||||
|
||||
/// @brief Prints the locally owned rows in parallel. The resulting files can
|
||||
/// be read with Read_IJMatrix().
|
||||
void Print(const std::string &fname, HYPRE_Int offi = 0,
|
||||
HYPRE_Int offj = 0) const;
|
||||
/// Prints the locally owned rows in parallel
|
||||
void Print(const char *fname, HYPRE_Int offi = 0, HYPRE_Int offj = 0) const;
|
||||
/// Reads the matrix from a file
|
||||
void Read(MPI_Comm comm, const std::string &fname);
|
||||
void Read(MPI_Comm comm, const char *fname);
|
||||
/// Read a matrix saved as a HYPRE_IJMatrix
|
||||
void Read_IJMatrix(MPI_Comm comm, const std::string &fname);
|
||||
void Read_IJMatrix(MPI_Comm comm, const char *fname);
|
||||
|
||||
/// Print information about the hypre_ParCSRCommPkg of the HypreParMatrix.
|
||||
void PrintCommPkg(std::ostream &out = mfem::out) const;
|
||||
@@ -1075,40 +1037,29 @@ protected:
|
||||
bool A_is_symmetric;
|
||||
|
||||
public:
|
||||
/// HYPRE smoother types
|
||||
enum Type
|
||||
{
|
||||
Jacobi = 0, ///< Jacobi
|
||||
l1Jacobi = 1, ///< l1-scaled Jacobi
|
||||
l1GS = 2, ///< l1-scaled block Gauss-Seidel/SSOR
|
||||
l1GStr = 4, ///< truncated l1-scaled block Gauss-Seidel/SSOR
|
||||
lumpedJacobi = 5, ///< lumped Jacobi
|
||||
GS = 6, ///< Gauss-Seidel
|
||||
OPFS = 10, /**< On-processor forward solve for matrix w/ triangular
|
||||
structure */
|
||||
Chebyshev = 16, ///< Chebyshev
|
||||
Taubin = 1001, ///< Taubin polynomial smoother
|
||||
FIR = 1002 ///< FIR polynomial smoother
|
||||
};
|
||||
|
||||
/// @deprecated Use DefaultType() instead
|
||||
/** Hypre smoother types:
|
||||
0 = Jacobi
|
||||
1 = l1-scaled Jacobi
|
||||
2 = l1-scaled block Gauss-Seidel/SSOR
|
||||
4 = truncated l1-scaled block Gauss-Seidel/SSOR
|
||||
5 = lumped Jacobi
|
||||
6 = Gauss-Seidel
|
||||
10 = On-processor forward solve for matrix w/ triangular structure
|
||||
16 = Chebyshev
|
||||
1001 = Taubin polynomial smoother
|
||||
1002 = FIR polynomial smoother. */
|
||||
enum Type { Jacobi = 0, l1Jacobi = 1, l1GS = 2, l1GStr = 4, lumpedJacobi = 5,
|
||||
GS = 6, OPFS = 10, Chebyshev = 16, Taubin = 1001, FIR = 1002
|
||||
};
|
||||
#if !defined(HYPRE_USING_GPU)
|
||||
MFEM_DEPRECATED static constexpr Type default_type = l1GS;
|
||||
static constexpr Type default_type = l1GS;
|
||||
#else
|
||||
MFEM_DEPRECATED static constexpr Type default_type = l1Jacobi;
|
||||
static constexpr Type default_type = l1Jacobi;
|
||||
#endif
|
||||
|
||||
/** @brief Default value for the smoother type used by the constructors:
|
||||
Type::l1GS when HYPRE is running on CPU and Type::l1Jacobi when HYPRE is
|
||||
running on GPU. */
|
||||
static Type DefaultType()
|
||||
{
|
||||
return HypreUsingGPU() ? l1Jacobi : l1GS;
|
||||
}
|
||||
|
||||
HypreSmoother();
|
||||
|
||||
HypreSmoother(const HypreParMatrix &A_, int type = DefaultType(),
|
||||
HypreSmoother(const HypreParMatrix &A_, int type = default_type,
|
||||
int relax_times = 1, real_t relax_weight = 1.0,
|
||||
real_t omega = 1.0, int poly_order = 2,
|
||||
real_t poly_fraction = .3, int eig_est_cg_iter = 10);
|
||||
@@ -1523,53 +1474,12 @@ public:
|
||||
|
||||
virtual void SetOperator(const Operator &op);
|
||||
|
||||
/// Set the threshold and levels parameters
|
||||
/** The accuracy and cost of ParaSails are parametrized by the real
|
||||
* @a thresh and integer @a nlevels parameters (0<=thresh<=1, 0<=nlevels).
|
||||
* Lower values of @a thresh and higher values of @a nlevels lead to
|
||||
* more accurate, but more expensive preconditioners. More accurate
|
||||
* preconditioners are also more expensive per iteration. The default
|
||||
* values are @a thresh = 0.1 and @a nlevels = 1.
|
||||
*/
|
||||
void SetParams(real_t thresh, int nlevels);
|
||||
|
||||
/// Set the filter parameter
|
||||
/** The filter parameter is used to drop small nonzeros in the preconditioner,
|
||||
* to reduce the cost of applying the preconditioner. Values from 0.055
|
||||
* to 0.1 are recommended. The default value is 0.1.
|
||||
*/
|
||||
void SetParams(real_t threshold, int max_levels);
|
||||
void SetFilter(real_t filter);
|
||||
|
||||
/// Set symmetry parameter
|
||||
/** The recognized options are:
|
||||
* 0 = nonsymmetric and/or indefinite problem, and nonsymmetric preconditioner
|
||||
* 1 = SPD problem, and SPD (factored) preconditioner
|
||||
* 2 = nonsymmetric, definite problem, and SPD (factored) preconditioner
|
||||
*/
|
||||
void SetSymmetry(int sym);
|
||||
|
||||
/// Set the load balance parameter
|
||||
/** A zero value indicates that no load balance is attempted; a value
|
||||
* of unity indicates that perfect load balance will be attempted. The
|
||||
* recommended value is 0.9 to balance the overhead of data exchanges
|
||||
* for load balancing. No load balancing is needed if the preconditioner
|
||||
* is very sparse and fast to construct. The default value is 0.
|
||||
*/
|
||||
void SetLoadBal(real_t loadbal);
|
||||
|
||||
/// Set the pattern reuse parameter
|
||||
/** A nonzero value indicates that the pattern of the preconditioner
|
||||
* should be reused for subsequent constructions of the proconditioner.
|
||||
* A zero value inicates that the peconditioner should be constructed
|
||||
* from scratch. The default value is 0.
|
||||
*/
|
||||
void SetReuse(int reuse);
|
||||
|
||||
/// Set the logging parameter
|
||||
/** A nonzero value prints statistics of the setup procedure to stdout.
|
||||
* The default value of this parameter is 1.
|
||||
*/
|
||||
void SetLogging(int logging);
|
||||
void SetSymmetry(int sym);
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal sai_precond
|
||||
virtual operator HYPRE_Solver() const { return sai_precond; }
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "../general/mem_manager.hpp"
|
||||
// Enable internal hypre timing routines
|
||||
#define HYPRE_TIMING
|
||||
|
||||
// hypre header files
|
||||
#include <_hypre_parcsr_mv.h>
|
||||
#include "../general/mem_manager.hpp"
|
||||
#include "_hypre_parcsr_mv.h"
|
||||
|
||||
// Older hypre versions do not define HYPRE_BigInt and HYPRE_MPI_BIG_INT, so we
|
||||
// define them here for backward compatibility.
|
||||
@@ -45,10 +46,10 @@ typedef HYPRE_Int HYPRE_BigInt;
|
||||
#else // MFEM_HYPRE_VERSION >= 21400
|
||||
|
||||
#define mfem_hypre_TAlloc(type, size) \
|
||||
hypre_TAlloc(type, size, mfem::GetHypreMemoryLocation())
|
||||
hypre_TAlloc(type, size, HYPRE_MEMORY_DEVICE)
|
||||
#define mfem_hypre_CTAlloc(type, size) \
|
||||
hypre_CTAlloc(type, size, mfem::GetHypreMemoryLocation())
|
||||
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr, mfem::GetHypreMemoryLocation())
|
||||
hypre_CTAlloc(type, size, HYPRE_MEMORY_DEVICE)
|
||||
#define mfem_hypre_TFree(ptr) hypre_TFree(ptr, HYPRE_MEMORY_DEVICE)
|
||||
|
||||
#define mfem_hypre_TAlloc_host(type, size) \
|
||||
hypre_TAlloc(type, size, HYPRE_MEMORY_HOST)
|
||||
|
||||
+52
-130
@@ -309,51 +309,18 @@ public:
|
||||
|
||||
|
||||
/// Base abstract class for first order time dependent operators.
|
||||
/** Operator of the form: (u,t) -> k(u,t), where k generally solves the
|
||||
algebraic equation F(u,k,t) = G(u,t). The functions F and G represent the
|
||||
_implicit_ and _explicit_ parts of the operator, respectively.
|
||||
|
||||
A common use for this class is representing a differential algebraic
|
||||
equation of the form $ F(y,\frac{dy}{dt},t) = G(y,t) $.
|
||||
|
||||
For example, consider an ordinary differential equation of the form
|
||||
$ M \frac{dy}{dt} = g(y,t) $. There are various ways of expressing this ODE
|
||||
as a TimeDependentOperator depending on the choices for F and G. Here are
|
||||
some common choices:
|
||||
|
||||
1. F(u,k,t) = k and G(u,t) = inv(M) g(u,t),
|
||||
2. F(u,k,t) = M k and G(u,t) = g(u,t),
|
||||
3. F(u,k,t) = M k - g(u,t) and G(u,t) = 0.
|
||||
|
||||
Note that depending on the ODE solver, some of the above choices may be
|
||||
preferable to the others.
|
||||
*/
|
||||
/** Operator of the form: (x,t) -> f(x,t), where k = f(x,t) generally solves the
|
||||
algebraic equation F(x,k,t) = G(x,t). The functions F and G represent the
|
||||
_implicit_ and _explicit_ parts of the operator, respectively. For explicit
|
||||
operators, F(x,k,t) = k, so f(x,t) = G(x,t). */
|
||||
class TimeDependentOperator : public Operator
|
||||
{
|
||||
public:
|
||||
/// Enum used to describe the form of the time-dependent operator.
|
||||
/** The type should be set by classes derived from TimeDependentOperator to
|
||||
describe the form, in terms of the functions F and G, used by the
|
||||
specific derived class. This information can be queried by classes or
|
||||
functions (like time stepping algorithms) to make choices about the
|
||||
algorithm to use, or to ensure that the TimeDependentOperator uses the
|
||||
form expected by the class/function.
|
||||
|
||||
For example, assume that a derived class is implementing the ODE
|
||||
$M \frac{dy}{dt} = g(y,t)$ and chooses to define $F(u,k,t) = M k$ and
|
||||
$G(u,t) = g(u,t)$. Then it cannot use type EXPLICIT, unless $M = I$, or
|
||||
type HOMOGENEOUS, unless $g(u,t) = 0$. If, on the other hand, the derived
|
||||
class chooses to define $F(u,k,t) = k$ and $G(u,t) = M^{-1} g(y,t)$, then
|
||||
the natural choice is to set the type to EXPLICIT, even though setting it
|
||||
to IMPLICIT is also not wrong -- doing so will simply fail to inform
|
||||
methods that query this information that it uses a more specific
|
||||
implementation, EXPLICIT, that may allow the use of algorithms that
|
||||
support only the EXPLICIT type. */
|
||||
enum Type
|
||||
{
|
||||
EXPLICIT, ///< This type assumes F(u,k,t) = k.
|
||||
EXPLICIT, ///< This type assumes F(x,k,t) = k, i.e. k = f(x,t) = G(x,t).
|
||||
IMPLICIT, ///< This is the most general type, no assumptions on F and G.
|
||||
HOMOGENEOUS ///< This type assumes that G(u,t) = 0.
|
||||
HOMOGENEOUS ///< This type assumes that G(x,t) = 0.
|
||||
};
|
||||
|
||||
/// Evaluation mode. See SetEvalMode() for details.
|
||||
@@ -361,30 +328,29 @@ public:
|
||||
{
|
||||
/** Normal evaluation. */
|
||||
NORMAL,
|
||||
/** Assuming additive split, k(u,t) = k1(u,t) + k2(u,t), evaluate the
|
||||
first term, k1. */
|
||||
/** Assuming additive split, f(x,t) = f1(x,t) + f2(x,t), evaluate the
|
||||
first term, f1. */
|
||||
ADDITIVE_TERM_1,
|
||||
/** Assuming additive split, k(u,t) = k1(u,t) + k2(u,t), evaluate the
|
||||
second term, k2. */
|
||||
/** Assuming additive split, f(x,t) = f1(x,t) + f2(x,t), evaluate the
|
||||
second term, f2. */
|
||||
ADDITIVE_TERM_2
|
||||
};
|
||||
|
||||
protected:
|
||||
real_t t; ///< Current time.
|
||||
Type type; /**< @brief Describes the form of the TimeDependentOperator, see
|
||||
the documentation of #Type. */
|
||||
Type type; ///< Describes the form of the TimeDependentOperator.
|
||||
EvalMode eval_mode; ///< Current evaluation mode.
|
||||
|
||||
public:
|
||||
/** @brief Construct a "square" TimeDependentOperator (u,t) -> k(u,t), where
|
||||
u and k have the same dimension @a n. */
|
||||
/** @brief Construct a "square" TimeDependentOperator y = f(x,t), where x and
|
||||
y have the same dimension @a n. */
|
||||
explicit TimeDependentOperator(int n = 0, real_t t_ = 0.0,
|
||||
Type type_ = EXPLICIT)
|
||||
: Operator(n) { t = t_; type = type_; eval_mode = NORMAL; }
|
||||
|
||||
/** @brief Construct a TimeDependentOperator (u,t) -> k(u,t), where u and k
|
||||
have dimensions @a w and @a h, respectively. */
|
||||
TimeDependentOperator(int h, int w, double t_ = 0.0, Type type_ = EXPLICIT)
|
||||
/** @brief Construct a TimeDependentOperator y = f(x,t), where x and y have
|
||||
dimensions @a w and @a h, respectively. */
|
||||
TimeDependentOperator(int h, int w, real_t t_ = 0.0, Type type_ = EXPLICIT)
|
||||
: Operator(h, w) { t = t_; type = type_; eval_mode = NORMAL; }
|
||||
|
||||
/// Read the currently set time.
|
||||
@@ -407,7 +373,7 @@ public:
|
||||
/** The evaluation mode is a switch that allows time-stepping methods to
|
||||
request evaluation of separate components/terms of the time-dependent
|
||||
operator. For example, IMEX methods typically assume additive split of
|
||||
the operator: k(u,t) = k1(u,t) + k2(u,t) and they rely on the ability to
|
||||
the operator: f(x,t) = f1(x,t) + f2(x,t) and they rely on the ability to
|
||||
evaluate the two terms separately.
|
||||
|
||||
Generally, setting the evaluation mode should affect the behavior of all
|
||||
@@ -418,104 +384,62 @@ public:
|
||||
{ eval_mode = new_eval_mode; }
|
||||
|
||||
/** @brief Perform the action of the explicit part of the operator, G:
|
||||
@a v = G(@a u, t) where t is the current time.
|
||||
@a y = G(@a x, t) where t is the current time.
|
||||
|
||||
Presently, this method is used by some PETSc ODE solvers, for more
|
||||
details, see the PETSc Manual. */
|
||||
virtual void ExplicitMult(const Vector &u, Vector &v) const;
|
||||
virtual void ExplicitMult(const Vector &x, Vector &y) const;
|
||||
|
||||
/** @brief Perform the action of the implicit part of the operator, F:
|
||||
@a v = F(@a u, @a k, t) where t is the current time.
|
||||
@a y = F(@a x, @a k, t) where t is the current time.
|
||||
|
||||
Presently, this method is used by some PETSc ODE solvers, for more
|
||||
details, see the PETSc Manual.*/
|
||||
virtual void ImplicitMult(const Vector &u, const Vector &k, Vector &v) const;
|
||||
virtual void ImplicitMult(const Vector &x, const Vector &k, Vector &y) const;
|
||||
|
||||
/** @brief Perform the action of the operator (u,t) -> k(u,t) where t is the
|
||||
current time set by SetTime() and @a k satisfies
|
||||
F(@a u, @a k, t) = G(@a u, t).
|
||||
/** @brief Perform the action of the operator: @a y = k = f(@a x, t), where
|
||||
k solves the algebraic equation F(@a x, k, t) = G(@a x, t) and t is the
|
||||
current time. */
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
For solving an ordinary differential equation of the form
|
||||
$ M \frac{dy}{dt} = g(y,t) $, recall that F and G can be defined in
|
||||
various ways, e.g.:
|
||||
/** @brief Solve the equation: @a k = f(@a x + @a dt @a k, t), for the
|
||||
unknown @a k at the current time t.
|
||||
|
||||
1. F(u,k,t) = k and G(u,t) = inv(M) g(u,t)
|
||||
2. F(u,k,t) = M k and G(u,t) = g(u,t)
|
||||
3. F(u,k,t) = M k - g(u,t) and G(u,t) = 0.
|
||||
For general F and G, the equation for @a k becomes:
|
||||
F(@a x + @a dt @a k, @a k, t) = G(@a x + @a dt @a k, t).
|
||||
|
||||
Regardless of the choice of F and G, this function should always compute
|
||||
@a k = inv(M) g(@a u, t). */
|
||||
virtual void Mult(const Vector &u, Vector &v) const override;
|
||||
The input vector @a x corresponds to time index (or cycle) n, while the
|
||||
currently set time, #t, and the result vector @a k correspond to time
|
||||
index n+1. The time step @a dt corresponds to the time interval between
|
||||
cycles n and n+1.
|
||||
|
||||
/** @brief Solve for the unknown @a k, at the current time t, the following
|
||||
equation:
|
||||
F(@a u + @a gamma @a k, @a k, t) = G(@a u + @a gamma @a k, t).
|
||||
|
||||
For solving an ordinary differential equation of the form
|
||||
$ M \frac{dy}{dt} = g(y,t) $, recall that F and G can be defined in
|
||||
various ways, e.g.:
|
||||
|
||||
1. F(u,k,t) = k and G(u,t) = inv(M) g(u,t)
|
||||
2. F(u,k,t) = M k and G(u,t) = g(u,t)
|
||||
3. F(u,k,t) = M k - g(u,t) and G(u,t) = 0
|
||||
|
||||
Regardless of the choice of F and G, this function should solve for @a k
|
||||
in M @a k = g(@a u + @a gamma @a k, t).
|
||||
|
||||
To see how @a k can be useful, consider the backward Euler method defined
|
||||
by $ y(t + \Delta t) = y(t) + \Delta t k_0 $ where
|
||||
$ M k_0 = g \big( y(t) + \Delta t k_0, t + \Delta t \big) $. A backward
|
||||
Euler integrator can use @a k from this function for $k_0$, with the call
|
||||
using @a u set to $ y(t) $, @a gamma set to $ \Delta t$, and time set to
|
||||
$t + \Delta t$. See class BackwardEulerSolver.
|
||||
|
||||
Generalizing further, consider a diagonally implicit Runge-Kutta (DIRK)
|
||||
method defined by
|
||||
$ y(t + \Delta t) = y(t) + \Delta t \sum_{i=1}^s b_i k_i $ where
|
||||
$ M k_i = g \big( y(t) + \Delta t \sum_{j=1}^i a_{ij} k_j,
|
||||
t + c_i \Delta t \big) $.
|
||||
A DIRK integrator can use @a k from this function, with @a u set to
|
||||
$ y(t) + \Delta t \sum_{j=1}^{i-1} a_{ij} k_j $ and @a gamma set to
|
||||
$ a_{ii} \Delta t $, for $ k_i $. For example, see class SDIRK33Solver.
|
||||
This method allows for the abstract implementation of some time
|
||||
integration methods, including diagonal implicit Runge-Kutta (DIRK)
|
||||
methods and the backward Euler method in particular.
|
||||
|
||||
If not re-implemented, this method simply generates an error. */
|
||||
virtual void ImplicitSolve(const real_t gamma, const Vector &u, Vector &k);
|
||||
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
|
||||
|
||||
/** @brief Return an Operator representing (dF/dk @a shift + dF/du) at the
|
||||
given @a u, @a k, and the currently set time.
|
||||
/** @brief Return an Operator representing (dF/dk @a shift + dF/dx) at the
|
||||
given @a x, @a k, and the currently set time.
|
||||
|
||||
Presently, this method is used by some PETSc ODE solvers, for more
|
||||
details, see the PETSc Manual. */
|
||||
virtual Operator& GetImplicitGradient(const Vector &u, const Vector &k,
|
||||
virtual Operator& GetImplicitGradient(const Vector &x, const Vector &k,
|
||||
real_t shift) const;
|
||||
|
||||
/** @brief Return an Operator representing dG/du at the given point @a u and
|
||||
/** @brief Return an Operator representing dG/dx at the given point @a x and
|
||||
the currently set time.
|
||||
|
||||
Presently, this method is used by some PETSc ODE solvers, for more
|
||||
details, see the PETSc Manual. */
|
||||
virtual Operator& GetExplicitGradient(const Vector &u) const;
|
||||
virtual Operator& GetExplicitGradient(const Vector &x) const;
|
||||
|
||||
/** @brief Setup a linear system as needed by some SUNDIALS ODE solvers.
|
||||
/** @brief Setup the ODE linear system $ A(x,t) = (I - gamma J) $ or
|
||||
$ A = (M - gamma J) $, where $ J(x,t) = \frac{df}{dt(x,t)} $.
|
||||
|
||||
For solving an ordinary differential equation of the form
|
||||
$ M \frac{dy}{dt} = g(y,t) $, recall that F and G can be defined as one
|
||||
of the following:
|
||||
|
||||
1. F(u,k,t) = k and G(u,t) = inv(M) g(u,t)
|
||||
2. F(u,k,t) = M k and G(u,t) = g(u,t)
|
||||
3. F(u,k,t) = M k - g(u,t) and G(u,t) = 0
|
||||
|
||||
This function performs setup to solve $ A x = b $ where A is either
|
||||
|
||||
1. A(@a y,t) = I - @a gamma inv(M) J(@a y,t)
|
||||
2. A(@a y,t) = M - @a gamma J(@a y,t)
|
||||
3. A(@a y,t) = M - @a gamma J(@a y,t)
|
||||
|
||||
with J = dg/dy (or a reasonable approximation thereof).
|
||||
|
||||
@param[in] y The state at which A(@a y,t) should be evaluated.
|
||||
@param[in] v The value of inv(M) g(y,t) for 1 or g(y,t) for 2 & 3.
|
||||
@param[in] x The state at which $A(x,t)$ should be evaluated.
|
||||
@param[in] fx The current value of the ODE rhs function, $f(x,t)$.
|
||||
@param[in] jok Flag indicating if the Jacobian should be updated.
|
||||
@param[out] jcur Flag to signal if the Jacobian was updated.
|
||||
@param[in] gamma The scaled time step value.
|
||||
@@ -524,10 +448,10 @@ public:
|
||||
|
||||
Presently, this method is used by SUNDIALS ODE solvers, for more
|
||||
details, see the SUNDIALS User Guides. */
|
||||
virtual int SUNImplicitSetup(const Vector &y, const Vector &v,
|
||||
virtual int SUNImplicitSetup(const Vector &x, const Vector &fx,
|
||||
int jok, int *jcur, real_t gamma);
|
||||
|
||||
/** @brief Solve the ODE linear system A @a x = @a b, where A is defined by
|
||||
/** @brief Solve the ODE linear system $ A x = b $ as setup by
|
||||
the method SUNImplicitSetup().
|
||||
|
||||
@param[in] b The linear system right-hand side.
|
||||
@@ -540,8 +464,7 @@ public:
|
||||
details, see the SUNDIALS User Guides. */
|
||||
virtual int SUNImplicitSolve(const Vector &b, Vector &x, real_t tol);
|
||||
|
||||
/** @brief Setup the mass matrix in the ODE system
|
||||
$ M \frac{dy}{dt} = g(y,t) $ .
|
||||
/** @brief Setup the mass matrix in the ODE system $ M y' = f(y,t) $ .
|
||||
|
||||
If not re-implemented, this method simply generates an error.
|
||||
|
||||
@@ -549,8 +472,8 @@ public:
|
||||
details, see the ARKode User Guide. */
|
||||
virtual int SUNMassSetup();
|
||||
|
||||
/** @brief Solve the mass matrix linear system M @a x = @a b, where M is
|
||||
defined by the method SUNMassSetup().
|
||||
/** @brief Solve the mass matrix linear system $ M x = b $
|
||||
as setup by the method SUNMassSetup().
|
||||
|
||||
@param[in] b The linear system right-hand side.
|
||||
@param[in,out] x On input, the initial guess. On output, the solution.
|
||||
@@ -562,8 +485,7 @@ public:
|
||||
details, see the ARKode User Guide. */
|
||||
virtual int SUNMassSolve(const Vector &b, Vector &x, real_t tol);
|
||||
|
||||
/** @brief Compute the mass matrix-vector product @a v = M @a x, where M is
|
||||
defined by the method SUNMassSetup().
|
||||
/** @brief Compute the mass matrix-vector product $ v = M x $ .
|
||||
|
||||
@param[in] x The vector to multiply.
|
||||
@param[out] v The result of the matrix-vector product.
|
||||
|
||||
+4
-6
@@ -1188,8 +1188,6 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
|
||||
final_norm = std::max(rel_tol*beta, abs_tol);
|
||||
|
||||
converged = false;
|
||||
|
||||
if (beta <= final_norm)
|
||||
{
|
||||
final_norm = beta;
|
||||
@@ -1305,6 +1303,8 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
MFEM_ASSERT(IsFinite(beta), "beta = " << beta);
|
||||
if (beta <= final_norm)
|
||||
{
|
||||
final_norm = beta;
|
||||
final_iter = j;
|
||||
converged = true;
|
||||
|
||||
break;
|
||||
@@ -1317,9 +1317,7 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
if (v[i]) { delete v[i]; }
|
||||
if (z[i]) { delete z[i]; }
|
||||
}
|
||||
|
||||
final_norm = beta;
|
||||
final_iter = converged ? j : max_iter;
|
||||
converged = false;
|
||||
|
||||
// Note: j is off by one when we arrive here
|
||||
if (!print_options.iterations && print_options.first_and_last)
|
||||
@@ -1330,7 +1328,7 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
|
||||
}
|
||||
if (print_options.summary || (print_options.warnings && !converged))
|
||||
{
|
||||
mfem::out << "FGMRES: Number of iterations: " << final_iter << '\n';
|
||||
mfem::out << "FGMRES: Number of iterations: " << j-1 << '\n';
|
||||
}
|
||||
if (print_options.warnings && !converged)
|
||||
{
|
||||
|
||||
+5
-14
@@ -58,20 +58,6 @@ DenseSymmetricMatrix &DenseSymmetricMatrix::operator=(real_t c)
|
||||
return *this;
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix &DenseSymmetricMatrix::operator=(const DenseSymmetricMatrix
|
||||
&m)
|
||||
{
|
||||
SetSize(m.height);
|
||||
|
||||
const int hw = m.GetStoredSize();
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = m.data[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
real_t &DenseSymmetricMatrix::Elem(int i, int j)
|
||||
{
|
||||
return (*this)(i,j);
|
||||
@@ -103,6 +89,11 @@ MatrixInverse *DenseSymmetricMatrix::Inverse() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DenseSymmetricMatrix::Print (std::ostream & os, int width_) const
|
||||
{
|
||||
mfem_error("DenseSymmetricMatrix::Print() not implemented!");
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix::~DenseSymmetricMatrix()
|
||||
{
|
||||
data.Delete();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user