Compare commits

..
Author SHA1 Message Date
Stowell, Mark L 466fc7ff82 Merge remote-tracking branch 'origin/master' into complex-strumpack-dev 2019-04-12 15:06:53 -07:00
Stowell, Mark L ef2068552c Merge remote-tracking branch 'origin/master' into complex-strumpack-dev 2019-04-09 14:18:56 -07:00
Stowell, Mark L 2905a94155 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev
# Conflicts:
#	examples/ex11p.cpp
#	linalg/strumpack.cpp
#	linalg/strumpack.hpp
2019-04-01 11:03:38 -07:00
Stowell, Mark L b84a5c6c4d Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-12-17 10:58:56 -08:00
Stowell, Mark L 16403e1ba2 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-11-26 14:11:15 -08:00
Stowell, Mark L 7434c8e66c make style 2018-10-26 21:22:20 -07:00
Stowell, Mark L 08a9af35c5 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev
# Conflicts:
#	examples/ex21p.cpp
2018-10-26 21:19:35 -07:00
Dylan Copeland 4c746bd831 Added solver timer. 2018-10-17 09:08:30 -07:00
Dylan Copeland 98b26dba79 Adding strumpack version of ex3p. 2018-10-15 10:11:48 -07:00
Stowell, Mark L 0d1ca9dc79 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-10-10 21:09:42 -07:00
Stowell, Mark L d0a58f0b3d This functionality seems to have vanished from the latest STRUMPACK 2018-09-25 16:52:19 -07:00
Stowell, Mark L 82fdc3d4ce Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-09-25 13:19:27 -07:00
Stowell, Mark L 76f0d6a956 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-09-08 14:56:47 -07:00
Mark L. Stowell 72bf549085 Small changes to assist debugging 2018-08-31 14:34:35 -07:00
Stowell, Mark L 63ee675bd4 Avoiding template instanitations each time strumpack header is included 2018-08-24 19:39:07 -07:00
Stowell, Mark L 42a509538d Adding STRUMPACK support to example 21 2018-08-23 15:04:09 -07:00
Stowell, Mark L ca7cb115b1 CSRMatrixMPI does not _borrow_ the data array, it copies it so this should avoid a large memory leak 2018-08-23 14:44:56 -07:00
Stowell, Mark L 0dfa567ce3 styling changes 2018-08-23 14:44:46 -07:00
Stowell, Mark L 837e2abed4 Adding wrappers for STRUMPACK's complex sparse matrix and solver 2018-08-23 14:44:08 -07:00
293 changed files with 11645 additions and 46780 deletions
+8 -9
View File
@@ -26,25 +26,24 @@ install:
- cd ..
# Install hypre
- ps: Start-FileDownload 'https://github.com/hypre-space/hypre/archive/V2-10-0b.tar.gz'
- 7z x V2-10-0b.tar.gz -so | 7z x -si -ttar > nul
- cd hypre-2-10-0b
- cmake -H. -Bbuild -DHYPRE_USING_FEI=OFF -DMPI_C_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DMPI_C_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include"
- ps: Start-FileDownload 'https://computation.llnl.gov/project/linear_solvers/download/hypre-2.10.0b.tar.gz'
- 7z x hypre-2.10.0b.tar.gz -so | 7z x -si -ttar > nul
- cd hypre-2.10.0b
- cmake -Hsrc -Bbuild -DMPI_C_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DMPI_C_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include"
# - cmake -Hsrc -Bbuild -DCMAKE_BUILD_TYPE=Release -DMPI_C_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DMPI_C_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include"
- cmake --build build
- cmake --build build --target install
- cd ..
# MFEM
before_build:
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_parallel -DMFEM_USE_MPI=TRUE -DMFEM_USE_METIS_5=TRUE -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DHYPRE_LIBRARIES=%cd%\hypre-2-10-0b\hypre\lib\HYPRE.lib -DHYPRE_INCLUDE_DIRS=%cd%\hypre-2-10-0b\hypre\include -DHYPRE_VERSION=21000 -DMETIS_LIBRARIES=%cd%\metis-5.1.0\build\libmetis\Debug\metis.lib -DMETIS_INCLUDE_DIRS=%cd%\metis-5.1.0\include
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_serial -DMFEM_USE_MPI=FALSE
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_parallel -DMFEM_USE_MPI=TRUE -DMFEM_USE_METIS_5=TRUE -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DHYPRE_LIBRARIES=%cd%\hypre-2.10.0b\src\hypre\lib\HYPRE.lib -DHYPRE_INCLUDE_DIRS=%cd%\hypre-2.10.0b\src\hypre\include -DHYPRE_VERSION=21000 -DMETIS_LIBRARIES=%cd%\metis-5.1.0\build\libmetis\Debug\metis.lib -DMETIS_INCLUDE_DIRS=%cd%\metis-5.1.0\include
- cmake -H. -DCMAKE_INSTALL_PREFIX=install -Bbuild_serial -DMFEM_USE_MPI=FALSE -DMFEM_USE_METIS_5=TRUE -DMPI_CXX_LIBRARIES="C:\Program Files (x86)\Microsoft SDKs\MPI\Lib\x86\msmpi.lib" -DMPI_CXX_INCLUDE_PATH="C:\Program Files (x86)\Microsoft SDKs\MPI\Include" -DHYPRE_LIBRARIES=%cd%\hypre-2.10.0b\src\hypre\lib\HYPRE.lib -DHYPRE_INCLUDE_DIRS=%cd%\hypre-2.10.0b\src\hypre\include -DHYPRE_VERSION=21000 -DMETIS_LIBRARIES=%cd%\metis-5.1.0\build\libmetis\Debug\metis.lib -DMETIS_INCLUDE_DIRS=%cd%\metis-5.1.0\include
build_script:
- cmake --build build_parallel
- cmake --build build_serial
- cmake --build build_serial --target exec
after_build:
# - cmake --build build_parallel --target check
- cmake --build build_serial --target RUN_TESTS
- cmake --build build_serial --target check
+3 -21
View File
@@ -9,7 +9,6 @@
# Object and library files
*.o
/libmfem.*
/miniapps/common/libmfem-common.*
# CMake generated files
CMakeCache.txt
@@ -54,7 +53,6 @@ examples/displaced.mesh
examples/mesh.*
examples/ex5.mesh
examples/Example5*
examples/PVExample*
examples/Example9*
examples/Example15*
examples/Example16*
@@ -62,8 +60,6 @@ examples/sphere_refined.*
examples/sol.*
examples/sol_u.*
examples/sol_p.*
examples/sol_r.*
examples/sol_i.*
examples/ex9.mesh
examples/ex9-mesh.*
examples/ex9-init.*
@@ -86,9 +82,9 @@ examples/ex20.dat
examples/ex20p_?????.dat
examples/gnuplot_ex20.inp
examples/gnuplot_ex20p.inp
examples/ex21*.mesh
examples/ex21*.sol
examples/ex21p_*.*
examples/ex22*.mesh
examples/ex22*.sol
examples/ex22p_*.*
examples/sundials/ex9
examples/sundials/ex1[06]
@@ -117,7 +113,6 @@ examples/petsc/sol.*
examples/petsc/sol_p.*
examples/petsc/sol_u.*
examples/petsc/Example5*
examples/petsc/ex9.mesh
examples/petsc/ex9-mesh.*
examples/petsc/ex9-init.*
examples/petsc/ex9-final.*
@@ -129,11 +124,6 @@ examples/petsc/elastic_energy.*
examples/pumi/ex1
examples/pumi/ex[126]p
examples/hiop/ex9.mesh
examples/hiop/ex9-mesh.*
examples/hiop/ex9-init.*
examples/hiop/ex9-final.*
examples/pumi/refined.mesh
examples/pumi/sol.gf
examples/pumi/mesh.*
@@ -180,7 +170,6 @@ miniapps/tools/display-basis
miniapps/tools/load-dc
miniapps/tools/convert-dc
miniapps/tools/lor-transfer
miniapps/tools/get-values
miniapps/nurbs/ex1
miniapps/nurbs/ex1p
@@ -191,13 +180,6 @@ miniapps/nurbs/sol.*
miniapps/nurbs/mode_*
miniapps/nurbs/Example1*
miniapps/gslib/field-diff
miniapps/gslib/findpts
miniapps/gslib/pfindpts
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
# VPATH builds
build-*/*
+15 -113
View File
@@ -1,114 +1,28 @@
language: cpp
sudo: false
stages:
- checks
- tests
- optional
language: cpp
jobs:
matrix:
include:
# ========================
# Checks
# ========================
# - code-style
# - documentation
# - gitignore
- stage: checks
os: linux
name: "code-style"
addons:
apt:
packages:
- astyle=2.05.1-0ubuntu1
script:
- cd ${TRAVIS_BUILD_DIR}
- cd tests/scripts
- ./runtest code-style
- stage: checks
os: linux
name: "documentation"
addons:
apt:
packages:
- doxygen
- graphviz
- mpich
- libmpich-dev
env: MPI=YES
script:
- cd ${TRAVIS_BUILD_DIR}
- cd tests/scripts
- ./runtest documentation
- stage: checks
os: linux
name: "gitignore"
addons:
apt:
packages:
- mpich
- libmpich-dev
env: MPI=YES
script:
- cd ${TRAVIS_BUILD_DIR}
- make config MFEM_USE_MPI=YES MFEM_MPI_NP=2
- make all -j3
- make test-noclean
- cd tests/scripts
- ./runtest gitignore
# ========================
# Optional Checks/Tests
# ========================
# - branch-history
- stage: optional
name: "branch-history"
# need full git history for the binary/big files check
git:
depth: false
script:
- cd ${TRAVIS_BUILD_DIR}
# update master
- git fetch origin master:master
# checkout a branch (otherwise Travis works in detached head)
- git checkout -b travis_tests
- cd tests/scripts
- ./runtest branch-history
# ========================
# Linux tests
# ========================
# - serial + debug
# - serial
# - parallel + debug
# - parallel
- stage: tests
os: linux
#
# Linux
#
- os: linux
compiler: gcc
name: "Linux: Serial + Debug"
env: DEBUG=YES
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=check
#
- os: linux
compiler: gcc
name: "Linux: Serial"
env: DEBUG=NO
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=test
#
- os: linux
compiler: gcc
name: "Linux: Parallel + Debug"
addons:
apt:
# sources:
@@ -135,10 +49,9 @@ jobs:
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a ..; rm -rf *; mv ../libmetis.a .
#
- os: linux
compiler: gcc
name: "Linux: Parallel"
addons:
apt:
# sources:
@@ -165,37 +78,28 @@ jobs:
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a ..; rm -rf *; mv ../libmetis.a .
# ========================
# Mac OS X tests
# ========================
# - serial + debug
# - serial
# - parallel + debug
# - parallel
#
# Mac OS X
#
- os: osx
# osx_image: xcode7.3
compiler: clang
name: "Mac: Serial + Debug"
env: DEBUG=YES
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=check
#
- os: osx
# osx_image: xcode7.3
compiler: clang
name: "Mac: Serial"
env: DEBUG=NO
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=test
#
- os: osx
# osx_image: xcode7.3
compiler: clang
name: "Mac: Parallel + Debug"
env: DEBUG=YES
MPI=YES
CODECOV=NO
@@ -211,11 +115,10 @@ jobs:
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a ..; rm -rf *; mv ../libmetis.a .
#
- os: osx
# osx_image: xcode7.3
compiler: clang
name: "Mac: Parallel"
env: DEBUG=NO
MPI=YES
CODECOV=YES
@@ -302,7 +205,6 @@ install:
else
echo "Reusing cached hypre-2.10.0b/";
fi;
ln -s hypre-2.10.0b hypre;
else
echo "Serial build, not using hypre";
fi
+32 -205
View File
@@ -8,172 +8,23 @@
http://mfem.org
Version 4.0.1 (development)
===========================
Version 4.0-RC1, Apr 11, 2019
=============================
Improved GPU support
--------------------
- Added support for matrix-free diagonal smoothers on GPUs.
- Added initial support for AMD GPUs based on HIP: a C++ runtime API and kernel
language that can run on both AMD and NVIDIA hardware. With this change and
the libCEED addition below, the current list of available backends is:
"ceed-cuda", "occa-cuda", "raja-cuda", "cuda", "hip", "occa-omp", "raja-omp",
"omp", "ceed-cpu", "occa-cpu", "raja-cpu", and "cpu".
- Improved RAJA backend and multi-GPU MPI communications.
libCEED support
---------------
- Added support for libCEED, the portable library for high-order operator
evaluation developed by the Center for Efficient Exascale Discretizations in
the Exascale Computing Project, https://github.com/CEED/libCEED.
- This initial integration includes Mass and Diffusion integrators. libCEED GPU
backends can be used without specific MFEM configuration, however it is highly
recommended to use the "cuda" build option to minimize memory transfers.
- Both CPU and GPU modes are available as MFEM device backends (ceed-cpu and
ceed-cuda), using some of the best performing CPU and GPU backends from
libCEED, see the sample runs in examples 1 and 6.
Meshing improvements
--------------------
- Added support for non-conforming AMR on prisms and tetrahedra, including
coarsening and parallel load balancing. Anisotropic prism refinement is only
available in the serial version at the moment.
- The TMOP mesh optimization algorithms were extended to support r-adaptivity.
Target matrices can now be constructed either via a given analytical function
(e.g. spatial dependence of size, aspect ratio, etc., for each element) or via
a (Par)GridFunction specified on the original mesh.
- New method Mesh::GetHilbertElementOrdering for sorting mesh elements along the
Hilbert curve. The ordering can be used to improve caching and parallel
partitioning in non-conforming AMR.
- Added support for creating refined versions of periodic meshes, making use of
the new L2ElementRestriction class. This class also allows for computing
geometric factors on periodic meshes using partial assembly.
- The TMOP mesh optimization algorithms have been improved to support AMR meshes.
- Improved element numbering after uniform mesh refinement.
Discretization improvements
---------------------------
- Added support for GSLIB-FindPoints, a general high-order interpolation utility
that can robustly evaluate a GridFunction in an arbitrary collection of points
in physical space. See INSTALL for details on building MFEM with GSLIB, and
miniapps/gslib for examples of how to use this feature.
- Added support for complex-valued finite element operators and fields using a
2x2 block structured linear system to mimic complex arithmetic. New classes
include: ComplexGridFunction, SesquilinearForm, ComplexLinearForm, and their
parallel counterparts.
- Two integrators related to Stokes problems, (Q grad u, v) and (Q div v, u),
where u and the components of v are in H1, were added/modified to support full
and partial assembly modes. See the new GradientIntegrator and the updated
VectorDivergenceIntegrator classes in fem/bilininteg.hpp, as well as the PA
kernels in fem/bilininteg_gradient.cpp and fem/bilininteg_divergence.cpp.
- Diagonals of partially assembled operators can now be computed efficiently.
See the new methods AssembleDiagonal in BilinearForm, AssembleDiagonalPA in
BilinearFormIntegrator and the implementations in fem/bilininteg_*.cpp.
- Added initial support for NonlinearForms to support the partial assembly mode.
- Added a nonlinear vector valued convection integrator (Q u \cdot grad u, v)
where u_i and v_i are in H1. This form occurs e.g. in the Navier-Stokes
equations. The integrator supports the partial assembly mode for its
action. In full assembly mode we also provide the GetGradient method that
computes the linearized version of the integrator.
- Added a new method, MixedBilinearForm::FormRectangularLinearSystem, that can
be used to impose boundary conditions on the non-square off-diagonal blocks of
a block operator (similar to FormLinearSystem in the square case).
- Extended the support for partial assembly to vector mass and vector diffusion
bilinear integrators.
Linear and nonlinear solvers
Requirements and Limitations
----------------------------
- Added a general interface for specifying and solving nonlinear constrained
optimization problems through the new classes OptimizationProblem and
OptimizationSolver, see linalg/solver.hpp
- This is a release candidate for mfem-4.0.
- Use at your own risk -- not everything will work, the API may change.
- We are looking for feedback from friendly users.
- Unlike previous MFEM releases, this version requires a C++11 compiler.
- Added support for HiOp, a lightweight HPC solver for nonlinear optimization
problems see class HiOpNLPOptimizer and the example codes in examples/hiop.
- Added support for Ginkgo, a high-performance linear algebra library for GPU
and manycore nodes, with a focus on sparse solution of linear systems. For
more details see linalg/ginkgo.hpp and the example code in examples/gingko.
- Added Adams-Bashforth and Adams-Moulton time integrators.
New and updated examples and miniapps
-------------------------------------
- Added two new miniapps: Find Points (serial + parallel) and Field Diff in
miniapps/gslib that show how GSLIB-FindPoints can be used to interpolate a
(Par)GridFunction in an arbitrary number of physical space points in 2D and
3D. The GridFunction must be in H1 and in the same space as the mesh that is
used to find the points.
- Added a new example, Example 22/22p, to demonstrate the use of the new
complex-valued finite element operators. The example defines and solves
a family of time-harmonic PDEs related to damped harmonic oscillators.
- Updated Example 1/1p to use diagonal preconditioning in partial assembly mode.
- The mesh-optimizer and pmesh-optimizer miniapps have been updated to
demonstrate the new r-adaptivity capabilities of TMOP.
- New options to reorder and partition the mesh in the mesh-explorer miniapp.
- The (p)mesh-optimizer miniapp has been updated to demonstrate mesh
optimization for an AMR mesh.
- Added a modification of Example 1 in examples/ginkgo that demonstrates the use
of the Gingko interface to solve a linear system.
- Added a modification of ex9 in examples/hiop that demonstrates the nonlinear
constrained optimization interface and the use of the SLBQP and HiOp solvers.
Improved testing
----------------
- Added a new directory, tests/scripts, with several shell scripts that perform
simple checks on the code including: code styling, documentation formatting,
proper use of .gitignore, and preventing the accidental commit of large files.
- It is recommended that developers run the above tests scripts (via the runtest
script) before pushing to GitHub. See the README file in tests/scripts.
- The Travis CI settings have been updated to include an initial Checks stage
which currently runs the code-style, documentation and gitignore test scripts,
as well as a final stage for optional checks/tests which currently runs the
branch-history script.
Miscellaneous
-------------
- Upgraded the SUNDIALS interface to utilize version 5.0. This necessitated a
complete rework of the interface and requires changes at the application
level. Example usage of the new interface can be found in examples/sundials.
- Added support for output in the ParaView XML format. See Examples 5/5p, 9/9p
and the new ParaViewDataCollection class.
- Collected object files from the miniapps/common directory into a new library,
libmfem-common for the convenience of application developers. The new library
is now used in several miniapps in the electromagnetic and tools directories.
- Added unit tests for time integrators.
Version 4.0, released on May 24, 2019
=====================================
Unlike previous MFEM releases, this version requires a C++11 compiler.
- GPU-related limitations:
* NVCC is not supported in the CMake build system yet.
* Element batching is currently ignored.
* Full-assembly (on device), element assembly, and matrix-free bilinear forms
are not supported yet.
* FunctionCoefficients do not currently work on GPUs.
* Partial assembly kernels are not implemented yet for simplices.
GPU support
-----------
@@ -184,7 +35,7 @@ GPU support
seamlessly with a new lightweight device/host memory manager. The kernels can
be implemented either in OCCA, or as a simple wrapper around for-loops, which
can then be dispatched to RAJA and native backends. See the files forall.hpp
and mem_manager.hpp in the general/ directory for more details.
and mem_manager.hpp in the general/ directory.
- Several of the MFEM example codes (ex1, ex1p, ex6, and ex6p) can now take
advantage of GPU acceleration with the backend selectable at runtime. Many of
@@ -192,43 +43,26 @@ GPU support
bilinear forms) have been extended to take advantage of kernel acceleration by
simply replacing loops with the MFEM_FORALL() macro.
- In addition to native CUDA kernels, the library currently supports OCCA, RAJA
and OpenMP kernels, which could be mixed and matched in different parts of the
same application. We plan on adding support for more programming models and
devices in the future, without the need for significant modifications in user
code. The list of current backends is: "occa-cuda", "raja-cuda", "cuda",
"occa-omp", "raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
- GPU-related limitations:
* Hypre preconditioners are not yet available in GPU mode, and in particular
hypre must be built in CPU mode.
* Only constant coefficients are currently supported on GPUs.
* Optimized element assembly, and matrix-free bilinear forms are not
implemented yet. Element batching is currently ignored.
* In device mode, full assembly is performed on the host (but the matvec
action is performed on the device).
* Partial assembly kernels are not implemented yet for simplices.
- In addition to pure CUDA, the library currently supports OCCA, RAJA and OpenMP
kernels, which could be mixed and matched in different parts of the same
application. We plan on adding support for more programming models and devices
in the future, without the need for significant modifications in user code.
The list of current backends is: "occa-cuda", "raja-cuda", "cuda", "occa-omp",
"raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
Discretization improvements
---------------------------
- Partial assembled finite element operators are now available in the core
library, based on the new classes PABilinearFormExtension, ElementRestriction,
DofToQuad and GeometricFactors (associated with the classes BilinearForm,
FiniteElementSpace, FiniteElement and Mesh, respectively). The kernels for
partial assembled Setup/Assembly and Action/Mult are implemented in the
BilinearFormIntegrator methods AssemblePA and AddMultPA.
- Added support for a general "low-order refined"-to-"high-order" transfer of
GridFunction data from a "low-order refined" (LOR) space defined on a refined
mesh to a "high-order" (HO) finite element space defined on a coarse mesh. See
the new classes InterpolationGridTransfer and L2ProjectionGridTransfer and the
new LOR Transfer miniapp: miniapps/tools/lor-transfer.cpp.
- Added support for derefinement of vector (RT + ND) spaces.
- Added element flux, and flux energy computation in class ElasticityIntegrator,
allowing for the use of Zienkiewicz-Zhu type error estimators with the
integrator. For an illustration of this addition, see the new Example 21.
- Added support for derefinement of vector (RT + ND) spaces.
integrator. For an illustration of this addition, see the new Example 22.
- Added a variety of coefficients which are sums or products of existing
coefficients as well as grid function coefficients which return the
@@ -240,13 +74,13 @@ Support for wedge elements and meshes with mixed element types
type PRISM) which have two triangular faces and three quadrilateral faces.
Several examples of such meshes can be found in the data/ directory.
- Added H1 and L2 finite elements of arbitrary order for Wedge elements.
- Added support for mixed meshes containing triangles and quadrilaterals in 2D
or tetrahedra, wedges, and hexahedra in 3D. This includes support for uniform
refinement of such meshes. Several examples of such meshes can be found in the
data/ directory.
- Added H1 and L2 finite elements of arbitrary order for Wedge elements.
- Added support for reading and writing linear and quadratic meshes containing
wedge elements in VTK mesh format. Several examples of such meshes can be
found in the data/ directory.
@@ -267,10 +101,6 @@ Other meshing improvements
This guarantees that the shape regularity of the elements will be preserved
under refinement.
- The TMOP mesh optimization algorithms were extended to support user-defined
space-dependent limiting terms. Improved the TMOP objective functions by more
accurate normalization of the different terms.
- Added support for parallel communication groups on non-conforming meshes.
- Improved parallel partitioning of non-conforming meshes. If the coarse mesh
@@ -284,6 +114,10 @@ Other meshing improvements
- Added support for reading linear and quadratic 2D quadrilateral and triangular
Cubit meshes.
- The TMOP mesh optimization algorithms were extended to support user-defined
space-dependent limiting terms. Improved the TMOP objective functions by more
accurate normalization of the different terms.
New and updated examples and miniapps
-------------------------------------
- Added a new meshing miniapp, Toroid, which can produce a variety of torus
@@ -299,7 +133,7 @@ New and updated examples and miniapps
from a Hamiltonian. The example demonstrates the use of the variable order,
symplectic integration algorithm implemented in class SIAVSolver.
- Added a new example, Example 21/21p, that illustrates the use of AMR to solve
- Added a new example, Example 22/22p, that illustrates the use of AMR to solve
a linear elasticity problem. This is an extension of Example 2/2p.
New and improved solvers and preconditioners
@@ -311,24 +145,17 @@ New and improved solvers and preconditioners
Miscellaneous
-------------
- Added unit tests based on the Catch++ library in the test/ directory.
- Added unit tests based on the Catch++ library.
- Renamed the option MFEM_USE_OPENMP to MFEM_USE_LEGACY_OPENMP. This legacy
option is deprecated and planned for removal in a future release. The original
option name, MFEM_USE_OPENMP, is now used to enable the new OpenMP backends in
the new kernels.
- In SparseMatrix added the option to perform MultTranspose() by matvec with
computed and stored transpose matrix. This is required for deterministic
results when using devices such as CUDA and OpenMP.
- Altered the way FGMRES counts its iterations so that it matches GMRES.
- Various other simplifications, extensions, and bugfixes in the code.
- Construct abstract parallel rectangular truedof-to-truedof operators via
Operator::FormDiscreteOperator().
API changes
-----------
- In multiple places, use Geometry::Type instead of int, where appropriate.
+10 -87
View File
@@ -50,7 +50,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.0.1)
set(${PROJECT_NAME}_VERSION 3.4.1)
# Prohibit in-source build
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
@@ -86,13 +86,6 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/config/XSDKDefaults.cmake")
# Enable languages.
enable_language(CXX)
if (MFEM_USE_CUDA)
# MFEM_USE_CUDA requires CMake 3.8 or newer (for direct CUDA support)
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
enable_language(CUDA)
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
endif()
if (XSDK_ENABLE_C)
enable_language(C)
endif()
@@ -164,15 +157,6 @@ if (MFEM_USE_METIS)
find_package(METIS REQUIRED)
endif()
if (MFEM_USE_GINKGO)
find_package(Ginkgo REQUIRED)
if (Ginkgo_FOUND)
get_target_property(Ginkgo_INCLUDE_DIRS
Ginkgo::ginkgo INTERFACE_INCLUDE_DIRECTORIES)
set(Ginkgo_LIBRARIES Ginkgo::ginkgo)
endif()
endif()
# GZSTREAM -> zlib
if (MFEM_USE_GZSTREAM)
find_package(ZLIB REQUIRED)
@@ -257,17 +241,13 @@ if (MFEM_USE_MPFR)
find_package(MPFR REQUIRED)
endif()
if (MFEM_USE_CEED)
find_package(libCEED REQUIRED)
endif()
if (MFEM_USE_CONDUIT)
find_package(Conduit REQUIRED conduit relay blueprint )
endif()
# Axom/Sidre
if (MFEM_USE_SIDRE)
find_package(Axom REQUIRED Axom)
find_package(Axom REQUIRED Sidre SLIC axom_utils)
endif()
# PUMI
@@ -286,37 +266,6 @@ if (MFEM_USE_PUMI)
endif()
endif()
# HiOp optimizer
if (MFEM_USE_HIOP)
find_package(HIOP REQUIRED)
# find_package updates HIOP_FOUND, HIOP_INCLUDE_DIRS, HIOP_LIBRARIES
endif()
# CUDA
if (MFEM_USE_CUDA)
set(CMAKE_CUDA_STANDARD 11)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_EXTENSIONS OFF)
set(CMAKE_CUDA_FLAGS "-arch=${CUDA_ARCH} --expt-extended-lambda"
CACHE STRING "CUDA flags set for MFEM" FORCE)
if (MFEM_USE_MPI)
set(CUDA_CCBIN_COMPILER ${MPI_CXX_COMPILER})
else()
set(CUDA_CCBIN_COMPILER ${CMAKE_CXX_COMPILER})
endif()
string(APPEND CMAKE_CUDA_FLAGS " -ccbin ${CUDA_CCBIN_COMPILER}")
endif()
# OCCA
if (MFEM_USE_OCCA)
find_package(OCCA REQUIRED)
endif()
# RAJA
if (MFEM_USE_RAJA)
find_package(RAJA REQUIRED)
endif()
# MFEM_TIMER_TYPE
if (NOT DEFINED MFEM_TIMER_TYPE)
if (APPLE)
@@ -341,8 +290,8 @@ endif()
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
# be before SuiteSparse.
set(MFEM_TPLS MPI_CXX OPENMP BLAS LAPACK METIS HYPRE SuiteSparse SUNDIALS PETSC
MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT GECKO Ginkgo GNUTLS NETCDF MPFR
PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA)
MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT GECKO GNUTLS NETCDF MPFR PUMI
POSIXCLOCKS MFEMBacktrace ZLIB)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
@@ -378,13 +327,6 @@ set(MFEM_SOURCE_DIRS general linalg mesh fem)
foreach(DIR IN LISTS MFEM_SOURCE_DIRS)
add_subdirectory(${DIR})
endforeach()
if (MFEM_USE_CUDA)
foreach(file IN LISTS SOURCES)
set_property(SOURCE ${file} PROPERTY LANGUAGE CUDA)
endforeach()
endif()
add_subdirectory(config)
set(MASTER_HEADERS
${PROJECT_SOURCE_DIR}/mfem.hpp
@@ -395,11 +337,6 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON CACHE BOOL "")
set(CMAKE_INSTALL_RPATH "${_lib_path}" CACHE PATH "")
set(CMAKE_INSTALL_NAME_DIR "${_lib_path}" CACHE PATH "")
set(MFEM_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
"The MFEM source directory" FORCE)
set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} CACHE PATH
"The MFEM install directory" FORCE)
# Declaring the library
add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
@@ -414,11 +351,11 @@ endif()
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
# If building out-of-source, define MFEM_CONFIG_FILE to point to the config file
# inside the build directory.
# If building out-of-source, define MFEM_BUILD_DIR to point to the build
# directory.
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
target_compile_definitions(mfem PRIVATE
"MFEM_CONFIG_FILE=\"${PROJECT_BINARY_DIR}/config/_config.hpp\"")
"MFEM_BUILD_DIR=${PROJECT_BINARY_DIR}")
endif()
# Generate configuration file in the build directory: config/_config.hpp.
@@ -434,7 +371,7 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
"Writing substitute header --> \"${Header}\"")
file(WRITE "${PROJECT_BINARY_DIR}/${Header}"
"// Auto-generated file.
#define MFEM_CONFIG_FILE \"${PROJECT_BINARY_DIR}/config/_config.hpp\"
#define MFEM_BUILD_DIR ${PROJECT_BINARY_DIR}
#include \"${PROJECT_SOURCE_DIR}/${Header}\"
")
# This version will be installed in the top include directory:
@@ -497,12 +434,12 @@ endif()
# Add 'check' target - quick test
if (NOT MFEM_USE_MPI)
add_custom_target(check
${CMAKE_CTEST_COMMAND} -R \"^ex1_ser\" -C ${CMAKE_CFG_INTDIR}
${CMAKE_CTEST_COMMAND} -R '^ex1_ser' -C ${CMAKE_CFG_INTDIR}
USES_TERMINAL)
add_dependencies(check ex1)
else()
add_custom_target(check
${CMAKE_CTEST_COMMAND} -R \"^ex1p\" -C ${CMAKE_CFG_INTDIR}
${CMAKE_CTEST_COMMAND} -R '^ex1p' -C ${CMAKE_CFG_INTDIR}
USES_TERMINAL)
add_dependencies(check ex1p)
endif()
@@ -547,20 +484,6 @@ install(DIRECTORY ${MFEM_SOURCE_DIRS}
DESTINATION ${INSTALL_INCLUDE_DIR}/mfem
FILES_MATCHING PATTERN "*.hpp")
# Install the okl files
if (MFEM_USE_OCCA)
install(DIRECTORY ${MFEM_SOURCE_DIRS}
DESTINATION ${INSTALL_INCLUDE_DIR}/mfem
FILES_MATCHING PATTERN "*.okl")
endif()
# Install the libCEED files
if (MFEM_USE_CEED)
install(DIRECTORY ${MFEM_SOURCE_DIRS}
DESTINATION ${INSTALL_INCLUDE_DIR}/mfem
FILES_MATCHING PATTERN "fem/libceed/*.h")
endif()
# Install ${HEADERS}
# ---
# foreach (HDR ${HEADERS})
-15
View File
@@ -83,8 +83,6 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
│ └── web
│ └── examples
├── examples
│ ├── ginkgo
│ ├── hiop
│ ├── petsc
│ ├── pumi
│ └── sundials
@@ -95,7 +93,6 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
├── miniapps
│ ├── common
│ ├── electromagnetics
│ ├── gslib
│ ├── meshing
│ ├── nurbs
│ ├── performance
@@ -145,16 +142,6 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
+ [`HypreParMatrix`](http://mfem.github.io/doxygen/html/classmfem_1_1HypreParMatrix.html) and [`HypreParVector`](http://mfem.github.io/doxygen/html/classmfem_1_1HypreParVector.html)
+ [`HypreSolver`](http://mfem.github.io/doxygen/html/classmfem_1_1HypreSolver.html) and other [hypre classes](http://mfem.github.io/doxygen/html/hypre_8hpp.html)
- GPU and multi-core CPU support is based on device kernels supporting different
backends (CUDA, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
device/host memory manager.
- The main device-relevant classes and sources are:
+ [`Device`](http://mfem.github.io/doxygen/html/device_8hpp.html)
+ [`MemoryManager`](http://mfem.github.io/doxygen/html/mem_manager_8hpp.html)
+ the [`MFEM_FORALL`](http://mfem.github.io/doxygen/html/forall_8hpp.html) macro
+ the [`cuda.hpp`](http://mfem.github.io/doxygen/html/cuda_8hpp.html) and [`occa.hpp`](http://mfem.github.io/doxygen/html/occa_8hpp.html) files
- The `general/` directory contains C++ classes that serve as utilities for
communication, error handling, arrays, (Boolean) tables, timing, etc.
@@ -353,7 +340,6 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Add the example code to the `ALL_EXE_SRCS` variable.
- [ ] Make sure `THIS_TEST_OPTIONS` is set correctly for the new example.
- [ ] List the new example in `doc/CodeDocumentation.dox`.
- [ ] If new examples directory (e.g.`examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
- [ ] Update or add example-specific documentation, see e.g. the `src/examples.md`.
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
@@ -368,7 +354,6 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Add/update the `CMakeLists.txt` file in the new miniapp directory.
- [ ] Consider adding a new test for the new miniapp.
- [ ] List the new miniapp in `doc/CodeDocumentation.dox`
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
+41 -128
View File
@@ -13,31 +13,22 @@ of MFEM is a (modern) C++ compiler, such as g++. The parallel version of MFEM
requires an MPI C++ compiler, as well as the following external libraries:
- hypre (a library of high-performance preconditioners)
https://github.com/hypre-space/hypre
http://www.llnl.gov/CASC/hypre
- METIS (a family of multilevel partitioning algorithms)
http://glaros.dtc.umn.edu/gkhome/metis/metis/overview
The hypre dependency can be downloaded as a tarball from GitHub or from the
project webpage https://www.llnl.gov/casc/hypre. For example, the 2.16.0 release
of hypre is available at
https://github.com/hypre-space/hypre/archive/v2.16.0.tar.gz
The METIS dependency can be disabled but that is not generally recommended, see
the option MFEM_USE_METIS.
MFEM also includes support for devices such as GPUs, and programming models such
as CUDA, HIP, OCCA, OpenMP and RAJA.
as CUDA, OCCA, OpenMP and RAJA.
- Starting with version 4.0, MFEM requires a C++11 compiler
- CUDA support requires an NVIDIA GPU and an installation of the CUDA Toolkit
https://developer.nvidia.com/cuda-toolkit
- HIP support requires an AMD GPU and an installation of the ROCm software stack
https://rocm.github.io/ROCmInstall.html#installing-from-amd-rocm-repositories
- OCCA support requires the OCCA library
https://libocca.org
@@ -57,7 +48,7 @@ following package managers:
- Spack, https://github.com/spack/spack
- OpenHPC, http://openhpc.community
- Homebrew/Science, https://github.com/Homebrew/homebrew-science (deprecated)
- Homebrew/Science, https://github.com/Homebrew/homebrew-science
We also recommend downloading and building the MFEM-based GLVis visualization
tool which can be used to visualize the meshes and solution in MFEM's examples
@@ -69,19 +60,15 @@ Serial build:
make serial -j 4
Parallel build:
(download hypre and METIS 4 from above URLs)
(download hypre 2.10.0b and METIS 4 from above URLs)
(build METIS 4 in ../metis-4.0 relative to mfem/)
(build hypre in ../hypre relative to mfem/)
(build hypre 2.10.0b in ../hypre-2.10.0b relative to mfem/)
make parallel -j 4
CUDA build:
make cuda -j 4
(build for a specific compute capability: 'make cuda -j 4 CUDA_ARCH=sm_30')
HIP build:
make hip -j 4
(build for a specific AMD GPU chip: 'make hip -j 4 HIP_ARCH=gfx900')
Example codes (serial/parallel, depending on the build):
cd examples
make -j 4
@@ -100,19 +87,13 @@ Serial build:
make -j 4 (assuming "UNIX Makefiles" generator)
Parallel build:
(download hypre and METIS 4 from above URLs)
(download hypre 2.10.0b and METIS 4 from above URLs)
(build METIS 4 in ../metis-4.0 relative to mfem/)
(build hypre in ../hypre relative to mfem/)
(build hypre 2.10.0b in ../hypre-2.10.0b relative to mfem/)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
make -j 4
CUDA build:
(this build requires CMake 3.8 or newer)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES
make -j 4
Example codes (serial/parallel, depending on the build):
make examples -j 4
@@ -168,18 +149,14 @@ Note that re-configuration is only needed to change the currently configured
options. Several shortcut targets combining (re-)configuration and compilation
are also defined:
make serial -> Builds serial optimized version of the library
make parallel -> Builds parallel optimized version of the library
make debug -> Builds serial debug version of the library
make pdebug -> Builds parallel debug version of the library
make cuda -> Builds serial cuda optimized version of the library
make pcuda -> Builds parallel cuda optimized version of the library
make cudebug -> Builds serial cuda debug version of the library
make pcudebug -> Builds parallel cuda debug version of the library
make hip -> Builds serial hip optimized version of the library
make phip -> Builds parallel hip optimized version of the library
make hipdebug -> Builds serial hip debug version of the library
make phipdebug -> Builds parallel hip debug version of the library
make serial -> Builds serial optimized version of the library
make parallel -> Builds parallel optimized version of the library
make debug -> Builds serial debug version of the library
make pdebug -> Builds parallel debug version of the library
make cuda -> Builds serial cuda optimized version of the library
make pcuda -> Builds parallel cuda optimized version of the library
make cudebug -> Builds serial cuda debug version of the library
make pcudebug -> Builds parallel cuda debug version of the library
Note that any of the above shortcuts accept configuration options, either at the
command line or through a user configuration file.
@@ -301,7 +278,6 @@ MFEM_THREAD_SAFE = YES/NO
MFEM_USE_LEGACY_OPENMP = YES/NO
Enable (basic) experimental OpenMP support. Requires MFEM_THREAD_SAFE.
This option is deprecated.
MFEM_USE_OPENMP = YES/NO
Enable the OpenMP backend.
@@ -349,12 +325,6 @@ MFEM_USE_STRUMPACK = YES/NO
classes. When enabled, this option uses the STRUMPACK_* library options, see
below.
MFEM_USE_GINKGO = YES/NO
Enable MFEM functionality based on the Ginkgo library, which provides
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
https://github.com/ginkgo-project/ginkgo. When enabled, the user can use
Ginkgo's solvers and preconditioners as shown in examples/ginkgo/.
MFEM_USE_GNUTLS = YES/NO
Enable secure socket support in class socketstream, using the auxiliary
GnuTLS_* classes, based on the GnuTLS library. This option may be useful in
@@ -389,11 +359,11 @@ MFEM_USE_MPFR = YES/NO
see below.
MFEM_USE_SIDRE = YES/NO
Sidre is a component of LLNL's axom project, https://github.com/LLNL/axom,
that provides an HDF5-based file format for visualization or restart
capability following the Conduit (https://github.com/LLNL/conduit) mesh
blueprint specification. When enabled, this option requires installation of
HDF5 (see also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
Sidre is a component of LLNL's axom project, http://goo.gl/cZyJdn, that
provides an HDF5-based file format for visualization or restart capability
following the Conduit (https://github.com/LLNL/conduit) mesh blueprint
specification. When enabled, this option requires installation of HDF5 (see
also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
MFEM_USE_CONDUIT = YES/NO
Enables support for converting MFEM Mesh and Grid Function objects to and
@@ -421,47 +391,28 @@ MFEM_USE_PUMI = YES/NO
models and effectively supports automated adaptive analysis. PUMI enables
support for parallel unstructured mesh modifications in MFEM.
MFEM_USE_HIOP = YES/NO
Enable the usage of HiOp (https://github.com/LLNL/hiop) in MFEM. HiOp is an
HPC solver for nonlinear optimization problems.
MFEM_USE_MM = YES/NO
Enables support for the MFEM's memory manager (MM), which is required to
support devices with different memory spaces.
MFEM_USE_CUDA = YES/NO
Enables support for CUDA devices in MFEM. CUDA is a parallel computing
platform and programming model for general computing on graphical processing
units (GPUs). The variable CUDA_ARCH is used to specify the CUDA compute
capability used during compilation (by default, CUDA_ARCH=sm_60). When
enabled, this option uses the CUDA_* build options, see below.
MFEM_USE_HIP = YES/NO
Enables support for AMD devices in MFEM. HIP is a heterogeneous-compute
interface for portability developed by AMD that can target both AMD and
NVIDIA GPUs. The variable HIP_ARCH is used to specify the AMD GPU processor
used during compilation (by default, HIP_ARCH=gfx900). When enabled, this
option uses the HIP_* build options, see below.
units (GPUs). This option requires MFEM_USE_MM. The variable CUDA_ARCH is
used to specify the CUDA compute capability used during compilation (by
default, CUDA_ARCH=sm_60). When enabled, this option uses the CUDA_* build
options, see below.
MFEM_USE_RAJA = YES/NO
Enable support for the RAJA performance portability layer in MFEM. RAJA
provides a portable abstraction for loops, supporting different programming
model backends. When using RAJA built with CUDA support, CUDA support must be
also enabled in MFEM, i.e. MFEM_USE_CUDA=YES must be set.
model backends. When using the RAJA CUDA backend, MFEM_USE_MM is required.
MFEM_USE_OCCA = YES/NO
Enables support for the OCCA library in MFEM. OCCA is an open-source library
which aims to make it easy to program different types of devices (e.g. CPU,
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
MFEM_USE_GSLIB = YES/NO
Enables MFEM functionality based on the GSLIB library, and specifically its
FindPoints component, which provides a robust algorithms to evaluate finite
element functions in a collection of points in physical space. When enabled,
the user can use the GSLIB-FindPoints methods as shown in miniapps/gslib.
MFEM_USE_CEED = YES/NO
Enables support for the libCEED library in MFEM. libCEED is a portable
library for performant high-order operator evaluation developed by the Center
for Efficient Exascale Discretizations in the Exascale Computing Project.
backends. When using the OCCA CUDA backend, MFEM_USE_MM is required.
MFEM_BUILD_TAG = (any value)
An optional tag to characterize the build. Exported to config/config.mk.
@@ -484,7 +435,7 @@ directory and use the string @MFEM_DIR@, e.g. HYPRE_OPT = -I@MFEM_DIR@/../hypre.
The specific libraries and their options are:
- HYPRE, required for the parallel build, i.e. when MFEM_USE_MPI = YES.
URL: https://github.com/hypre-space/hypre and https://www.llnl.gov/casc/hypre
URL: http://www.llnl.gov/CASC/hypre
Options: HYPRE_OPT, HYPRE_LIB.
- METIS, used when MFEM_USE_METIS = YES. If using METIS 5, set
@@ -509,7 +460,6 @@ The specific libraries and their options are:
- SUNDIALS (optional), used when MFEM_USE_SUNDIALS = YES.
Beginning with MFEM v3.3, SUNDIALS v2.7.0 is supported.
Beginning with MFEM v3.3.2, SUNDIALS v3.0.0 is also supported.
Beginning with MFEM v4.1, only SUNDIALS v5.0.0+ is supported.
If MFEM_USE_MPI is enabled, we expect that SUNDIALS is built with support for
both MPI and hypre.
URL: http://computation.llnl.gov/projects/sundials/sundials-software
@@ -539,12 +489,6 @@ The specific libraries and their options are:
URL: http://portal.nersc.gov/project/sparse/strumpack
Options: STRUMPACK_OPT, STRUMPACK_LIB.
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
C++ compiler that supports the C++-11 standard. For additional requirements
and dependencies of specific modules see the Ginkgo webpage below.
URL: https://ginkgo-project.github.io
Options: GINKGO_OPT (Not used), GINKGO_LIB.
- GnuTLS (optional), used when MFEM_USE_GNUTLS = YES. On most Linux systems,
GnuTLS is available as a development package, e.g. gnutls-devel. On Mac OS X,
one can get the library through the Homebrew package manager (http://brew.sh).
@@ -570,55 +514,30 @@ The specific libraries and their options are:
Options: PETSC_OPT, PETSC_LIB.
- Sidre (optional), part of LLNL's axom project, used when MFEM_USE_SIDRE = YES.
Starting with MFEM v4.1, Axom version 0.3.1 or later is required.
URL: https://github.com/LLNL/axom
URL: http://goo.gl/cZyJdn (axom, to be released)
https://github.com/LLNL/conduit (Conduit)
https://support.hdfgroup.org/HDF5 (HDF5)
Options: SIDRE_OPT, SIDRE_LIB.
- Conduit (optional), used when MFEM_USE_CONDUIT = YES. Conduit Mesh Blueprint
- Conduit, used when MFEM_USE_CONDUIT = YES. Direct Conduit Mesh Blueprint
support requires Conduit >= v0.3.1 and VisIt >= v2.13.1 to read the output.
URL: https://github.com/LLNL/conduit (Conduit)
https://support.hdfgroup.org/HDF5 (HDF5)
Options: CONDUIT_OPT, CONDUIT_LIB.
- PUMI (optional), used when MFEM_USE_PUMI = YES.
- PUMI, used when MFEM_USE_PUMI = YES.
URL: https://scorec.rpi.edu/pumi
Options: PUMI_OPT, PUMI_LIB.
- HiOp (optional), used when MFEM_USE_HIOP = YES.
URL: https://github.com/LLNL/hiop
Options: HIOP_OPT, HIOP_LIB.
- GSLIB (optional), used when MFEM_USE_GSLIB = YES. The gslib library must be
built prior to the MFEM build, as follows: download gslib-1.0.5, untar it at
the same level as MFEM and create a symbolic link: "ln -s gslib-1.0.5 gslib".
Build gslib in parallel or in serial based on the desired MFEM build: "make
clean; make CC=mpicc" or "make clean; make CC=gcc MPI=0". Build MFEM with
MFEM_USE_GSLIB=YES.
URL: https://github.com/gslib/gslib/archive/v1.0.5.tar.gz
Options: GSLIB_OPT, GSLIB_LIB.
- CUDA (optional), used when MFEM_USE_CUDA = YES.
- CUDA, used when MFEM_USE_CUDA = YES.
URL: https://developer.nvidia.com/cuda-toolkit
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
- HIP (optional), used when MFEM_USE_HIP = YES.
URL: https://rocm.github.io/ROCmInstall.html
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
- OCCA (optional), used when MFEM_USE_OCCA = YES.
- OCCA, used when MFEM_USE_OCCA = YES.
URL: https://libocca.org
Options: OCCA_DIR, OCCA_OPT, OCCA_LIB.
- libCEED (optional), used when MFEM_USE_CEED = YES. Requires libCEED's master
branch, specifically, git-hash c00ee0d or later.
URL: https://github.com/CEED/libCEED
https://ceed.exascaleproject.org/libceed
Options: CEED_DIR, CEED_OPT, CEED_LIB.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
- RAJA, used when MFEM_USE_RAJA = YES.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
@@ -637,6 +556,7 @@ The specific libraries and their options are:
URL: https://zlib.net
Options: ZLIB_OPT, ZLIB_LIB.
Building with CMake
===================
The MFEM build system consists of two steps: configuration and compilation.
@@ -725,8 +645,6 @@ Configuration variables (CMake)
===============================
See the configuration file config/defaults.cmake for the default settings.
Note: the option MFEM_USE_CUDA requires CMake version 3.8 or newer!
Non-standard CMake variables for compilers:
CXX - If set, overwrite the auto-detected C++ compiler, serial build
MPICXX - If set, overwrite the auto-detected MPI C++ compiler, parallel build
@@ -752,18 +670,18 @@ MFEM_USE_MESQUITE
MFEM_USE_SUITESPARSE
MFEM_USE_SUPERLU
MFEM_USE_STRUMPACK
MFEM_USE_GINKGO
MFEM_USE_GNUTLS
MFEM_USE_NETCDF
MFEM_USE_MPFR
MFEM_USE_GZSTREAM
MFEM_USE_PUMI
MFEM_USE_HIOP
The following GNU make options are not supported with CMake yet:
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_CEED
MFEM_USE_RAJA
MFEM_USE_SIDRE
MFEM_USE_MM
The following options are CMake specific:
@@ -804,17 +722,12 @@ The CMake build system adds auto-detection for the following packages/libraries:
- SuiteSparse
- SuperLUDist, STRUMPACK
- ParMETIS
- Ginkgo
- GNUTLS - Extends the built-in CMake support, to search GNUTLS_DIR as well.
- NETCDF
- MPFR
- LIBUNWIND
- POSIXCLOCKS
- PUMI
- HIOP
- OCCA
- RAJA
- AXOM - Used when MFEM_USE_SIDRE is enabled
The following built-in CMake packages are also used:
+16 -17
View File
@@ -8,9 +8,9 @@
http://mfem.org
MFEM is a modular parallel C++ library for finite element methods. Its goal is
to enable high-performance scalable finite element discretization research and
application development on a wide variety of platforms, ranging from laptops to
supercomputers.
to enable the research and development of scalable finite element discretization
and solver algorithms through general finite element abstractions, accurate and
flexible visualization, and tight integration with the hypre library.
* For building instructions, see the file INSTALL, or type "make help".
@@ -39,24 +39,23 @@ conforming and non-conforming (AMR) adaptive refinement. Arbitrary element
transformations, allowing for high-order mesh elements with curved boundaries,
are also supported.
When used as a "finite element to linear algebra translator", MFEM can take a
problem described in terms of finite element-type objects, and produce the
corresponding linear algebra vectors and fully or partially assembled operators,
e.g. in the form of global sparse matrices or matrix-free operators. The library
includes simple smoothers and Krylov solvers, such as PCG, MINRES and GMRES, as
well as support for sequential sparse direct solvers from the SuiteSparse
MFEM is commonly used as a "finite element to linear algebra translator", since
it can take a problem described in terms of finite element-type objects, and
produce the corresponding linear algebra vectors and sparse matrices. In order
to facilitate this, MFEM uses compressed sparse row (CSR) sparse matrix storage
and includes simple smoothers and Krylov solvers, such as PCG, MINRES and GMRES,
as well as support for sequential sparse direct solvers from the SuiteSparse
library. Nonlinear solvers (the Newton method), eigensolvers (LOBPCG), and
several explicit and implicit Runge-Kutta time integrators are also available.
MFEM supports MPI-based parallelism throughout the library, and can readily be
used as a scalable unstructured finite element problem generator. As of version
4.0, MFEM offers initial support for GPU acceleration, and programming models,
such as CUDA, OCCA, RAJA and OpenMP. MFEM-based applications require minimal
changes to switch from a serial to a high-performing MPI-parallel version of the
code, where they can take advantage of the integrated linear solvers from the
hypre library. Comprehensive support for other external packages, e.g. PETSc
and SUNDIALS is also included, giving access to many additional linear and
nonlinear solvers, preconditioners, time integrators, etc.
used as a scalable unstructured finite element problem generator. MFEM-based
applications require minimal changes to transition from a serial to a
high-performing parallel version of the code, where they can take advantage of
the integrated scalable linear solvers from the hypre library. Comprehensive
support for other external packages, e.g. PETSc and SUNDIALS is also included,
giving access to many additional linear and nonlinear solvers, preconditioners,
time integrators, etc.
For examples of using MFEM, see the examples/ and miniapps/ directories, as well
as the OpenGL visualization tool GLVis which is available at http://glvis.org.
+4 -27
View File
@@ -74,7 +74,7 @@
IF (NOT COMMAND PRINT_VAR)
FUNCTION(PRINT_VAR VAR_NAME)
MESSAGE(STATUS "${VAR_NAME} = '${${VAR_NAME}}'")
MESSAGE("-- " "${VAR_NAME} = '${${VAR_NAME}}'")
ENDFUNCTION()
ENDIF()
@@ -166,14 +166,14 @@ IF (USE_XSDK_DEFAULTS)
ENDIF()
XSDK_HANDLE_LANG_DEFAULTS(Fortran FC "FFLAGS;FCFLAGS")
ENDIF()
# Set XSDK defaults for other CMake variables
IF ("${BUILD_SHARED_LIBS}" STREQUAL "")
MESSAGE("-- " "XSDK: Setting default BUILD_SHARED_LIBS=TRUE")
SET(BUILD_SHARED_LIBS TRUE CACHE BOOL "Set by default in XSDK mode")
ENDIF()
IF ("${CMAKE_BUILD_TYPE}" STREQUAL "")
MESSAGE("-- " "XSDK: Setting default CMAKE_BUILD_TYPE=DEBUG")
SET(CMAKE_BUILD_TYPE DEBUG CACHE STRING "Set by default in XSDK mode")
@@ -181,13 +181,6 @@ IF (USE_XSDK_DEFAULTS)
ENDIF()
##################################################################################
#
# MFEM-specific additions: set TPL MFEM_USE_* defaults
#
##################################################################################
IF (DEFINED TPL_ENABLE_MPI)
SET(MFEM_USE_MPI ${TPL_ENABLE_MPI} CACHE BOOL "Enable MPI parallel build" FORCE)
ENDIF()
@@ -232,10 +225,6 @@ IF (DEFINED TPL_ENABLE_GECKO)
SET(MFEM_USE_GECKO ${TPL_ENABLE_GECKO} CACHE BOOL "Enable GECKO usage" FORCE)
ENDIF()
IF (DEFINED TPL_ENABLE_GINKGO)
SET(MFEM_USE_GINKGO ${TPL_ENABLE_GINKGO} CACHE BOOL "Enable GINKGO usage" FORCE)
ENDIF()
IF (DEFINED TPL_ENABLE_GNUTLS)
SET(MFEM_USE_GNUTLS ${TPL_ENABLE_GNUTLS} CACHE BOOL "Enable GNUTLS usage" FORCE)
ENDIF()
@@ -263,15 +252,3 @@ ENDIF()
IF (DEFINED TPL_ENABLE_PUMI)
SET(MFEM_USE_PUMI ${TPL_ENABLE_PUMI} CACHE BOOL "Enable PUMI" FORCE)
ENDIF()
IF (DEFINED TPL_ENABLE_CUDA)
SET(MFEM_USE_CUDA ${TPL_ENABLE_CUDA} CACHE BOOL "Enable CUDA" FORCE)
ENDIF()
IF (DEFINED TPL_ENABLE_OCCA)
SET(MFEM_USE_OCCA ${TPL_ENABLE_OCCA} CACHE BOOL "Enable OCCA" FORCE)
ENDIF()
IF (DEFINED TPL_ENABLE_RAJA)
SET(MFEM_USE_RAJA ${TPL_ENABLE_RAJA} CACHE BOOL "Enable RAJA" FORCE)
ENDIF()
-5
View File
@@ -34,7 +34,6 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
set(MFEM_USE_GECKO @MFEM_USE_GECKO@)
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
set(MFEM_USE_NETCDF @MFEM_USE_NETCDF@)
set(MFEM_USE_PETSC @MFEM_USE_PETSC@)
@@ -42,10 +41,6 @@ set(MFEM_USE_MPFR @MFEM_USE_MPFR@)
set(MFEM_USE_SIDRE @MFEM_USE_SIDRE@)
set(MFEM_USE_CONDUIT @MFEM_USE_CONDUIT@)
set(MFEM_USE_PUMI @MFEM_USE_PUMI@)
set(MFEM_USE_CUDA @MFEM_USE_CUDA@)
set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
set(MFEM_CXX_FLAGS "@CMAKE_CXX_FLAGS@")
-25
View File
@@ -30,12 +30,6 @@
#define MFEM_VERSION_MINOR (((MFEM_VERSION)/100)%100)
#define MFEM_VERSION_PATCH ((MFEM_VERSION)%100)
// MFEM source directory.
#define MFEM_SOURCE_DIR "@MFEM_SOURCE_DIR@"
// MFEM install directory.
#define MFEM_INSTALL_DIR "@MFEM_INSTALL_DIR@"
// Description of the git commit used to build MFEM.
#cmakedefine MFEM_GIT_STRING "@MFEM_GIT_STRING@"
@@ -92,9 +86,6 @@
// Enable functionality based on the Gecko library
#cmakedefine MFEM_USE_GECKO
// Enable functionality based on the Ginkgo library
#cmakedefine MFEM_USE_GINKGO
// Enable MFEM functionality based on the GnuTLS library
#cmakedefine MFEM_USE_GNUTLS
@@ -113,22 +104,6 @@
// Enable MFEM functionality based on the PUMI library
#cmakedefine MFEM_USE_PUMI
// Enable MFEM functionality based on the HiOp library
#cmakedefine MFEM_USE_HIOP
// Build the GPU/CUDA-enabled version of the MFEM library.
// Requires a CUDA compiler (nvcc).
#cmakedefine MFEM_USE_CUDA
// Enable MFEM functionality based on the RAJA library
#cmakedefine MFEM_USE_RAJA
// Enable MFEM functionality based on the OCCA library
#cmakedefine MFEM_USE_OCCA
// Enable MFEM functionality based on the libCEED library
#cmakedefine MFEM_USE_CEED
// Which library functions to use in class StopWatch for measuring time.
// For a list of the available options, see INSTALL.
// If not defined, an option is selected automatically.
+3 -1
View File
@@ -18,4 +18,6 @@ include(MfemCmakeUtilities)
# Note: components are enabled based on the find_package() parameters.
mfem_find_package(Axom AXOM AXOM_DIR "include" "" "lib" ""
"Paths to headers required by Axom." "Libraries required by Axom."
ADD_COMPONENT Axom "include" axom/config.hpp "lib" axom)
ADD_COMPONENT Sidre "include" sidre/sidre.hpp "lib" sidre
ADD_COMPONENT SLIC "include" slic/slic.hpp "lib" slic
ADD_COMPONENT axom_utils "include" axom_utils/Utilities.hpp "lib" axom_utils)
-36
View File
@@ -1,36 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Sets the following variables:
# - HIOP_FOUND
# - HIOP_INCLUDE_DIRS
# - HIOP_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(HIOP HIOP HIOP_DIR
"include" "hiopInterface.hpp"
"lib" "hiop"
"Paths to headers required by HIOP."
"Libraries required by HIOP.")
# this test fails with parallel MFEM since mpi.h is not available (cxx compiler is used for some reason)
# CHECK_BUILD HIOP_VERSION_OK TRUE
#"
##include <hiopInterface.hpp>
#using namespace hiop;
#int main(int argc, char *argv[])
#{
# MPI_Init(&argc, &argv);
# MPI_Comm comm = MPI_COMM_WORLD;
#
# return 0;
#}
#")
-19
View File
@@ -1,19 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Defines the following variables:
# - OCCA_FOUND
# - OCCA_LIBRARIES
# - OCCA_INCLUDE_DIRS
include(MfemCmakeUtilities)
mfem_find_package(OCCA OCCA OCCA_DIR "include" "occa.hpp" "lib" "occa"
"Paths to headers required by OCCA." "Libraries required by OCCA.")
-30
View File
@@ -1,30 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Defines the following variables:
# - RAJA_FOUND
# - RAJA_LIBRARIES
# - RAJA_INCLUDE_DIRS
include(MfemCmakeUtilities)
mfem_find_package(RAJA RAJA RAJA_DIR "include" "RAJA/RAJA.hpp" "lib" "RAJA"
"Paths to headers required by RAJA." "Libraries required by RAJA.")
if (NOT RAJA_CONFIG_CMAKE)
set(RAJA_CONFIG_CMAKE "${RAJA_DIR}/share/raja/cmake/raja-config.cmake")
endif()
if (EXISTS "${RAJA_CONFIG_CMAKE}")
include("${RAJA_CONFIG_CMAKE}")
if (ENABLE_CUDA AND NOT MFEM_USE_CUDA)
message(FATAL_ERROR
"RAJA is built with CUDA: MFEM_USE_CUDA=YES is required")
endif()
endif()
-19
View File
@@ -1,19 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Defines the following variables:
# - CEED_FOUND
# - CEED_LIBRARIES
# - CEED_INCLUDE_DIRS
include(MfemCmakeUtilities)
mfem_find_package(libCEED CEED CEED_DIR "include" ceed.h "lib" ceed
"Paths to headers required by libCEED." "Libraries required by libCEED.")
@@ -232,12 +232,10 @@ function(mfem_find_package Name Prefix DirVar IncSuffixes Header LibSuffixes
# If we have the TPL_ versions of _INCLUDE_DIRS and _LIBRARIES then set the
# standard ${Prefix} versions
if (TPL_${Prefix}_INCLUDE_DIRS)
set(${Prefix}_INCLUDE_DIRS ${TPL_${Prefix}_INCLUDE_DIRS} CACHE STRING
"TPL_${Prefix}_INCLUDE_DIRS was found." FORCE)
set(${Prefix}_INCLUDE_DIRS ${TPL_${Prefix}_INCLUDE_DIRS} CACHE STRING "TPL_${Prefix}_INCLUDE_DIRS was found." FORCE)
endif()
if (TPL_${Prefix}_LIBRARIES)
set(${Prefix}_LIBRARIES ${TPL_${Prefix}_LIBRARIES} CACHE STRING
"TPL_${Prefix}_LIBRARIES was found." FORCE)
set(${Prefix}_LIBRARIES ${TPL_${Prefix}_LIBRARIES} CACHE STRING "TPL_${Prefix}_LIBRARIES was found." FORCE)
endif()
# Quick return
@@ -720,7 +718,7 @@ function(mfem_export_mk_files)
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GECKO MFEM_USE_GNUTLS
MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_MPFR MFEM_USE_SIDRE
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA)
MFEM_USE_CONDUIT MFEM_USE_PUMI)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
@@ -728,7 +726,6 @@ function(mfem_export_mk_files)
set(${var} NO)
endif()
endforeach()
# TODO: Add support for MFEM_USE_CUDA=YES
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_CPPFLAGS "")
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
@@ -814,7 +811,7 @@ function(mfem_export_mk_files)
foreach(lib ${TPL_LIBRARIES})
get_filename_component(suffix ${lib} EXT)
# handle interfaces (e.g., SCOREC::apf)
if ("${lib}" MATCHES "SCOREC::.*" OR "${lib}" MATCHES "Ginkgo::.*")
if ("${lib}" MATCHES "SCOREC::.*")
elseif (NOT "${lib}" MATCHES "SCOREC::.*" AND "${lib}" MATCHES ".*::.*")
message(FATAL_ERROR "***** interface lib found ... exiting *****")
# handle static and shared libs
+6 -3
View File
@@ -10,15 +10,18 @@
// Software Foundation) version 2.1 dated February 1999.
// Support out-of-source builds: if MFEM_CONFIG_FILE is defined, include it.
// Support out-of-source builds: if MFEM_BUILD_DIR is defined, load the config
// file MFEM_BUILD_DIR/config/_config.hpp.
//
// Otherwise, use the local file: _config.hpp.
#ifndef MFEM_CONFIG_HPP
#define MFEM_CONFIG_HPP
#ifdef MFEM_CONFIG_FILE
#include MFEM_CONFIG_FILE
#ifdef MFEM_BUILD_DIR
#define MFEM_QUOTE(a) #a
#define MFEM_MAKE_PATH(x,y) MFEM_QUOTE(x/y)
#include MFEM_MAKE_PATH(MFEM_BUILD_DIR,config/_config.hpp)
#else
#include "_config.hpp"
#endif
+3 -16
View File
@@ -100,9 +100,6 @@
// Enable functionality based on the Gecko library
// #define MFEM_USE_GECKO
// Enable MFEM features based on the Ginkgo library
// #define MFEM_USE_GINKGO
// Enable secure socket streams based on the GNUTLS library
// #define MFEM_USE_GNUTLS
@@ -124,28 +121,18 @@
// Enable MFEM functionality based on the PUMI library
// #define MFEM_USE_PUMI
// Enable MFEM functionality based on the HIOP library.
// #define MFEM_USE_HIOP
// Enable MFEM functionality based on the GSLIB library
// #define MFEM_USE_GSLIB
// Build the NVIDIA GPU/CUDA-enabled version of the MFEM library.
// Build the GPU/CUDA-enabled version of the MFEM library.
// Requires a CUDA compiler (nvcc).
// #define MFEM_USE_CUDA
// Build the AMD GPU/HIP-enabled version of the MFEM library.
// Requires a HIP compiler (hipcc).
// #define MFEM_USE_HIP
// Enable functionality based on the RAJA library.
// #define MFEM_USE_RAJA
// Enable functionality based on the OCCA library.
// #define MFEM_USE_OCCA
// Enable functionality based on the libCEED library.
// #define MFEM_USE_CEED
// Enable MFEM's internal Memory Manager (needed e.g. for MFEM_USE_CUDA)
// #define MFEM_USE_MM
// Version of HYPRE used for building MFEM.
// #define MFEM_HYPRE_VERSION @MFEM_HYPRE_VERSION@
+1 -5
View File
@@ -34,7 +34,6 @@ MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
MFEM_USE_GECKO = @MFEM_USE_GECKO@
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
@@ -42,13 +41,10 @@ MFEM_USE_MPFR = @MFEM_USE_MPFR@
MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
MFEM_USE_PUMI = @MFEM_USE_PUMI@
MFEM_USE_HIOP = @MFEM_USE_HIOP@
MFEM_USE_GSLIB = @MFEM_USE_GSLIB@
MFEM_USE_CUDA = @MFEM_USE_CUDA@
MFEM_USE_HIP = @MFEM_USE_HIP@
MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
MFEM_USE_CEED = @MFEM_USE_CEED@
MFEM_USE_MM = @MFEM_USE_MM@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
+3 -32
View File
@@ -35,7 +35,6 @@ option(MFEM_USE_SUITESPARSE "Enable SuiteSparse usage" OFF)
option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
option(MFEM_USE_GECKO "Enable GECKO usage" OFF)
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
option(MFEM_USE_PETSC "Enable PETSc support." OFF)
@@ -43,13 +42,6 @@ option(MFEM_USE_MPFR "Enable MPFR usage." OFF)
option(MFEM_USE_SIDRE "Enable Axom/Sidre usage" OFF)
option(MFEM_USE_CONDUIT "Enable Conduit usage" OFF)
option(MFEM_USE_PUMI "Enable PUMI" OFF)
option(MFEM_USE_HIOP "Enable HiOp" OFF)
option(MFEM_USE_CUDA "Enable CUDA" OFF)
option(MFEM_USE_OCCA "Enable OCCA" OFF)
option(MFEM_USE_RAJA "Enable RAJA" OFF)
option(MFEM_USE_CEED "Enable CEED" OFF)
option(MFEM_USE_ADEPT "Enable AD using ADEPT" OFF)
option(MFEM_USE_CODIPACK "Enable AD using CoDiPack" OFF)
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
@@ -67,16 +59,13 @@ option(MFEM_ENABLE_MINIAPPS "Build all of the miniapps" OFF)
# set(CXX g++)
# set(MPICXX mpicxx)
# Set the target CUDA architecture
set(CUDA_ARCH "sm_60" CACHE STRING "Target CUDA architecture.")
set(MFEM_DIR ${CMAKE_CURRENT_SOURCE_DIR})
# The *_DIR paths below will be the first place searched for the corresponding
# headers and library. If these fail, then standard cmake search is performed.
# Note: if the variables are already in the cache, they are not overwritten.
set(HYPRE_DIR "${MFEM_DIR}/../hypre/src/hypre" CACHE PATH
set(HYPRE_DIR "${MFEM_DIR}/../hypre-2.10.0b/src/hypre" CACHE PATH
"Path to the hypre library.")
# If hypre was compiled to depend on BLAS and LAPACK:
# set(HYPRE_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
@@ -86,7 +75,7 @@ set(METIS_DIR "${MFEM_DIR}/../metis-4.0" CACHE PATH "Path to the METIS library."
set(LIBUNWIND_DIR "" CACHE PATH "Path to Libunwind.")
set(SUNDIALS_DIR "${MFEM_DIR}/../sundials-5.0.0/instdir" CACHE PATH
set(SUNDIALS_DIR "${MFEM_DIR}/../sundials-3.0.0" CACHE PATH
"Path to the SUNDIALS library.")
# The following may be necessary, if SUNDIALS was built with KLU:
# set(SUNDIALS_REQUIRED_PACKAGES "SuiteSparse/KLU/AMD/BTF/COLAMD/config"
@@ -141,8 +130,6 @@ set(ScaLAPACK_TARGET_NAMES scalapack)
set(GECKO_DIR "${MFEM_DIR}/../gecko" CACHE PATH "Path to the Gecko library.")
set(Ginkgo_DIR "${MFEM_DIR}/../ginkgo" CACHE PATH "Path to the Ginkgo library.")
set(GNUTLS_DIR "" CACHE PATH "Path to the GnuTLS library.")
set(NETCDF_DIR "" CACHE PATH "Path to the NetCDF library.")
@@ -161,33 +148,17 @@ set(CONDUIT_DIR "${MFEM_DIR}/../conduit" CACHE PATH
set(AXOM_DIR "${MFEM_DIR}/../axom" CACHE PATH "Path to the Axom library.")
# May need to add "Boost" as requirement.
set(Axom_REQUIRED_PACKAGES "Conduit/relay/blueprint" CACHE STRING
set(Axom_REQUIRED_PACKAGES "Conduit/relay" CACHE STRING
"Additional packages required by Axom.")
set(PUMI_DIR "${MFEM_DIR}/../pumi-2.1.0" CACHE STRING
"Directory where PUMI is installed")
set(HIOP_DIR "${MFEM_DIR}/../hiop/install" CACHE STRING
"Directory where HiOp is installed")
set(HIOP_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
"Packages that HiOp depends on.")
set(OCCA_DIR "${MFEM_DIR}/../occa" CACHE PATH "Path to OCCA")
set(RAJA_DIR "${MFEM_DIR}/../raja" CACHE PATH "Path to RAJA")
set(CEED_DIR "${MFEM_DIR}/../libCEED" CACHE PATH "Path to libCEED")
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
set(LAPACK_LIBRARIES "" CACHE STRING "The LAPACK library.")
set(ADEPT_INCLUDE_DIRS "${MFEM_DIR}/../adept-1.1op/include" CACHE STRING "Path to ADEPT headers.")
set(ADEPT_LIBRARIES "-L${MFEM_DIR}/../adept-1.1op/lib -ladept" CACHE STRING "The ADEPT library.")
set(CODIPACK_INCLUDE_DIRS "${MFEM_DIR}/../CoDiPack/include" CACHE STRING "Path to CoDiPack headers.")
# Some useful variables:
set(CMAKE_SKIP_PREPROCESSED_SOURCE_RULES ON) # Skip *.i rules
set(CMAKE_SKIP_ASSEMBLY_SOURCE_RULES ON) # Skip *.s rules
+12 -46
View File
@@ -46,14 +46,6 @@ CUDA_FLAGS = -x=cu --expt-extended-lambda -arch=$(CUDA_ARCH)
CUDA_XCOMPILER = -Xcompiler=
CUDA_XLINKER = -Xlinker=
# HIP configuration options
HIP_CXX = hipcc
# The HIP_ARCH option specifies the AMD GPU processor, similar to CUDA_ARCH. For
# example: gfx600 (tahiti), gfx700 (kaveri), gfx701 (hawaii), gfx801 (carrizo),
# gfx900, gfx1010, etc.
HIP_ARCH = gfx900
HIP_FLAGS = --amdgpu-target=$(HIP_ARCH)
ifneq ($(NOTMAC),)
AR = ar
ARFLAGS = cruv
@@ -122,7 +114,6 @@ MFEM_USE_SUITESPARSE = NO
MFEM_USE_SUPERLU = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_GECKO = NO
MFEM_USE_GINKGO = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
@@ -130,13 +121,10 @@ MFEM_USE_MPFR = NO
MFEM_USE_SIDRE = NO
MFEM_USE_CONDUIT = NO
MFEM_USE_PUMI = NO
MFEM_USE_HIOP = NO
MFEM_USE_GSLIB = NO
MFEM_USE_CUDA = NO
MFEM_USE_HIP = NO
MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_CEED = NO
MFEM_USE_MM = NO
# Compile and link options for zlib.
ZLIB_DIR =
@@ -148,7 +136,7 @@ LIBUNWIND_OPT = -g
LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
# HYPRE library configuration (needed to build the parallel version)
HYPRE_DIR = @MFEM_DIR@/../hypre/src/hypre
HYPRE_DIR = @MFEM_DIR@/../hypre-2.10.0b/src/hypre
HYPRE_OPT = -I$(HYPRE_DIR)/include
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
@@ -186,9 +174,9 @@ OPENMP_LIB =
POSIX_CLOCKS_LIB = -lrt
# SUNDIALS library configuration
SUNDIALS_DIR = @MFEM_DIR@/../sundials-5.0.0/instdir
SUNDIALS_DIR = @MFEM_DIR@/../sundials-3.0.0
SUNDIALS_OPT = -I$(SUNDIALS_DIR)/include
SUNDIALS_LIB = -Wl,-rpath,$(SUNDIALS_DIR)/lib64 -L$(SUNDIALS_DIR)/lib64\
SUNDIALS_LIB = -Wl,-rpath,$(SUNDIALS_DIR)/lib -L$(SUNDIALS_DIR)/lib\
-lsundials_arkode -lsundials_cvode -lsundials_nvecserial -lsundials_kinsol
ifeq ($(MFEM_USE_MPI),YES)
@@ -213,7 +201,7 @@ SUITESPARSE_LIB = -Wl,-rpath,$(SUITESPARSE_DIR)/lib -L$(SUITESPARSE_DIR)/lib\
# SuperLU library configuration
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_5.1.0
SUPERLU_OPT = -I$(SUPERLU_DIR)/SRC
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib -lsuperlu_dist_5.1.0
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/SRC -L$(SUPERLU_DIR)/SRC -lsuperlu_dist
# SCOTCH library configuration (required by STRUMPACK <= v2.1.0, optional in
# STRUMPACK >= v2.2.0)
@@ -248,11 +236,6 @@ GECKO_DIR = @MFEM_DIR@/../gecko
GECKO_OPT = -I$(GECKO_DIR)/inc
GECKO_LIB = -L$(GECKO_DIR)/lib -lgecko
# Ginkgo library configuration (currently not needed)
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
GINKGO_OPT = -isystem $(GINKGO_DIR)/include
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_DIR)/lib -L$(GINKGO_DIR)/lib -lginkgo -lginkgo_omp -lginkgo_cuda -lginkgo_reference
# GnuTLS library configuration
GNUTLS_OPT =
GNUTLS_LIB = -lgnutls
@@ -308,7 +291,7 @@ SIDRE_LIB = \
-Wl,-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-laxom -lconduit -lconduit_relay -lconduit_blueprint -lhdf5 $(ZLIB_LIB) -ldl
-lsidre -lslic -laxom_utils -lconduit -lconduit_relay -lhdf5 $(ZLIB_LIB) -ldl
# PUMI
# Note that PUMI_DIR is needed -- it is used to check for gmi_sim.h
@@ -317,36 +300,19 @@ PUMI_OPT = -I$(PUMI_DIR)/include
PUMI_LIB = -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf -lpcu -lgmi -lparma\
-llion -lmth -lapf_zoltan -lspr
# HIOP
HIOP_DIR = @MFEM_DIR@/../hiop/install
HIOP_OPT = -I$(HIOP_DIR)/include
HIOP_LIB = -L$(HIOP_DIR)/lib -lhiop $(LAPACK_LIB)
# GSLIB library
GSLIB_DIR = @MFEM_DIR@/../gslib/build
GSLIB_OPT = -I$(GSLIB_DIR)/include
GSLIB_LIB = -L$(GSLIB_DIR)/lib -lgs
# CUDA library configuration (currently not needed)
# CUDA library configuration. Since we compile and link with nvcc (when CUDA is
# enabled) we only need to explicitly link with the CUDA driver, libcuda.*,
# which is usually in a system path.
CUDA_OPT =
CUDA_LIB =
# HIP library configuration (currently not needed)
HIP_OPT =
HIP_LIB =
CUDA_LIB = $(if $(NOTMAC),,-L/usr/local/cuda/lib) -lcuda
# OCCA library configuration
OCCA_DIR = @MFEM_DIR@/../occa
OCCA_DIR ?= @MFEM_DIR@/../occa
OCCA_OPT = -I$(OCCA_DIR)/include
OCCA_LIB = $(XLINKER)-rpath,$(OCCA_DIR)/lib -L$(OCCA_DIR)/lib -locca
# libCEED library configuration
CEED_DIR ?= @MFEM_DIR@/../libCEED
CEED_OPT = -I$(CEED_DIR)/include
CEED_LIB = $(XLINKER)-rpath,$(CEED_DIR)/lib -L$(CEED_DIR)/lib -lceed
# RAJA library configuration
RAJA_DIR = @MFEM_DIR@/../raja
RAJA_DIR ?= @MFEM_DIR@/../raja
RAJA_OPT = -I$(RAJA_DIR)/include
ifdef CUB_DIR
RAJA_OPT += -I$(CUB_DIR)
+1 -2
View File
@@ -36,7 +36,6 @@ CONFIG_MK = config.mk
all: header config-mk
MPI = $(MFEM_USE_MPI:NO=)
GHV_CXX ?= $(MFEM_CXX)
GHV = get_hypre_version
GHV_FLAGS = $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..),$(HYPRE_OPT))
SMX = $(if $(MFEM_USE_PUMI:NO=),MFEM_USE_SIMMETRIX)
@@ -45,7 +44,7 @@ SMX_FILE = $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..),$(SMX_PATH))
$(GHV): $(SRC)$(GHV).cpp
$(call mfem-info, Determining HYPRE version ...)
$(GHV_CXX) ${GHV_FLAGS} $(SRC)$(GHV).cpp -o $(GHV)
$(MFEM_CXX) ${GHV_FLAGS} $(SRC)$(GHV).cpp -o $(GHV)
$(GHV).out: $(GHV)
./$(GHV) > $(GHV).out
.INTERMEDIATE: $(GHV) $(GHV).out
+1 -53
View File
@@ -18,8 +18,6 @@ run_prefix=""
run_vg="valgrind --leak-check=full --show-reachable=yes --track-origins=yes"
run_suffix="-no-vis"
skip_gen_meshes="yes"
# filter-out device runs ("no") or non-device runs ("yes"):
device_runs="no"
cur_dir="${PWD}"
mfem_dir="$(cd "$(dirname "$0")"/.. && pwd)"
mfem_build_dir=""
@@ -150,20 +148,6 @@ function extract_sample_runs()
if [ "$skip_gen_meshes" == "yes" ]; then
runs=`printf "%s" "$runs" | grep -v ".* -m .*\.gen"`
fi
if [ "$device_runs" == "yes" ]; then
runs=`printf "%s" "$runs" | grep ".* -d .*"`
if [ "$have_occa" == "no" ]; then
runs=`printf "%s" "$runs" | grep -v ".* -d occa-.*"`
fi
if [ "$have_raja" == "no" ]; then
runs=`printf "%s" "$runs" | grep -v ".* -d raja-.*"`
fi
if [ "$have_ceed" == "no" ]; then
runs=`printf "%s" "$runs" | grep -v ".* -d ceed-.*"`
fi
else
runs=`printf "%s" "$runs" | grep -v ".* -d .*"`
fi
IFS=$'\n'
runs=(${runs})
IFS="${old_IFS}"
@@ -185,9 +169,6 @@ function help_message()
-g <dir> <pattern>
Specify explicitly a group (dir + file pattern) to run; This
option can be used multiple times to define multiple groups
-dev configure only sample runs using devices.
To test with a parallel build, the parallel (-p|-par) option
should be set first on the command line.
-v Enable valgrind
-o <dir> [${output_dir:-"<empty>: output goes to stdout"}]
If not empty, save output to files inside <dir>
@@ -272,7 +253,7 @@ case "$1" in
-h|-help)
opt_help="yes"
;;
-p|-par)
-p|-parallel)
mfem_config="MFEM_USE_MPI=YES MFEM_DEBUG=NO"
;;
-g)
@@ -283,11 +264,6 @@ case "$1" in
groups=("${groups[@]}" "${test_group}")
shift 2
;;
-dev)
device_runs="yes"
mfem_config+=" MFEM_USE_CUDA=YES MFEM_USE_OPENMP=YES"
# OCCA, RAJA, libCEED are enabled below, if available
;;
-v)
valgrind="yes"
;;
@@ -318,10 +294,6 @@ case "$1" in
-n)
run_prefix="echo"
;;
-*)
echo "unknown option: '$1'"
exit 1
;;
*=*)
eval $1
;;
@@ -467,30 +439,6 @@ fi
TIMEFORMAT="${base_timeformat}"
# Setup optional libraries when not using externally built MFEM:
if [ "${built}" == "no" ]; then
have_occa="no"
have_raja="no"
have_ceed="no"
if [ "${device_runs}" == "yes" ]; then
if [ -n "${CUDA_ARCH}" ]; then
mfem_config+=" CUDA_ARCH=${CUDA_ARCH}"
fi
if [ -d "${mfem_dir}/../occa" ]; then
mfem_config+=" MFEM_USE_OCCA=YES"
have_occa="yes"
fi
if [ -d "${mfem_dir}/../raja" ]; then
mfem_config+=" MFEM_USE_RAJA=YES"
have_raja="yes"
fi
if [ -d "${mfem_dir}/../libCEED" ]; then
mfem_config+=" MFEM_USE_CEED=YES"
have_ceed="yes"
fi
fi
fi
function set_echo_log()
{
local dirname=`dirname "$1"`
+3 -2
View File
@@ -43,14 +43,15 @@
#define MFEM_ALIGN_SIZE(size,type) \
MFEM_ROUNDUP(size,(MFEM_SIMD_SIZE)/sizeof(type))
#ifdef MFEM_COUNT_FLOPS
namespace mfem
{
namespace internal
{
extern long long flop_count;
long long flop_count;
}
}
#ifdef MFEM_COUNT_FLOPS
#define MFEM_FLOPS_RESET() (mfem::internal::flop_count = 0)
#define MFEM_FLOPS_ADD(cnt) (mfem::internal::flop_count += (cnt))
#define MFEM_FLOPS_GET() (mfem::internal::flop_count)
-1
View File
@@ -82,7 +82,6 @@ test-par-NO: $(SEQ_$(MFEM_TESTS):=-test-seq)
test-ser: test-par-NO
test-par: test-par-YES
test: all test-par-$(MFEM_USE_MPI) clean-exec
test-noclean: all test-par-$(MFEM_USE_MPI)
test-clean: ; @rm -f *.stderr
test-print: mfem-test=printf " $(3) [$(2) ./$(1) -no-vis $(if $(4),$(4) )]\n"
test-print: mfem-test-file=printf " $(3) [$(2) ./$(1) -no-vis ]\n"
-1409
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = "MFEM"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = v4.0.1
PROJECT_NUMBER = v3.4.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
@@ -768,13 +768,11 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/examples \
@MFEM_SOURCE_DIR@/examples/petsc \
@MFEM_SOURCE_DIR@/examples/pumi \
@MFEM_SOURCE_DIR@/examples/hiop \
@MFEM_SOURCE_DIR@/examples/sundials \
@MFEM_SOURCE_DIR@/miniapps/common \
@MFEM_SOURCE_DIR@/miniapps/meshing \
@MFEM_SOURCE_DIR@/miniapps/tools \
@MFEM_SOURCE_DIR@/miniapps/nurbs \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
@MFEM_SOURCE_DIR@/miniapps/performance
+3 -20
View File
@@ -35,12 +35,6 @@ namespace mfem {
* - HypreParMatrix and HypreParVector
* - HypreSolver and other \link hypre.hpp hypre classes\endlink
*
* <H3>Main GPU classes</H3>
* - Device
* - Memory
* - MemoryManager
* - MFEM_FORALL macro in forall.hpp
*
* <H3>Example codes</H3>
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Laplace problem
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Laplace problem
@@ -79,10 +73,8 @@ namespace mfem {
* - <a class="el" href="ex19p_8cpp_source.html">Example 19p</a>: parallel incompressible nonlinear elasticity
* - <a class="el" href="ex20_8cpp_source.html">Example 20</a>: symplectic ODE integration
* - <a class="el" href="ex20p_8cpp_source.html">Example 20p</a>: parallel symplectic ODE integration
* - <a class="el" href="ex21_8cpp_source.html">Example 21</a>: adaptive mesh refinement for linear elasticity
* - <a class="el" href="ex21p_8cpp_source.html">Example 21p</a>: parallel adaptive mesh refinement for linear elasticity
* - <a class="el" href="ex22_8cpp_source.html">Example 22</a>: complex-valued linear systems for damped harmonic oscillators
* - <a class="el" href="ex22p_8cpp_source.html">Example 22p</a>: parallel complex-valued linear systems for damped harmonic oscillators
* - <a class="el" href="ex22_8cpp_source.html">Example 22</a>: adaptive mesh refinement for linear elasticity
* - <a class="el" href="ex22p_8cpp_source.html">Example 22p</a>: parallel adaptive mesh refinement for linear elasticity
*
* <H4>SUNDIALS Examples</H4>
* - Variants of Examples
@@ -117,12 +109,6 @@ namespace mfem {
* <a class="el" href="pumi_2ex6p_8cpp_source.html">6p</a>
* demonstrating the use of MFEM's \link pumi.hpp PUMI classes\endlink
*
* <H4>HiOp Examples</H4>
* - Variants of Examples
* <a class="el" href="hiop_2ex9_8cpp_source.html">9</a> and
* <a class="el" href="hiop_2ex9p_8cpp_source.html">9p</a>,
* demonstrating the use of MFEM's \link hiop.hpp HiOp classes\endlink
*
* <H3>Miniapps</H3>
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
* - <a class="el" href="tesla_8cpp_source.html">Tesla</a>: simple magnetostatics simulation code
@@ -136,12 +122,9 @@ namespace mfem {
* - <a class="el" href="mesh-explorer_8cpp_source.html">Mesh Explorer</a>: visualize and manipulate meshes
* - <a class="el" href="mesh-optimizer_8cpp_source.html">Mesh Optimizer</a>: optimize high-order meshes, <a class="el" href="mesh-optimizer_8cpp_source.html">serial</a> and <a class="el" href="pmesh-optimizer_8cpp_source.html">parallel</a> versions
* - <a class="el" href="display-basis_8cpp_source.html">Display Basis</a>: visualize finite element basis functions
* - <a class="el" href="get-values_8cpp_source.html">Get Values</a>: extract field values via DataCollection classes
* - <a class="el" href="load-dc_8cpp_source.html">Load DC</a>: visualize fields saved via DataCollection classes
* - <a class="el" href="convert-dc_8cpp_source.html">Convert DC</a>: convert between different DataCollection formats
* - <a class="el" href="convert-dc_8cpp_source.html">Convert DC</a>: convert between diffirent DataCollection formats
* - <a class="el" href="lor-transfer_8cpp_source.html">LOR Transfer</a>: map functions between high-order and low-order refined spaces
* - <a class="el" href="findpts_8cpp_source.html">Find Points</a>: evaluate grid function in physical space, <a class="el" href="findpts_8cpp_source.html">serial</a> and <a class="el" href="pfindpts_8cpp_source.html">parallel</a> versions
* - <a class="el" href="field-diff_8cpp_source.html">Field Diff</a>: compare grid functions on different meshes
* - <a class="el" href="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
*
Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

-13
View File
@@ -102,11 +102,6 @@ if (MFEM_USE_SUNDIALS)
add_subdirectory(sundials)
endif()
# Include the examples/sundials directory if SUNDIALS is enabled.
if (MFEM_USE_GINKGO)
add_subdirectory(ginkgo)
endif()
# Include the examples/petsc directory if PETSc is enabled.
if (MFEM_USE_PETSC)
add_subdirectory(petsc)
@@ -116,11 +111,3 @@ endif()
if (MFEM_USE_PUMI)
add_subdirectory(pumi)
endif()
if (MFEM_USE_HIOP)
add_subdirectory(hiop)
endif()
if (MFEM_USE_ADEPT)
add_subdirectory(ad)
endif()
+164 -245
View File
File diff suppressed because one or more lines are too long
+27 -26
View File
@@ -26,14 +26,12 @@
// ex1 -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// ex1 -pa -d cuda
// ex1 -pa -d raja-cuda
// ex1 -pa -d occa-cuda
// ex1 -pa -d raja-omp
// ex1 -pa -d occa-omp
// ex1 -pa -d ceed-cpu
// ex1 -pa -d ceed-cuda
// ex1 -m ../data/beam-hex.mesh -pa -d cuda
// > ex1 -pa -d cuda
// > ex1 -pa -d raja-cuda
// > ex1 -pa -d occa-cuda
// > ex1 -pa -d raja-omp
// > ex1 -pa -d occa-omp
// > ex1 -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
@@ -64,7 +62,7 @@ int main(int argc, char *argv[])
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
const char *device = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
@@ -77,7 +75,7 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -90,18 +88,13 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
// 2. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
device.Print();
// 3. Read the mesh from the given mesh file. We can handle triangular,
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh to increase the resolution. In this example we do
// 3. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
// largest number that gives a final mesh with no more than 50,000
// elements.
@@ -114,7 +107,7 @@ int main(int argc, char *argv[])
}
}
// 5. Define a finite element space on the mesh. Here we use continuous
// 4. Define a finite element space on the mesh. Here we use continuous
// Lagrange finite elements of the specified order. If order < 1, we
// instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
@@ -135,7 +128,7 @@ int main(int argc, char *argv[])
cout << "Number of finite element unknowns: "
<< fespace->GetTrueVSize() << endl;
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking all
// the boundary attributes from the mesh as essential (Dirichlet) and
// converting them to a list of true dofs.
@@ -147,7 +140,7 @@ int main(int argc, char *argv[])
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
// the basis functions in the finite element fespace.
LinearForm *b = new LinearForm(fespace);
@@ -155,6 +148,12 @@ int main(int argc, char *argv[])
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 7. Set device config parameters from the command line options and switch
// to working on the device.
Device::Configure(device);
Device::Print();
Device::Enable();
// 8. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
@@ -196,16 +195,18 @@ int main(int argc, char *argv[])
umf_solver.Mult(B, X);
#endif
}
else // Jacobi preconditioning in partial assembly mode
else // No preconditioning for now in partial assembly mode.
{
OperatorJacobiSmoother M(*a, ess_tdof_list);
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
CG(*A, B, X, 1, 2000, 1e-12, 0.0);
}
// 12. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
// 13. Save the refined mesh and the solution. This output can be viewed later
// 13. Switch back to the host.
Device::Disable();
// 14. 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);
@@ -214,7 +215,7 @@ int main(int argc, char *argv[])
sol_ofs.precision(8);
x.Save(sol_ofs);
// 14. Send the solution by socket to a GLVis server.
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -224,7 +225,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *mesh << x << flush;
}
// 15. Free the used memory.
// 16. Free the used memory.
delete a;
delete b;
delete fespace;
+6 -6
View File
@@ -5,11 +5,11 @@
// Sample runs:
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3877 -o 2 -sys
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 79 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3876
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 79
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3876 -o 2 -sys
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4526 -n 6 -o 3 -elast
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
// mpirun -np 4 ex12p -m ../data/beam-hex-nurbs.mesh
//
@@ -57,7 +57,7 @@ int main(int argc, char *argv[])
const char *mesh_file = "../data/beam-tri.mesh";
int order = 1;
int nev = 5;
int seed = 66;
int seed = 75;
bool visualization = 1;
bool amg_elast = 0;
+1 -1
View File
@@ -3,7 +3,7 @@
// Compile with: make ex13p
//
// Sample runs: mpirun -np 4 ex13p -m ../data/star.mesh
// mpirun -np 4 ex13p -m ../data/square-disc.mesh -o 2 -n 4
// mpirun -np 4 ex13p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex13p -m ../data/beam-tet.mesh
// mpirun -np 4 ex13p -m ../data/beam-hex.mesh
// mpirun -np 4 ex13p -m ../data/escher.mesh
+4 -1
View File
@@ -16,6 +16,9 @@
// ex15 -m ../data/ball-nurbs.mesh -tf 0.3
// ex15 -m ../data/mobius-strip.mesh
// ex15 -m ../data/amr-quad.mesh
//
// Conforming meshes (no derefinement):
//
// ex15 -m ../data/square-disc.mesh
// ex15 -m ../data/escher.mesh -r 2 -tf 0.3
//
@@ -127,7 +130,7 @@ int main(int argc, char *argv[])
if (ref_levels > 0) { ref_levels--; }
mesh.SetCurvature(2);
}
mesh.EnsureNCMesh(true);
mesh.EnsureNCMesh();
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
+4 -1
View File
@@ -16,6 +16,9 @@
// mpirun -np 4 ex15p -m ../data/ball-nurbs.mesh -tf 0.5
// mpirun -np 4 ex15p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex15p -m ../data/amr-quad.mesh
//
// Conforming meshes (no load balancing and derefinement):
//
// mpirun -np 4 ex15p -m ../data/square-disc.mesh
// mpirun -np 4 ex15p -m ../data/escher.mesh -r 2 -tf 0.3
//
@@ -143,7 +146,7 @@ int main(int argc, char *argv[])
if (ref_levels > 0) { ref_levels--; }
mesh->SetCurvature(2);
}
mesh->EnsureNCMesh(true);
mesh->EnsureNCMesh();
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
+1 -1
View File
@@ -252,7 +252,7 @@ int main(int argc, char *argv[])
}
else
{
GMRES(A, M, B, X, 3, 5000, 100, rtol*rtol, 0.0);
GMRES(A, M, B, X, 3, 5000, 50, rtol*rtol, 0.0);
}
#else
// 11. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
+1 -1
View File
@@ -144,7 +144,7 @@ void InitialDeformation(const Vector &x, Vector &y);
int main(int argc, char *argv[])
{
// 1. Parse command-line options
const char *mesh_file = "../data/beam-tet.mesh";
const char *mesh_file = "../data/beam-hex.mesh";
int ref_levels = 0;
int order = 2;
bool visualization = true;
+1 -1
View File
@@ -150,7 +150,7 @@ int main(int argc, char *argv[])
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options
const char *mesh_file = "../data/beam-tet.mesh";
const char *mesh_file = "../data/beam-hex.mesh";
int ser_ref_levels = 0;
int par_ref_levels = 0;
int order = 2;
+25 -30
View File
@@ -26,11 +26,9 @@
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// mpirun -np 4 ex1p -pa -d cuda
// mpirun -np 4 ex1p -pa -d occa-cuda
// mpirun -np 4 ex1p -pa -d raja-omp
// mpirun -np 4 ex1p -pa -d ceed-cpu
// mpirun -np 4 ex1p -pa -d ceed-cuda
// > mpirun -np 4 ex1p -pa -d cuda
// > mpirun -np 4 ex1p -pa -d occa-cuda
// > mpirun -np 4 ex1p -pa -d raja-omp
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
@@ -67,7 +65,7 @@ int main(int argc, char *argv[])
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
const char *device = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
@@ -80,7 +78,7 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -100,18 +98,13 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 4. Read the (serial) mesh from the given mesh file on all processors. We
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 5. Refine the serial mesh on all processors to increase the resolution. In
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
@@ -124,7 +117,7 @@ int main(int argc, char *argv[])
}
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
@@ -137,7 +130,7 @@ int main(int argc, char *argv[])
}
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
@@ -164,7 +157,7 @@ int main(int argc, char *argv[])
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
@@ -176,7 +169,7 @@ int main(int argc, char *argv[])
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 9. Set up the parallel linear form b(.) which corresponds to the
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm *b = new ParLinearForm(fespace);
@@ -184,6 +177,12 @@ int main(int argc, char *argv[])
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 9. Set device config parameters from the command line options and switch
// to working on the device.
Device::Configure(device);
if (myid == 0) { Device::Print(); }
Device::Enable();
// 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
@@ -210,16 +209,9 @@ int main(int argc, char *argv[])
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * With partial assembly, use Jacobi smoothing, for now.
// * With partial assembly, use no preconditioner, for now.
Solver *prec = NULL;
if (pa)
{
prec = new OperatorJacobiSmoother(*a, ess_tdof_list);
}
else
{
prec = new HypreBoomerAMG;
}
if (!pa) { prec = new HypreBoomerAMG; }
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
@@ -233,7 +225,10 @@ int main(int argc, char *argv[])
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 15. Save the refined mesh and the solution in parallel. This output can
// 15. Switch back to the host.
Device::Disable();
// 16. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
@@ -249,7 +244,7 @@ int main(int argc, char *argv[])
x.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
// 17. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -260,7 +255,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 17. Free the used memory.
// 18. Free the used memory.
delete a;
delete b;
delete fespace;
+413 -246
View File
@@ -1,34 +1,40 @@
// MFEM Example 21
// MFEM Example 21
//
// Compile with: make ex21
//
// Sample runs: ex21
// ex21 -o 3
// ex21 -m ../data/beam-quad.mesh
// ex21 -m ../data/beam-quad.mesh -o 3
// ex21 -m ../data/beam-quad.mesh -o 3 -f 1
// ex21 -m ../data/beam-tet.mesh
// ex21 -m ../data/beam-tet.mesh -o 2
// ex21 -m ../data/beam-hex.mesh
// ex21 -m ../data/beam-hex.mesh -o 2
// Sample runs: ex21 -m ../data/inline-segment.mesh -o 3
// ex21 -m ../data/inline-tri.mesh -o 3
// ex21 -m ../data/inline-quad.mesh -o 3
// ex21 -m ../data/inline-quad.mesh -o 3 -p 1
// ex21 -m ../data/inline-quad.mesh -o 3 -p 2
// ex21 -m ../data/inline-tet.mesh -o 2
// ex21 -m ../data/inline-hex.mesh -o 2
// ex21 -m ../data/inline-hex.mesh -o 2 -p 1
// ex21 -m ../data/inline-hex.mesh -o 2 -p 2
// ex21 -m ../data/star.mesh -o 2 -sigma 10.0
//
// Description: This is a version of Example 2 with a simple adaptive mesh
// refinement loop. The problem being solved is again the linear
// elasticity describing a multi-material cantilever beam.
// The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
// or non-conforming (quadrilaterals, hexahedra) manner according
// to a simple ZZ error estimator.
// Description: This example code demonstrates the use of MFEM to define and
// solve simple complex-valued linear systems. We implement three
// variants of a damped harmonic oscillator:
//
// The example demonstrates MFEM's capability to work with both
// conforming and nonconforming refinements, in 2D and 3D, on
// linear and curved meshes. Interpolation of functions from
// coarse to fine meshes, as well as persistent GLVis
// visualization are also illustrated.
// 1) A scalar H1 field
// -Div(a Grad u) - omega^2 b u + i omega c u = 0
//
// 2) A vector H(Curl) field
// Curl(a Curl u) - omega^2 b u + i omega c u = 0
//
// 3) A vector H(Div) field
// -Grad(a Div u) - omega^2 b u + i omega c u = 0
//
// In each case the field is driven by a forced oscillation, with
// angular frequency omega, imposed at the boundary or a portion
// of the boundary.
//
// In electromagnetics the coefficients are typically named the
// permeability, mu = 1/a, permittivity, epsilon = b, and
// conductivity, sigma = c. The user can specify these constants
// using either set of names.
//
// We recommend viewing Examples 2 and 6 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
@@ -36,24 +42,61 @@
using namespace std;
using namespace mfem;
static double mu_ = 1.0;
static double epsilon_ = 1.0;
static double sigma_ = 20.0;
static double omega_ = 10.0;
double u0_real_exact(const Vector &);
double u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
bool check_for_inline_mesh(const char * mesh_file);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/beam-tri.mesh";
const char *mesh_file = "../data/inline-quad.mesh";
int ref_levels = 0;
int order = 1;
bool static_cond = false;
int flux_averaging = 0;
int prob = 0;
double freq = -1.0;
double a_coef = 0.0;
bool visualization = 1;
bool herm_conv = true;
bool exact_sol = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&flux_averaging, "-f", "--flux-averaging",
"Flux averaging: 0 - global, 1 - by mesh attribute.");
args.AddOption(&prob, "-p", "--problem-type",
"Choose from 0: H_1, 1: H(Curl), or 2: H(Div) "
"damped harmonic oscillator.");
args.AddOption(&a_coef, "-a", "--stiffness-coef",
"Stiffness coefficient (spring constant or 1/mu).");
args.AddOption(&epsilon_, "-b", "--mass-coef",
"Mass coefficient (or epsilon).");
args.AddOption(&sigma_, "-c", "--damping-coef",
"Damping coefficient (or sigma).");
args.AddOption(&mu_, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon_, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma_, "-sigma", "--conductivity",
"Conductivity (or damping constant).");
args.AddOption(&freq, "-f", "--frequency",
"Frequency (in Hz).");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -65,246 +108,370 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
if ( a_coef != 0.0 )
{
mu_ = 1.0 / a_coef;
}
if ( freq > 0.0 )
{
omega_ = 2.0 * M_PI * freq;
}
exact_sol = check_for_inline_mesh(mesh_file);
if (exact_sol)
{
cout << "Identified an 'inline' mesh" << endl;
}
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, and hexahedral meshes with the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(mesh.SpaceDimension() == dim, "invalid mesh");
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes
// with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
if (mesh.attributes.Max() < 2 || mesh.bdr_attributes.Max() < 2)
// 3. Refine the mesh to increase resolution. In this example we do
// 'ref_levels' of uniform refinement where the user specifies
// the number of levels with the '-r' option.
for (int l = 0; l < ref_levels; l++)
{
cerr << "\nInput mesh should have at least two materials and "
<< "two boundary attributes! (See schematic in ex2.cpp)\n"
<< endl;
return 3;
mesh->UniformRefinement();
}
// 3. Since a NURBS mesh can currently only be refined uniformly, we need to
// convert it to a piecewise-polynomial curved mesh. First we refine the
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext)
// 4. Define a finite element space on the mesh. Here we use continuous
// Lagrange, Nedelec, or Raviart-Thomas finite elements of the specified
// order.
if (dim == 1 && prob != 0 )
{
for (int i = 0; i < 2; i++)
cout << "Switching to problem type 0, H1 basis functions, "
<< "for 1 dimensional mesh." << endl;
prob = 0;
}
FiniteElementCollection *fec;
switch (prob)
{
case 0: fec = new H1_FECollection(order, dim); break;
case 1: fec = new ND_FECollection(order, dim); break;
case 2: fec = new RT_FECollection(order - 1, dim); break;
}
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
cout << "Number of finite element unknowns: " << fespace->GetTrueVSize()
<< endl;
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined based on the type
// of mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh->bdr_attributes.Size())
{
ess_bdr.SetSize(mesh->bdr_attributes.Max());
ess_bdr = 1;
if (exact_sol)
{
mesh.UniformRefinement();
switch (prob)
{
case 0: ess_bdr = 0; ess_bdr[0] = 1; break;
default: ess_bdr = 1; ess_bdr[2] = 0; break;
}
}
mesh.SetCurvature(2);
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 4. Define a finite element space on the mesh. The polynomial order is
// one (linear) by default, but this can be changed on the command line.
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(&mesh, &fec, dim);
// 6. Set up the linear form b(.) which corresponds to the
// right-hand side of the FEM linear system.
ComplexLinearForm b(fespace, conv);
b.Vector::operator=(0.0);
// 5. As in Example 2, we set up the linear form b(.) which corresponds to
// the right-hand side of the FEM linear system. In this case, b_i equals
// the boundary integral of f*phi_i where f represents a "pull down"
// force on the Neumann part of the boundary and phi_i are the basis
// functions in the finite element fespace. The force is defined by the
// VectorArrayCoefficient object f, which is a vector of Coefficient
// objects. The fact that f is non-zero on boundary attribute 2 is
// indicated by the use of piece-wise constants coefficient for its last
// component. We don't assemble the discrete problem yet, this will be
// done in the main loop.
VectorArrayCoefficient f(dim);
for (int i = 0; i < dim-1; i++)
// 7. Define the solution vector u as a finite element grid function
// corresponding to fespace. Initialize u with initial guess of 1+0i
// or the exact solution if it is known.
ComplexGridFunction u(fespace);
ComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
switch (prob)
{
f.Set(i, new ConstantCoefficient(0.0));
case 0:
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u0_r, u0_i); }
break;
case 1:
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u1_r, u1_i); }
break;
case 2:
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u2_r, u2_i); }
break;
}
if (visualization && exact_sol)
{
Vector pull_force(mesh.bdr_attributes.Max());
pull_force = 0.0;
pull_force(1) = -1.0e-2;
f.Set(dim-1, new PWConstCoefficient(pull_force));
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u_exact->real()
<< "window_title 'Exact Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u_exact->imag()
<< "window_title 'Exact Imaginary Part'" << flush;
}
LinearForm b(&fespace);
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
// 8. Set up the sesquilinear form a(.,.) on the finite element
// space corresponding to the damped harmonic oscillator operator
// of the appropriate type:
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + i omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) - omega^2 b + i omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + i omega c
//
ConstantCoefficient stiffnessCoef(1.0/mu_);
ConstantCoefficient massCoef(-omega_ * omega_ * epsilon_);
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with piece-wise
// constants coefficient lambda and mu.
Vector lambda(mesh.attributes.Max());
lambda = 1.0;
lambda(0) = lambda(1)*50;
PWConstCoefficient lambda_func(lambda);
Vector mu(mesh.attributes.Max());
mu = 1.0;
mu(0) = mu(1)*50;
PWConstCoefficient mu_func(mu);
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
}
BilinearForm a(&fespace);
BilinearFormIntegrator *integ =
new ElasticityIntegrator(lambda_func,mu_func);
a.AddDomainIntegrator(integ);
if (static_cond) { a.EnableStaticCondensation(); }
// 9. Assemble the bilinear form and the corresponding linear
// system, applying any necessary transformations such as:
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, etc.
a->Assemble();
// 7. The solution vector x and the associated finite element grid function
// will be maintained over the AMR iterations. We initialize it to zero.
Vector zero_vec(dim);
zero_vec = 0.0;
VectorConstantCoefficient zero_vec_coeff(zero_vec);
GridFunction x(&fespace);
x = 0.0;
OperatorHandle A;
Vector B, U;
// 8. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking only
// boundary attribute 1 from the mesh as essential and converting it to a
// list of true dofs. The conversion to true dofs will be done in the
// main loop.
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1;
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
u = 0.0;
U = 0.0;
// 9. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
{
ComplexSparseMatrix * Asp =
dynamic_cast<ComplexSparseMatrix*>(A.Ptr());
cout << "Size of linear system: "
<< 2 * Asp->real().Width() << endl << endl;
}
// 10. Define and apply a GMRES solver for AU=B.
{
GMRESSolver gmres;
gmres.SetOperator(*A.Ptr());
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(1000);
gmres.SetPrintLevel(1);
gmres.Mult(B, U);
}
// 11. Recover the solution as a finite element grid function and
// compute the errors if the exact solution is known.
a->RecoverFEMSolution(U, b, u);
if (exact_sol)
{
double err_r = -1.0;
double err_i = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
}
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
// 12. Save the refined mesh and the solution. This output can be
// viewed later using GLVis: "glvis -m mesh -g sol".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_r_ofs("sol_r.gf");
ofstream sol_i_ofs("sol_i.gf");
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
}
// 13. Send the solution by socket to a GLVis server.
if (visualization)
{
sol_sock.open(vishost, visport);
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u.real()
<< "window_title 'Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u.imag()
<< "window_title 'Comp Imaginary Part'" << flush;
}
if (visualization && exact_sol)
{
*u_exact -= u;
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u_exact->real()
<< "window_title 'Exact-Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u_exact->imag()
<< "window_title 'Exact-Comp Imaginary Part'" << flush;
}
if (visualization)
{
GridFunction u_t(fespace);
u_t = u.real();
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << u_t
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
int num_frames = 32;
int i = 0;
while (sol_sock)
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
add(cos( 2.0 * M_PI * t), u.real(),
sin(-2.0 * M_PI * t), u.imag(), u_t);
sol_sock << "solution\n" << *mesh << u_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
}
}
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the ElasticityIntegrator to
// recover a smoothed flux (stress) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^tdim (i.e., vector-valued) space is used here.
// Here, tdim represents the number of components for a symmetric (dim x
// dim) tensor.
const int tdim = dim*(dim+1)/2;
FiniteElementSpace flux_fespace(&mesh, &fec, tdim);
ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace);
estimator.SetFluxAveraging(flux_averaging);
// 11. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 12. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
const int max_dofs = 50000;
const int max_amr_itr = 20;
for (int it = 0; it <= max_amr_itr; it++)
{
int cdofs = fespace.GetTrueVSize();
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << cdofs << endl;
// 13. Assemble the stiffness matrix and the right-hand side.
a.Assemble();
b.Assemble();
// 14. Set Dirichlet boundary values in the GridFunction x.
// Determine the list of Dirichlet true DOFs in the linear system.
Array<int> ess_tdof_list;
x.ProjectBdrCoefficient(zero_vec_coeff, ess_bdr);
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 15. Create the linear system: eliminate boundary conditions, constrain
// hanging nodes and possibly apply other transformations. The system
// will be solved for true (unconstrained) DOFs only.
SparseMatrix A;
Vector B, X;
const int copy_interior = 1;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
#ifndef MFEM_USE_SUITESPARSE
// 16. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the linear system with PCG.
GSSmoother M(A);
PCG(A, M, B, X, 3, 2000, 1e-12, 0.0);
#else
// 16. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the
// the linear system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(B, X);
#endif
// 17. After solving the linear system, reconstruct the solution as a
// finite element GridFunction. Constrained nodes are interpolated
// from true DOFs (it may therefore happen that x.Size() >= X.Size()).
a.RecoverFEMSolution(X, b, x);
// 18. Send solution by socket to the GLVis server.
if (visualization && sol_sock.good())
{
GridFunction nodes(&fespace), *nodes_p = &nodes;
mesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
mesh.SwapNodes(nodes_p, own_nodes);
x.Neg(); // visualize the backward displacement
sol_sock << "solution\n" << mesh << x << flush;
x.Neg();
mesh.SwapNodes(nodes_p, own_nodes);
if (it == 0)
{
sol_sock << "keys '" << ((dim == 2) ? "Rjl" : "") << "m'" << endl;
}
sol_sock << "window_title 'AMR iteration: " << it << "'\n"
<< "pause" << endl;
cout << "Visualization paused. "
"Press <space> in the GLVis window to continue." << endl;
}
if (cdofs > max_dofs)
{
cout << "Reached the maximum number of dofs. Stop." << endl;
break;
}
// 19. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
refiner.Apply(mesh);
if (refiner.Stop())
{
cout << "Stopping criterion satisfied. Stop." << endl;
break;
}
// 20. Update the space to reflect the new state of the mesh. Also,
// interpolate the solution x so that it lies in the new space but
// represents the same function. This saves solver iterations later
// since we'll have a good initial guess of x in the next step.
// Internally, FiniteElementSpace::Update() calculates an
// interpolation matrix which is then used by GridFunction::Update().
fespace.Update();
x.Update();
// 21. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
}
{
ofstream mesh_ref_out("ex21_reference.mesh");
mesh_ref_out.precision(16);
mesh.Print(mesh_ref_out);
ofstream mesh_out("ex21_deformed.mesh");
mesh_out.precision(16);
GridFunction nodes(&fespace), *nodes_p = &nodes;
mesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
mesh.SwapNodes(nodes_p, own_nodes);
mesh.Print(mesh_out);
mesh.SwapNodes(nodes_p, own_nodes);
ofstream x_out("ex21_displacement.sol");
x_out.precision(16);
x.Save(x_out);
}
// 14. Free the used memory.
delete a;
delete u_exact;
delete fespace;
delete fec;
delete mesh;
return 0;
}
bool check_for_inline_mesh(const char * mesh_file)
{
string file(mesh_file);
size_t p0 = file.find_last_of("/");
string s0 = file.substr((p0==string::npos)?0:(p0+1),7);
return s0 == "inline-";
}
complex<double> u0_exact(const Vector &x)
{
int dim = x.Size();
complex<double> i(0.0, 1.0);
complex<double> alpha = (epsilon_ * omega_ - i * sigma_);
complex<double> kappa = std::sqrt(mu_ * omega_* alpha);
return std::exp(-i * kappa * x[dim - 1]);
}
double u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
double u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
}
+594 -302
View File
@@ -1,65 +1,122 @@
// MFEM Example 21
// MFEM Example 21 - Parallel Version
//
// Compile with: make ex21p
//
// Sample runs: mpirun -np 4 ex21p
// mpirun -np 4 ex21p -o 3
// mpirun -np 4 ex21p -m ../data/beam-quad.mesh
// mpirun -np 4 ex21p -m ../data/beam-quad.mesh -o 3
// mpirun -np 4 ex21p -m ../data/beam-tet.mesh
// mpirun -np 4 ex21p -m ../data/beam-tet.mesh -o 2
// mpirun -np 4 ex21p -m ../data/beam-hex.mesh
// mpirun -np 4 ex21p -m ../data/beam-hex.mesh -o 2
// Sample runs: mpirun -np 4 ex21p -m ../data/inline-segment.mesh -o 3
// mpirun -np 4 ex21p -m ../data/inline-tri.mesh -o 3
// mpirun -np 4 ex21p -m ../data/inline-quad.mesh -o 3
// mpirun -np 4 ex21p -m ../data/inline-quad.mesh -o 3 -p 1
// mpirun -np 4 ex21p -m ../data/inline-quad.mesh -o 3 -p 2
// mpirun -np 4 ex21p -m ../data/inline-tet.mesh -o 2
// mpirun -np 4 ex21p -m ../data/inline-hex.mesh -o 2
// mpirun -np 4 ex21p -m ../data/inline-hex.mesh -o 2 -p 1
// mpirun -np 4 ex21p -m ../data/inline-hex.mesh -o 2 -p 2
// mpirun -np 4 ex21p -m ../data/star.mesh -o 2 -sigma 10.0
//
// Description: This is a version of Example 2p with a simple adaptive mesh
// refinement loop. The problem being solved is again the linear
// elasticity describing a multi-material cantilever beam.
// The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
// or non-conforming (quadrilaterals, hexahedra) manner according
// to a simple ZZ error estimator.
// Description: This example code demonstrates the use of MFEM to define and
// solve simple complex-valued linear systems. We implement three
// variants of a damped harmonic oscillator:
//
// The example demonstrates MFEM's capability to work with both
// conforming and nonconforming refinements, in 2D and 3D, on
// linear and curved meshes. Interpolation of functions from
// coarse to fine meshes, as well as persistent GLVis
// visualization are also illustrated.
// 1) A scalar H1 field
// -Div(a Grad u) - omega^2 b u + i omega c u = 0
//
// We recommend viewing Examples 2p and 6p before viewing this
// example.
// 2) A vector H(Curl) field
// Curl(a Curl u) - omega^2 b u + i omega c u = 0
//
// 3) A vector H(Div) field
// -Grad(a Div u) - omega^2 b u + i omega c u = 0
//
// In each case the field is driven by a forced oscillation, with
// angular frequency omega, imposed at the boundary or a portion
// of the boundary.
//
// In electromagnetics the coefficients are typically named the
// permeability, mu = 1/a, permittivity, epsilon = b, and
// conductivity, sigma = c. The user can specify these constants
// using either set of names.
//
//#define MFEM_STRUMPACK_SRC
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "mfem.hpp"
using namespace std;
using namespace mfem;
static double mu_ = 1.0;
static double epsilon_ = 1.0;
static double sigma_ = 20.0;
static double omega_ = 10.0;
double u0_real_exact(const Vector &);
double u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
bool check_for_inline_mesh(const char * mesh_file);
int main(int argc, char *argv[])
{
// 0. Initialize MPI.
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm comm = MPI_COMM_WORLD;
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
// 1. Parse command-line options.
const char *mesh_file = "../data/beam-tri.mesh";
int serial_ref_levels = 0;
// 2. Parse command-line options.
const char *mesh_file = "../data/inline-quad.mesh";
int ser_ref_levels = 1;
int par_ref_levels = 1;
int order = 1;
bool static_cond = false;
int prob = 0;
double freq = -1.0;
double a_coef = 0.0;
bool visualization = 1;
bool herm_conv = true;
bool exact_sol = true;
#ifdef MFEM_USE_STRUMPACK
bool strumpack = false;
#endif
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&serial_ref_levels, "-rs", "--refine-serial",
"Number of uniform serial refinements (before parallel"
" partitioning)");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&prob, "-p", "--problem-type",
"Choose from 0: H_1, 1: H(Curl), or 2: H(Div) "
"damped harmonic oscillator.");
args.AddOption(&a_coef, "-a", "--stiffness-coef",
"Stiffness coefficient (spring constant or 1/mu).");
args.AddOption(&epsilon_, "-b", "--mass-coef",
"Mass coefficient (or epsilon).");
args.AddOption(&sigma_, "-c", "--damping-coef",
"Damping coefficient (or sigma).");
args.AddOption(&mu_, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon_, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma_, "-sigma", "--conductivity",
"Conductivity (or damping constant).");
args.AddOption(&freq, "-f", "--frequency",
"Frequency (in Hz).");
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&strumpack, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use STRUMPACK's double complex linear solver.");
#endif
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -78,289 +135,524 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, and hexahedral meshes with the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(mesh.SpaceDimension() == dim, "invalid mesh");
if (mesh.attributes.Max() < 2 || mesh.bdr_attributes.Max() < 2)
if ( a_coef != 0.0 )
{
cerr << "\nInput mesh should have at least two materials and "
<< "two boundary attributes! (See schematic in ex2.cpp)\n"
<< endl;
MPI_Finalize();
return 3;
mu_ = 1.0 / a_coef;
}
if ( freq > 0.0 )
{
omega_ = 2.0 * M_PI * freq;
}
// 3. Refine the mesh before parallel partitioning. Since a NURBS mesh can
// currently only be refined uniformly, we need to convert it to a
// piecewise-polynomial curved mesh. First we refine the NURBS mesh a bit
// more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext && serial_ref_levels == 0)
exact_sol = check_for_inline_mesh(mesh_file);
if (myid == 0 && exact_sol)
{
serial_ref_levels = 2;
}
for (int i = 0; i < serial_ref_levels; i++)
{
mesh.UniformRefinement();
}
if (mesh.NURBSext)
{
mesh.SetCurvature(2);
}
mesh.EnsureNCMesh();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// 4. Define a finite element space on the mesh. The polynomial order is
// one (linear) by default, but this can be changed on the command line.
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec, dim);
// 5. As in Example 2, we set up the linear form b(.) which corresponds to
// the right-hand side of the FEM linear system. In this case, b_i equals
// the boundary integral of f*phi_i where f represents a "pull down"
// force on the Neumann part of the boundary and phi_i are the basis
// functions in the finite element fespace. The force is defined by the
// VectorArrayCoefficient object f, which is a vector of Coefficient
// objects. The fact that f is non-zero on boundary attribute 2 is
// indicated by the use of piece-wise constants coefficient for its last
// component. We don't assemble the discrete problem yet, this will be
// done in the main loop.
VectorArrayCoefficient f(dim);
for (int i = 0; i < dim-1; i++)
{
f.Set(i, new ConstantCoefficient(0.0));
}
{
Vector pull_force(pmesh.bdr_attributes.Max());
pull_force = 0.0;
pull_force(1) = -1.0e-2;
f.Set(dim-1, new PWConstCoefficient(pull_force));
cout << "Identified an 'inline' mesh" << endl;
}
ParLinearForm b(&fespace);
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with piece-wise
// constants coefficient lambda and mu.
Vector lambda(pmesh.attributes.Max());
lambda = 1.0;
lambda(0) = lambda(1)*50;
PWConstCoefficient lambda_func(lambda);
Vector mu(pmesh.attributes.Max());
mu = 1.0;
mu(0) = mu(1)*50;
PWConstCoefficient mu_func(mu);
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
ParBilinearForm a(&fespace);
BilinearFormIntegrator *integ =
new ElasticityIntegrator(lambda_func,mu_func);
a.AddDomainIntegrator(integ);
if (static_cond) { a.EnableStaticCondensation(); }
// 7. The solution vector x and the associated finite element grid function
// will be maintained over the AMR iterations. We initialize it to zero.
Vector zero_vec(dim);
zero_vec = 0.0;
VectorConstantCoefficient zero_vec_coeff(zero_vec);
ParGridFunction x(&fespace);
x = 0.0;
// 8. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking only
// boundary attribute 1 from the mesh as essential and converting it to a
// list of true dofs. The conversion to true dofs will be done in the
// main loop.
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1;
// 9. GLVis visualization.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the ElasticityIntegrator to
// recover a smoothed flux (stress) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^tdim (i.e., vector-valued) space is used here.
// Here, tdim represents the number of components for a symmetric (dim x
// dim) tensor.
const int tdim = dim*(dim+1)/2;
L2_FECollection flux_fec(order, dim);
ParFiniteElementSpace flux_fespace(&pmesh, &flux_fec, tdim);
ParFiniteElementSpace smooth_flux_fespace(&pmesh, &fec, tdim);
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace,
smooth_flux_fespace);
// 11. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 12. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
const int max_dofs = 50000;
const int max_amr_itr = 20;
for (int it = 0; it <= max_amr_itr; it++)
// 4. Refine the serial mesh on all processors to increase the resolution.
for (int l = 0; l < ser_ref_levels; l++)
{
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
// 6. Define a parallel finite element space on the parallel
// mesh. Here we use continuous Lagrange, Nedelec, or
// Raviart-Thomas finite elements of the specified order.
if (dim == 1 && prob != 0 )
{
HYPRE_Int global_dofs = fespace.GlobalTrueVSize();
if (myid == 0)
{
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << global_dofs << endl;
cout << "Switching to problem type 0, H1 basis functions, "
<< "for 1 dimensional mesh." << endl;
}
// 13. Assemble the stiffness matrix and the right-hand side.
a.Assemble();
b.Assemble();
// 14. Set Dirichlet boundary values in the GridFunction x.
// Determine the list of Dirichlet true DOFs in the linear system.
Array<int> ess_tdof_list;
x.ProjectBdrCoefficient(zero_vec_coeff, ess_bdr);
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 15. Create the linear system: eliminate boundary conditions, constrain
// hanging nodes and possibly apply other transformations. The system
// will be solved for true (unconstrained) DOFs only.
HypreParMatrix A;
Vector B, X;
const int copy_interior = 1;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
// 16. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
// preconditioner from hypre.
HypreBoomerAMG amg;
amg.SetPrintLevel(0);
// amg.SetSystemsOptions(dim); // optional
CGSolver pcg(A.GetComm());
pcg.SetPreconditioner(amg);
pcg.SetOperator(A);
pcg.SetRelTol(1e-6);
pcg.SetMaxIter(500);
pcg.SetPrintLevel(3); // print the first and the last iterations only
pcg.Mult(B, X);
// 17. After solving the linear system, reconstruct the solution as a
// finite element GridFunction. Constrained nodes are interpolated
// from true DOFs (it may therefore happen that x.Size() >= X.Size()).
a.RecoverFEMSolution(X, b, x);
// 18. Send solution by socket to the GLVis server.
if (visualization && it == 0)
{
sol_sock.open(vishost, visport);
sol_sock.precision(8);
}
if (visualization && sol_sock.good())
{
GridFunction nodes(&fespace), *nodes_p = &nodes;
pmesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
pmesh.SwapNodes(nodes_p, own_nodes);
x.Neg(); // visualize the backward displacement
sol_sock << "parallel " << num_procs << ' ' << myid << '\n';
sol_sock << "solution\n" << pmesh << x << flush;
x.Neg();
pmesh.SwapNodes(nodes_p, own_nodes);
if (it == 0)
{
sol_sock << "keys '" << ((dim == 2) ? "Rjl" : "") << "m'" << endl;
}
sol_sock << "window_title 'AMR iteration: " << it << "'\n"
<< "pause" << endl;
if (myid == 0)
{
cout << "Visualization paused. "
"Press <space> in the GLVis window to continue." << endl;
}
}
if (global_dofs > max_dofs)
{
if (myid == 0)
{
cout << "Reached the maximum number of dofs. Stop." << endl;
}
break;
}
// 19. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
refiner.Apply(pmesh);
if (refiner.Stop())
{
if (myid == 0)
{
cout << "Stopping criterion satisfied. Stop." << endl;
}
break;
}
// 20. Update the space to reflect the new state of the mesh. Also,
// interpolate the solution x so that it lies in the new space but
// represents the same function. This saves solver iterations later
// since we'll have a good initial guess of x in the next step.
// Internally, FiniteElementSpace::Update() calculates an
// interpolation matrix which is then used by GridFunction::Update().
fespace.Update();
x.Update();
// 21. Load balance the mesh, and update the space and solution. Currently
// available only for nonconforming meshes.
if (pmesh.Nonconforming())
{
pmesh.Rebalance();
// Update the space and the GridFunction. This time the update matrix
// redistributes the GridFunction among the processors.
fespace.Update();
x.Update();
}
// 21. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
prob = 0;
}
FiniteElementCollection *fec;
switch (prob)
{
ostringstream mref_name, mesh_name, sol_name;
mref_name << "ex21p_reference_mesh." << setfill('0') << setw(6) << myid;
mesh_name << "ex21p_deformed_mesh." << setfill('0') << setw(6) << myid;
sol_name << "ex21p_displacement." << setfill('0') << setw(6) << myid;
ofstream mesh_ref_out(mref_name.str().c_str());
mesh_ref_out.precision(16);
pmesh.Print(mesh_ref_out);
ofstream mesh_out(mesh_name.str().c_str());
mesh_out.precision(16);
GridFunction nodes(&fespace), *nodes_p = &nodes;
pmesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
pmesh.SwapNodes(nodes_p, own_nodes);
pmesh.Print(mesh_out);
pmesh.SwapNodes(nodes_p, own_nodes);
ofstream x_out(sol_name.str().c_str());
x_out.precision(16);
x.Save(x_out);
case 0: fec = new H1_FECollection(order, dim); break;
case 1: fec = new ND_FECollection(order, dim); break;
case 2: fec = new RT_FECollection(order - 1, dim); break;
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// based on the type of mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
if (exact_sol)
{
switch (prob)
{
case 0: ess_bdr = 0; ess_bdr[0] = 1; break;
default: ess_bdr = 1; ess_bdr[2] = 0; break;
}
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system.
ParComplexLinearForm b(fespace, conv);
b.Vector::operator=(0.0);
// 9. Define the solution vector u as a parallel finite element
// grid function corresponding to fespace. Initialize u with
// initial guess of 1+0i or the exact solution if it is known.
ParComplexGridFunction u(fespace);
ParComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
switch (prob)
{
case 0:
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u0_r, u0_i); }
break;
case 1:
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u1_r, u1_i); }
break;
case 2:
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u2_r, u2_i); }
break;
}
if (visualization && exact_sol)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact Imaginary Part'" << flush;
}
// 10. Set up the parallel sesquilinear form a(.,.) on the finite element
// space corresponding to the damped harmonic oscillator operator
// of the appropriate type:
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + i omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) - omega^2 b + i omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + i omega c
//
ConstantCoefficient stiffnessCoef(1.0/mu_);
ConstantCoefficient massCoef(-omega_ * omega_ * epsilon_);
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
}
// 10a. Set up the parallel bilinear form for the preconditioner
// corresponding to the appropriate operator if the STRUMPACK solver
// has not been selected.
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) + omega^2 b + omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + omega c
//
ParBilinearForm *pcOp = NULL;
#ifdef MFEM_USE_STRUMPACK
if (!strumpack)
#endif
{
pcOp = new ParBilinearForm(fespace);
switch (prob)
{
case 0:
pcOp->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(lossCoef));
break;
case 1:
pcOp->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(negMassCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
case 2:
pcOp->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
}
}
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, etc.
a->Assemble();
if (pcOp) { pcOp->Assemble(); }
OperatorHandle A;
Vector B, U;
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
u = 0.0;
U = 0.0;
OperatorHandle PCOp;
if (pcOp) { pcOp->FormSystemMatrix(ess_tdof_list, PCOp); }
if (myid == 0)
{
ComplexHypreParMatrix * Ahyp =
dynamic_cast<ComplexHypreParMatrix*>(A.Ptr());
cout << "Size of linear system: "
<< 2 * Ahyp->real().GetGlobalNumRows() << endl << endl;
}
// 12. Define and apply a parallel FGMRES solver for AU=B with a
// block diagonal preconditioner based on the appropriate multigrid
// preconditioner from hypre or simply use STRUMPACK.
#ifdef MFEM_USE_STRUMPACK
if (!strumpack)
#endif
{
Array<HYPRE_Int> blockTrueOffsets;
blockTrueOffsets.SetSize(3);
blockTrueOffsets[0] = 0;
blockTrueOffsets[1] = PCOp.Ptr()->Height();
blockTrueOffsets[2] = PCOp.Ptr()->Height();
blockTrueOffsets.PartialSum();
BlockDiagonalPreconditioner BDP(blockTrueOffsets);
Operator * pc_r = NULL;
Operator * pc_i = NULL;
switch (prob)
{
case 0:
pc_r =
new HypreBoomerAMG(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()));
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
break;
case 1:
pc_r = new HypreAMS(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()),
fespace);
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
break;
case 2:
if (dim == 2 )
{
pc_r = new HypreAMS(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()),
fespace);
}
else
{
pc_r = new HypreADS(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()),
fespace);
}
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
break;
}
BDP.SetDiagonalBlock(0, pc_r);
BDP.SetDiagonalBlock(1, pc_i);
BDP.owns_blocks = 0;
FGMRESSolver fgmres(MPI_COMM_WORLD);
fgmres.SetPreconditioner(BDP);
fgmres.SetOperator(*A.Ptr());
fgmres.SetRelTol(1e-12);
fgmres.SetMaxIter(1000);
fgmres.SetPrintLevel(1);
fgmres.Mult(B, U);
}
#ifdef MFEM_USE_STRUMPACK
else
{
ComplexHypreParMatrix * Ahyp =
dynamic_cast<ComplexHypreParMatrix*>(A.Ptr());
STRUMPACKRowLocCmplxMatrix A_strmp(Ahyp->real(), Ahyp->imag());
STRUMPACKCmplxSolver strmp(argc, argv, comm);
strmp.SetPrintFactorStatistics(true);
strmp.SetPrintSolveStatistics(true);
// strmp.SetKrylovSolver(strumpack::KrylovSolver::AUTO); // core dump
strmp.SetKrylovSolver(strumpack::KrylovSolver::DIRECT); // core dump
// strmp.SetKrylovSolver(strumpack::KrylovSolver::REFINE); // core dump
// strmp.SetKrylovSolver(strumpack::KrylovSolver::PREC_GMRES); // index out of range asserts from strumpack::DenseMatrix
// strmp.SetKrylovSolver(strumpack::KrylovSolver::GMRES); // WORKS
strmp.SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strmp.SetOperator(A_strmp);
strmp.SetFromCommandLine();
strmp.Mult(B, U);
}
#endif
// 13. Recover the parallel grid function corresponding to U. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(U, b, u);
if (exact_sol)
{
double err_r = -1.0;
double err_i = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
}
if ( myid == 0 )
{
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
}
// 14. Save the refined mesh and the solution in parallel. This output can be
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_r_name, sol_i_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_r_name << "sol_r." << setfill('0') << setw(6) << myid;
sol_i_name << "sol_i." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_r_ofs(sol_r_name.str().c_str());
ofstream sol_i_ofs(sol_i_name.str().c_str());
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
}
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u.real()
<< "window_title 'Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u.imag()
<< "window_title 'Comp Imaginary Part'" << flush;
}
if (visualization && exact_sol)
{
*u_exact -= u;
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact-Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact-Comp Imaginary Part'" << flush;
}
if (visualization)
{
ParGridFunction u_t(fespace);
u_t = u.real();
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << u_t
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
if (myid == 0)
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
int num_frames = 32;
int i = 0;
while (sol_sock)
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
add(cos( 2.0 * M_PI * t), u.real(),
sin(-2.0 * M_PI * t), u.imag(), u_t);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock << "solution\n" << *pmesh << u_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
}
}
// 16. Free the used memory.
delete a;
delete u_exact;
delete pcOp;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
bool check_for_inline_mesh(const char * mesh_file)
{
string file(mesh_file);
size_t p0 = file.find_last_of("/");
string s0 = file.substr((p0==string::npos)?0:(p0+1),7);
return s0 == "inline-";
}
complex<double> u0_exact(const Vector &x)
{
int dim = x.Size();
complex<double> i(0.0, 1.0);
complex<double> alpha = (epsilon_ * omega_ - i * sigma_);
complex<double> kappa = std::sqrt(mu_ * omega_* alpha);
return std::exp(-i * kappa * x[dim - 1]);
}
double u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
double u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
}
+244 -495
View File
@@ -1,44 +1,32 @@
// MFEM Example 22
// MFEM Example 22
//
// Compile with: make ex22
//
// Sample runs: ex22 -m ../data/inline-segment.mesh -o 3
// ex22 -m ../data/inline-tri.mesh -o 3
// ex22 -m ../data/inline-quad.mesh -o 3
// ex22 -m ../data/inline-quad.mesh -o 3 -p 1
// ex22 -m ../data/inline-quad.mesh -o 3 -p 2
// ex22 -m ../data/inline-tet.mesh -o 2
// ex22 -m ../data/inline-hex.mesh -o 2
// ex22 -m ../data/inline-hex.mesh -o 2 -p 1
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2
// ex22 -m ../data/star.mesh -r 1 -o 2 -sigma 10.0
// Sample runs: ex22
// ex22 -o 3
// ex22 -m ../data/beam-quad.mesh
// ex22 -m ../data/beam-quad.mesh -o 3
// ex22 -m ../data/beam-quad.mesh -o 3 -f 1
// ex22 -m ../data/beam-tet.mesh
// ex22 -m ../data/beam-tet.mesh -o 2
// ex22 -m ../data/beam-hex.mesh
// ex22 -m ../data/beam-hex.mesh -o 2
//
// Description: This example code demonstrates the use of MFEM to define and
// solve simple complex-valued linear systems. It implements three
// variants of a damped harmonic oscillator:
// Description: This is a version of Example 2 with a simple adaptive mesh
// refinement loop. The problem being solved is again the linear
// elasticity describing a multi-material cantilever beam.
// The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
// or non-conforming (quadrilaterals, hexahedra) manner according
// to a simple ZZ error estimator.
//
// 1) A scalar H1 field
// -Div(a Grad u) - omega^2 b u + i omega c u = 0
// The example demonstrates MFEM's capability to work with both
// conforming and nonconforming refinements, in 2D and 3D, on
// linear and curved meshes. Interpolation of functions from
// coarse to fine meshes, as well as persistent GLVis
// visualization are also illustrated.
//
// 2) A vector H(Curl) field
// Curl(a Curl u) - omega^2 b u + i omega c u = 0
//
// 3) A vector H(Div) field
// -Grad(a Div u) - omega^2 b u + i omega c u = 0
//
// In each case the field is driven by a forced oscillation, with
// angular frequency omega, imposed at the boundary or a portion
// of the boundary.
//
// In electromagnetics, the coefficients are typically named the
// permeability, mu = 1/a, permittivity, epsilon = b, and
// conductivity, sigma = c. The user can specify these constants
// using either set of names.
//
// The example also demonstrates how to display a time-varying
// solution as a sequence of fields sent to a single GLVis socket.
//
// We recommend viewing examples 1, 3 and 4 before viewing this
// We recommend viewing Examples 2 and 6 before viewing this
// example.
#include "mfem.hpp"
@@ -48,61 +36,24 @@
using namespace std;
using namespace mfem;
static double mu_ = 1.0;
static double epsilon_ = 1.0;
static double sigma_ = 20.0;
static double omega_ = 10.0;
double u0_real_exact(const Vector &);
double u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
bool check_for_inline_mesh(const char * mesh_file);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/inline-quad.mesh";
int ref_levels = 0;
const char *mesh_file = "../data/beam-tri.mesh";
int order = 1;
int prob = 0;
double freq = -1.0;
double a_coef = 0.0;
bool static_cond = false;
int flux_averaging = 0;
bool visualization = 1;
bool herm_conv = true;
bool exact_sol = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&prob, "-p", "--problem-type",
"Choose between 0: H_1, 1: H(Curl), or 2: H(Div) "
"damped harmonic oscillator.");
args.AddOption(&a_coef, "-a", "--stiffness-coef",
"Stiffness coefficient (spring constant or 1/mu).");
args.AddOption(&epsilon_, "-b", "--mass-coef",
"Mass coefficient (or epsilon).");
args.AddOption(&sigma_, "-c", "--damping-coef",
"Damping coefficient (or sigma).");
args.AddOption(&mu_, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon_, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma_, "-sigma", "--conductivity",
"Conductivity (or damping constant).");
args.AddOption(&freq, "-f", "--frequency",
"Frequency (in Hz).");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&flux_averaging, "-f", "--flux-averaging",
"Flux averaging: 0 - global, 1 - by mesh attribute.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -114,448 +65,246 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
MFEM_VERIFY(prob >= 0 && prob <=2,
"Unrecognized problem type: " << prob);
if ( a_coef != 0.0 )
{
mu_ = 1.0 / a_coef;
}
if ( freq > 0.0 )
{
omega_ = 2.0 * M_PI * freq;
}
exact_sol = check_for_inline_mesh(mesh_file);
if (exact_sol)
{
cout << "Identified a mesh with known exact solution" << endl;
}
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes
// with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// quadrilateral, tetrahedral, and hexahedral meshes with the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(mesh.SpaceDimension() == dim, "invalid mesh");
// 3. Refine the mesh to increase resolution. In this example we do
// 'ref_levels' of uniform refinement where the user specifies
// the number of levels with the '-r' option.
for (int l = 0; l < ref_levels; l++)
if (mesh.attributes.Max() < 2 || mesh.bdr_attributes.Max() < 2)
{
mesh->UniformRefinement();
cerr << "\nInput mesh should have at least two materials and "
<< "two boundary attributes! (See schematic in ex2.cpp)\n"
<< endl;
return 3;
}
// 4. Define a finite element space on the mesh. Here we use continuous
// Lagrange, Nedelec, or Raviart-Thomas finite elements of the specified
// order.
if (dim == 1 && prob != 0 )
// 3. Since a NURBS mesh can currently only be refined uniformly, we need to
// convert it to a piecewise-polynomial curved mesh. First we refine the
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext)
{
cout << "Switching to problem type 0, H1 basis functions, "
<< "for 1 dimensional mesh." << endl;
prob = 0;
}
FiniteElementCollection *fec = NULL;
switch (prob)
{
case 0: fec = new H1_FECollection(order, dim); break;
case 1: fec = new ND_FECollection(order, dim); break;
case 2: fec = new RT_FECollection(order - 1, dim); break;
default: break; // This should be unreachable
}
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
cout << "Number of finite element unknowns: " << fespace->GetTrueVSize()
<< endl;
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined based on the type
// of mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh->bdr_attributes.Size())
{
ess_bdr.SetSize(mesh->bdr_attributes.Max());
ess_bdr = 1;
if (exact_sol)
for (int i = 0; i < 2; i++)
{
switch (prob)
{
case 0: ess_bdr = 0; ess_bdr[0] = 1; break;
default: ess_bdr = 1; ess_bdr[2] = 0; break;
}
mesh.UniformRefinement();
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
mesh.SetCurvature(2);
}
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system.
ComplexLinearForm b(fespace, conv);
b.Vector::operator=(0.0);
// 4. Define a finite element space on the mesh. The polynomial order is
// one (linear) by default, but this can be changed on the command line.
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(&mesh, &fec, dim);
// 7. Define the solution vector u as a complex finite element grid function
// corresponding to fespace. Initialize u with initial guess of 1+0i or
// the exact solution if it is known.
ComplexGridFunction u(fespace);
ComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
u = 0.0;
switch (prob)
// 5. As in Example 2, we set up the linear form b(.) which corresponds to
// the right-hand side of the FEM linear system. In this case, b_i equals
// the boundary integral of f*phi_i where f represents a "pull down"
// force on the Neumann part of the boundary and phi_i are the basis
// functions in the finite element fespace. The force is defined by the
// VectorArrayCoefficient object f, which is a vector of Coefficient
// objects. The fact that f is non-zero on boundary attribute 2 is
// indicated by the use of piece-wise constants coefficient for its last
// component. We don't assemble the discrete problem yet, this will be
// done in the main loop.
VectorArrayCoefficient f(dim);
for (int i = 0; i < dim-1; i++)
{
case 0:
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u0_r, u0_i); }
break;
case 1:
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u1_r, u1_i); }
break;
case 2:
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u2_r, u2_i); }
break;
default: break; // This should be unreachable
f.Set(i, new ConstantCoefficient(0.0));
}
if (visualization && exact_sol)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u_exact->real()
<< "window_title 'Exact: Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u_exact->imag()
<< "window_title 'Exact: Imaginary Part'" << flush;
Vector pull_force(mesh.bdr_attributes.Max());
pull_force = 0.0;
pull_force(1) = -1.0e-2;
f.Set(dim-1, new PWConstCoefficient(pull_force));
}
// 8. Set up the sesquilinear form a(.,.) on the finite element space
// corresponding to the damped harmonic oscillator operator of the
// appropriate type:
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + i omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) - omega^2 b + i omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + i omega c
//
ConstantCoefficient stiffnessCoef(1.0/mu_);
ConstantCoefficient massCoef(-omega_ * omega_ * epsilon_);
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
LinearForm b(&fespace);
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
default: break; // This should be unreachable
}
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with piece-wise
// constants coefficient lambda and mu.
Vector lambda(mesh.attributes.Max());
lambda = 1.0;
lambda(0) = lambda(1)*50;
PWConstCoefficient lambda_func(lambda);
Vector mu(mesh.attributes.Max());
mu = 1.0;
mu(0) = mu(1)*50;
PWConstCoefficient mu_func(mu);
// 8a. Set up the bilinear form for the preconditioner corresponding to the
// appropriate operator
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) + omega^2 b + omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + omega c
//
BilinearForm *pcOp = new BilinearForm(fespace);
switch (prob)
{
case 0:
pcOp->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(lossCoef));
break;
case 1:
pcOp->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(negMassCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
case 2:
pcOp->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
default: break; // This should be unreachable
}
BilinearForm a(&fespace);
BilinearFormIntegrator *integ =
new ElasticityIntegrator(lambda_func,mu_func);
a.AddDomainIntegrator(integ);
if (static_cond) { a.EnableStaticCondensation(); }
// 9. Assemble the form and the corresponding linear system, applying any
// necessary transformations such as: assembly, eliminating boundary
// conditions, conforming constraints for non-conforming AMR, etc.
a->Assemble();
pcOp->Assemble();
// 7. The solution vector x and the associated finite element grid function
// will be maintained over the AMR iterations. We initialize it to zero.
Vector zero_vec(dim);
zero_vec = 0.0;
VectorConstantCoefficient zero_vec_coeff(zero_vec);
GridFunction x(&fespace);
x = 0.0;
OperatorHandle A;
Vector B, U;
// 8. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking only
// boundary attribute 1 from the mesh as essential and converting it to a
// list of true dofs. The conversion to true dofs will be done in the
// main loop.
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1;
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
u = 0.0;
U = 0.0;
OperatorHandle PCOp;
pcOp->FormSystemMatrix(ess_tdof_list, PCOp);
{
ComplexSparseMatrix * Asp =
dynamic_cast<ComplexSparseMatrix*>(A.Ptr());
cout << "Size of linear system: "
<< 2 * Asp->real().Width() << endl << endl;
}
// 10. Define and apply a GMRES solver for AU=B with a block diagonal
// preconditioner based on the appropriate sparse smoother.
{
Array<int> blockOffsets;
blockOffsets.SetSize(3);
blockOffsets[0] = 0;
blockOffsets[1] = PCOp.Ptr()->Height();
blockOffsets[2] = PCOp.Ptr()->Height();
blockOffsets.PartialSum();
BlockDiagonalPreconditioner BDP(blockOffsets);
Operator * pc_r = NULL;
Operator * pc_i = NULL;
switch (prob)
{
case 0: // fallthrough to case 2
case 2:
pc_r = new DSmoother(*PCOp.As<SparseMatrix>());
break;
case 1:
pc_r = new GSSmoother(*PCOp.As<SparseMatrix>());
break;
default: break; // This should be unreachable
}
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
BDP.SetDiagonalBlock(0, pc_r);
BDP.SetDiagonalBlock(1, pc_i);
BDP.owns_blocks = 1;
GMRESSolver gmres;
gmres.SetPreconditioner(BDP);
gmres.SetOperator(*A.Ptr());
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(1000);
gmres.SetPrintLevel(1);
gmres.Mult(B, U);
}
// 11. Recover the solution as a finite element grid function and compute the
// errors if the exact solution is known.
a->RecoverFEMSolution(U, b, u);
if (exact_sol)
{
double err_r = -1.0;
double err_i = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
default: break; // This should be unreachable
}
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
// 12. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m mesh -g sol".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_r_ofs("sol_r.gf");
ofstream sol_i_ofs("sol_i.gf");
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
}
// 13. Send the solution by socket to a GLVis server.
// 9. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u.real()
<< "window_title 'Solution: Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u.imag()
<< "window_title 'Solution: Imaginary Part'" << flush;
}
if (visualization && exact_sol)
{
*u_exact -= u;
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u_exact->real()
<< "window_title 'Error: Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u_exact->imag()
<< "window_title 'Error: Imaginary Part'" << flush;
}
if (visualization)
{
GridFunction u_t(fespace);
u_t = u.real();
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.open(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << u_t
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
int num_frames = 32;
int i = 0;
while (sol_sock)
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
add(cos( 2.0 * M_PI * t), u.real(),
sin(-2.0 * M_PI * t), u.imag(), u_t);
sol_sock << "solution\n" << *mesh << u_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
}
}
// 14. Free the used memory.
delete a;
delete u_exact;
delete pcOp;
delete fespace;
delete fec;
delete mesh;
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the ElasticityIntegrator to
// recover a smoothed flux (stress) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^tdim (i.e., vector-valued) space is used here.
// Here, tdim represents the number of components for a symmetric (dim x
// dim) tensor.
const int tdim = dim*(dim+1)/2;
FiniteElementSpace flux_fespace(&mesh, &fec, tdim);
ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace);
estimator.SetFluxAveraging(flux_averaging);
// 11. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 12. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
const int max_dofs = 50000;
const int max_amr_itr = 20;
for (int it = 0; it <= max_amr_itr; it++)
{
int cdofs = fespace.GetTrueVSize();
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << cdofs << endl;
// 13. Assemble the stiffness matrix and the right-hand side.
a.Assemble();
b.Assemble();
// 14. Set Dirichlet boundary values in the GridFunction x.
// Determine the list of Dirichlet true DOFs in the linear system.
Array<int> ess_tdof_list;
x.ProjectBdrCoefficient(zero_vec_coeff, ess_bdr);
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 15. Create the linear system: eliminate boundary conditions, constrain
// hanging nodes and possibly apply other transformations. The system
// will be solved for true (unconstrained) DOFs only.
SparseMatrix A;
Vector B, X;
const int copy_interior = 1;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
#ifndef MFEM_USE_SUITESPARSE
// 16. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the linear system with PCG.
GSSmoother M(A);
PCG(A, M, B, X, 3, 2000, 1e-12, 0.0);
#else
// 16. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the
// the linear system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(B, X);
#endif
// 17. After solving the linear system, reconstruct the solution as a
// finite element GridFunction. Constrained nodes are interpolated
// from true DOFs (it may therefore happen that x.Size() >= X.Size()).
a.RecoverFEMSolution(X, b, x);
// 18. Send solution by socket to the GLVis server.
if (visualization && sol_sock.good())
{
GridFunction nodes(&fespace), *nodes_p = &nodes;
mesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
mesh.SwapNodes(nodes_p, own_nodes);
x.Neg(); // visualize the backward displacement
sol_sock << "solution\n" << mesh << x << flush;
x.Neg();
mesh.SwapNodes(nodes_p, own_nodes);
if (it == 0)
{
sol_sock << "keys '" << ((dim == 2) ? "Rjl" : "") << "m'" << endl;
}
sol_sock << "window_title 'AMR iteration: " << it << "'\n"
<< "pause" << endl;
cout << "Visualization paused. "
"Press <space> in the GLVis window to continue." << endl;
}
if (cdofs > max_dofs)
{
cout << "Reached the maximum number of dofs. Stop." << endl;
break;
}
// 19. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
refiner.Apply(mesh);
if (refiner.Stop())
{
cout << "Stopping criterion satisfied. Stop." << endl;
break;
}
// 20. Update the space to reflect the new state of the mesh. Also,
// interpolate the solution x so that it lies in the new space but
// represents the same function. This saves solver iterations later
// since we'll have a good initial guess of x in the next step.
// Internally, FiniteElementSpace::Update() calculates an
// interpolation matrix which is then used by GridFunction::Update().
fespace.Update();
x.Update();
// 21. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
}
{
ofstream mesh_ref_out("ex22_reference.mesh");
mesh_ref_out.precision(16);
mesh.Print(mesh_ref_out);
ofstream mesh_out("ex22_deformed.mesh");
mesh_out.precision(16);
GridFunction nodes(&fespace), *nodes_p = &nodes;
mesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
mesh.SwapNodes(nodes_p, own_nodes);
mesh.Print(mesh_out);
mesh.SwapNodes(nodes_p, own_nodes);
ofstream x_out("ex22_displacement.sol");
x_out.precision(16);
x.Save(x_out);
}
return 0;
}
bool check_for_inline_mesh(const char * mesh_file)
{
string file(mesh_file);
size_t p0 = file.find_last_of("/");
string s0 = file.substr((p0==string::npos)?0:(p0+1),7);
return s0 == "inline-";
}
complex<double> u0_exact(const Vector &x)
{
int dim = x.Size();
complex<double> i(0.0, 1.0);
complex<double> alpha = (epsilon_ * omega_ - i * sigma_);
complex<double> kappa = std::sqrt(mu_ * omega_* alpha);
return std::exp(-i * kappa * x[dim - 1]);
}
double u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
double u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
}
+282 -538
View File
@@ -1,47 +1,33 @@
// MFEM Example 22 - Parallel Version
// MFEM Example 22
//
// Compile with: make ex22p
//
// Sample runs: mpirun -np 4 ex22p -m ../data/inline-segment.mesh -o 3
// mpirun -np 4 ex22p -m ../data/inline-tri.mesh -o 3
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 3
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 3 -p 1
// mpirun -np 4 ex22p -m ../data/inline-quad.mesh -o 3 -p 2
// mpirun -np 4 ex22p -m ../data/inline-tet.mesh -o 2
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 1
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 2
// mpirun -np 4 ex22p -m ../data/star.mesh -o 2 -sigma 10.0
// Sample runs: mpirun -np 4 ex22p
// mpirun -np 4 ex22p -o 3
// mpirun -np 4 ex22p -m ../data/beam-quad.mesh
// mpirun -np 4 ex22p -m ../data/beam-quad.mesh -o 3
// mpirun -np 4 ex22p -m ../data/beam-tet.mesh
// mpirun -np 4 ex22p -m ../data/beam-tet.mesh -o 2
// mpirun -np 4 ex22p -m ../data/beam-hex.mesh
// mpirun -np 4 ex22p -m ../data/beam-hex.mesh -o 2
//
// Description: This example code demonstrates the use of MFEM to define and
// solve simple complex-valued linear systems. It implements three
// variants of a damped harmonic oscillator:
// Description: This is a version of Example 2p with a simple adaptive mesh
// refinement loop. The problem being solved is again the linear
// elasticity describing a multi-material cantilever beam.
// The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
// or non-conforming (quadrilaterals, hexahedra) manner according
// to a simple ZZ error estimator.
//
// 1) A scalar H1 field
// -Div(a Grad u) - omega^2 b u + i omega c u = 0
// The example demonstrates MFEM's capability to work with both
// conforming and nonconforming refinements, in 2D and 3D, on
// linear and curved meshes. Interpolation of functions from
// coarse to fine meshes, as well as persistent GLVis
// visualization are also illustrated.
//
// 2) A vector H(Curl) field
// Curl(a Curl u) - omega^2 b u + i omega c u = 0
//
// 3) A vector H(Div) field
// -Grad(a Div u) - omega^2 b u + i omega c u = 0
//
// In each case the field is driven by a forced oscillation, with
// angular frequency omega, imposed at the boundary or a portion
// of the boundary.
//
// In electromagnetics the coefficients are typically named the
// permeability, mu = 1/a, permittivity, epsilon = b, and
// conductivity, sigma = c. The user can specify these constants
// using either set of names.
//
// The example also demonstrates how to display a time-varying
// solution as a sequence of fields sent to a single GLVis socket.
//
// We recommend viewing examples 1, 3 and 4 before viewing this
// We recommend viewing Examples 2p and 6p before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
@@ -49,70 +35,31 @@
using namespace std;
using namespace mfem;
static double mu_ = 1.0;
static double epsilon_ = 1.0;
static double sigma_ = 20.0;
static double omega_ = 10.0;
double u0_real_exact(const Vector &);
double u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
bool check_for_inline_mesh(const char * mesh_file);
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
// 0. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/inline-quad.mesh";
int ser_ref_levels = 1;
int par_ref_levels = 1;
// 1. Parse command-line options.
const char *mesh_file = "../data/beam-tri.mesh";
int serial_ref_levels = 0;
int order = 1;
int prob = 0;
double freq = -1.0;
double a_coef = 0.0;
bool static_cond = false;
bool visualization = 1;
bool herm_conv = true;
bool exact_sol = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&serial_ref_levels, "-rs", "--refine-serial",
"Number of uniform serial refinements (before parallel"
" partitioning)");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&prob, "-p", "--problem-type",
"Choose between 0: H_1, 1: H(Curl), or 2: H(Div) "
"damped harmonic oscillator.");
args.AddOption(&a_coef, "-a", "--stiffness-coef",
"Stiffness coefficient (spring constant or 1/mu).");
args.AddOption(&epsilon_, "-b", "--mass-coef",
"Mass coefficient (or epsilon).");
args.AddOption(&sigma_, "-c", "--damping-coef",
"Damping coefficient (or sigma).");
args.AddOption(&mu_, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon_, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma_, "-sigma", "--conductivity",
"Conductivity (or damping constant).");
args.AddOption(&freq, "-f", "--frequency",
"Frequency (in Hz).");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -131,492 +78,289 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
MFEM_VERIFY(prob >= 0 && prob <=2,
"Unrecognized problem type: " << prob);
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, and hexahedral meshes with the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(mesh.SpaceDimension() == dim, "invalid mesh");
if ( a_coef != 0.0 )
if (mesh.attributes.Max() < 2 || mesh.bdr_attributes.Max() < 2)
{
mu_ = 1.0 / a_coef;
}
if ( freq > 0.0 )
{
omega_ = 2.0 * M_PI * freq;
cerr << "\nInput mesh should have at least two materials and "
<< "two boundary attributes! (See schematic in ex2.cpp)\n"
<< endl;
MPI_Finalize();
return 3;
}
exact_sol = check_for_inline_mesh(mesh_file);
if (myid == 0 && exact_sol)
// 3. Refine the mesh before parallel partitioning. Since a NURBS mesh can
// currently only be refined uniformly, we need to convert it to a
// piecewise-polynomial curved mesh. First we refine the NURBS mesh a bit
// more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext && serial_ref_levels == 0)
{
cout << "Identified a mesh with known exact solution" << endl;
serial_ref_levels = 2;
}
for (int i = 0; i < serial_ref_levels; i++)
{
mesh.UniformRefinement();
}
if (mesh.NURBSext)
{
mesh.SetCurvature(2);
}
mesh.EnsureNCMesh();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// 4. Define a finite element space on the mesh. The polynomial order is
// one (linear) by default, but this can be changed on the command line.
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec, dim);
// 5. As in Example 2, we set up the linear form b(.) which corresponds to
// the right-hand side of the FEM linear system. In this case, b_i equals
// the boundary integral of f*phi_i where f represents a "pull down"
// force on the Neumann part of the boundary and phi_i are the basis
// functions in the finite element fespace. The force is defined by the
// VectorArrayCoefficient object f, which is a vector of Coefficient
// objects. The fact that f is non-zero on boundary attribute 2 is
// indicated by the use of piece-wise constants coefficient for its last
// component. We don't assemble the discrete problem yet, this will be
// done in the main loop.
VectorArrayCoefficient f(dim);
for (int i = 0; i < dim-1; i++)
{
f.Set(i, new ConstantCoefficient(0.0));
}
{
Vector pull_force(pmesh.bdr_attributes.Max());
pull_force = 0.0;
pull_force(1) = -1.0e-2;
f.Set(dim-1, new PWConstCoefficient(pull_force));
}
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
ParLinearForm b(&fespace);
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with piece-wise
// constants coefficient lambda and mu.
Vector lambda(pmesh.attributes.Max());
lambda = 1.0;
lambda(0) = lambda(1)*50;
PWConstCoefficient lambda_func(lambda);
Vector mu(pmesh.attributes.Max());
mu = 1.0;
mu(0) = mu(1)*50;
PWConstCoefficient mu_func(mu);
// 4. Refine the serial mesh on all processors to increase the resolution.
for (int l = 0; l < ser_ref_levels; l++)
{
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange, Nedelec, or Raviart-Thomas finite elements of
// the specified order.
if (dim == 1 && prob != 0 )
ParBilinearForm a(&fespace);
BilinearFormIntegrator *integ =
new ElasticityIntegrator(lambda_func,mu_func);
a.AddDomainIntegrator(integ);
if (static_cond) { a.EnableStaticCondensation(); }
// 7. The solution vector x and the associated finite element grid function
// will be maintained over the AMR iterations. We initialize it to zero.
Vector zero_vec(dim);
zero_vec = 0.0;
VectorConstantCoefficient zero_vec_coeff(zero_vec);
ParGridFunction x(&fespace);
x = 0.0;
// 8. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking only
// boundary attribute 1 from the mesh as essential and converting it to a
// list of true dofs. The conversion to true dofs will be done in the
// main loop.
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1;
// 9. GLVis visualization.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the ElasticityIntegrator to
// recover a smoothed flux (stress) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^tdim (i.e., vector-valued) space is used here.
// Here, tdim represents the number of components for a symmetric (dim x
// dim) tensor.
const int tdim = dim*(dim+1)/2;
L2_FECollection flux_fec(order, dim);
ParFiniteElementSpace flux_fespace(&pmesh, &flux_fec, tdim);
ParFiniteElementSpace smooth_flux_fespace(&pmesh, &fec, tdim);
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace,
smooth_flux_fespace);
// 11. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 12. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
const int max_dofs = 50000;
const int max_amr_itr = 20;
for (int it = 0; it <= max_amr_itr; it++)
{
HYPRE_Int global_dofs = fespace.GlobalTrueVSize();
if (myid == 0)
{
cout << "Switching to problem type 0, H1 basis functions, "
<< "for 1 dimensional mesh." << endl;
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << global_dofs << endl;
}
prob = 0;
}
FiniteElementCollection *fec = NULL;
switch (prob)
{
case 0: fec = new H1_FECollection(order, dim); break;
case 1: fec = new ND_FECollection(order, dim); break;
case 2: fec = new RT_FECollection(order - 1, dim); break;
default: break; // This should be unreachable
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 13. Assemble the stiffness matrix and the right-hand side.
a.Assemble();
b.Assemble();
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// based on the type of mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
if (exact_sol)
// 14. Set Dirichlet boundary values in the GridFunction x.
// Determine the list of Dirichlet true DOFs in the linear system.
Array<int> ess_tdof_list;
x.ProjectBdrCoefficient(zero_vec_coeff, ess_bdr);
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 15. Create the linear system: eliminate boundary conditions, constrain
// hanging nodes and possibly apply other transformations. The system
// will be solved for true (unconstrained) DOFs only.
HypreParMatrix A;
Vector B, X;
const int copy_interior = 1;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
// 16. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
// preconditioner from hypre.
HypreBoomerAMG amg;
amg.SetPrintLevel(0);
// amg.SetSystemsOptions(dim); // optional
CGSolver pcg(A.GetComm());
pcg.SetPreconditioner(amg);
pcg.SetOperator(A);
pcg.SetRelTol(1e-6);
pcg.SetMaxIter(500);
pcg.SetPrintLevel(3); // print the first and the last iterations only
pcg.Mult(B, X);
// 17. After solving the linear system, reconstruct the solution as a
// finite element GridFunction. Constrained nodes are interpolated
// from true DOFs (it may therefore happen that x.Size() >= X.Size()).
a.RecoverFEMSolution(X, b, x);
// 18. Send solution by socket to the GLVis server.
if (visualization && it == 0)
{
switch (prob)
sol_sock.open(vishost, visport);
sol_sock.precision(8);
}
if (visualization && sol_sock.good())
{
GridFunction nodes(&fespace), *nodes_p = &nodes;
pmesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
pmesh.SwapNodes(nodes_p, own_nodes);
x.Neg(); // visualize the backward displacement
sol_sock << "parallel " << num_procs << ' ' << myid << '\n';
sol_sock << "solution\n" << pmesh << x << flush;
x.Neg();
pmesh.SwapNodes(nodes_p, own_nodes);
if (it == 0)
{
case 0: ess_bdr = 0; ess_bdr[0] = 1; break;
default: ess_bdr = 1; ess_bdr[2] = 0; break;
sol_sock << "keys '" << ((dim == 2) ? "Rjl" : "") << "m'" << endl;
}
sol_sock << "window_title 'AMR iteration: " << it << "'\n"
<< "pause" << endl;
if (myid == 0)
{
cout << "Visualization paused. "
"Press <space> in the GLVis window to continue." << endl;
}
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system.
ParComplexLinearForm b(fespace, conv);
b.Vector::operator=(0.0);
// 9. Define the solution vector u as a parallel complex finite element grid
// function corresponding to fespace. Initialize u with initial guess of
// 1+0i or the exact solution if it is known.
ParComplexGridFunction u(fespace);
ParComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
u = 0.0;
switch (prob)
{
case 0:
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u0_r, u0_i); }
break;
case 1:
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u1_r, u1_i); }
break;
case 2:
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u2_r, u2_i); }
break;
default: break; // This should be unreachable
}
if (visualization && exact_sol)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact: Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact: Imaginary Part'" << flush;
}
// 10. Set up the parallel sesquilinear form a(.,.) on the finite element
// space corresponding to the damped harmonic oscillator operator of the
// appropriate type:
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + i omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) - omega^2 b + i omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + i omega c
//
ConstantCoefficient stiffnessCoef(1.0/mu_);
ConstantCoefficient massCoef(-omega_ * omega_ * epsilon_);
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
default: break; // This should be unreachable
}
// 10a. Set up the parallel bilinear form for the preconditioner
// corresponding to the appropriate operator
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) + omega^2 b + omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + omega c
//
ParBilinearForm *pcOp = new ParBilinearForm(fespace);
switch (prob)
{
case 0:
pcOp->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(lossCoef));
break;
case 1:
pcOp->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(negMassCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
case 2:
pcOp->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
default: break; // This should be unreachable
}
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, etc.
a->Assemble();
pcOp->Assemble();
OperatorHandle A;
Vector B, U;
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
u = 0.0;
U = 0.0;
OperatorHandle PCOp;
pcOp->FormSystemMatrix(ess_tdof_list, PCOp);
if (myid == 0)
{
ComplexHypreParMatrix * Ahyp =
dynamic_cast<ComplexHypreParMatrix*>(A.Ptr());
cout << "Size of linear system: "
<< 2 * Ahyp->real().GetGlobalNumRows() << endl << endl;
}
// 12. Define and apply a parallel FGMRES solver for AU=B with a block
// diagonal preconditioner based on the appropriate multigrid
// preconditioner from hypre.
{
Array<HYPRE_Int> blockTrueOffsets;
blockTrueOffsets.SetSize(3);
blockTrueOffsets[0] = 0;
blockTrueOffsets[1] = PCOp.Ptr()->Height();
blockTrueOffsets[2] = PCOp.Ptr()->Height();
blockTrueOffsets.PartialSum();
BlockDiagonalPreconditioner BDP(blockTrueOffsets);
Operator * pc_r = NULL;
Operator * pc_i = NULL;
switch (prob)
if (global_dofs > max_dofs)
{
case 0:
pc_r = new HypreBoomerAMG(*PCOp.As<HypreParMatrix>());
break;
case 1:
pc_r = new HypreAMS(*PCOp.As<HypreParMatrix>(), fespace);
break;
case 2:
if (dim == 2 )
{
pc_r = new HypreAMS(*PCOp.As<HypreParMatrix>(), fespace);
}
else
{
pc_r = new HypreADS(*PCOp.As<HypreParMatrix>(), fespace);
}
break;
default: break; // This should be unreachable
}
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
BDP.SetDiagonalBlock(0, pc_r);
BDP.SetDiagonalBlock(1, pc_i);
BDP.owns_blocks = 1;
FGMRESSolver fgmres(MPI_COMM_WORLD);
fgmres.SetPreconditioner(BDP);
fgmres.SetOperator(*A.Ptr());
fgmres.SetRelTol(1e-12);
fgmres.SetMaxIter(1000);
fgmres.SetPrintLevel(1);
fgmres.Mult(B, U);
}
// 13. Recover the parallel grid function corresponding to U. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(U, b, u);
if (exact_sol)
{
double err_r = -1.0;
double err_i = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
default: break; // This should be unreachable
if (myid == 0)
{
cout << "Reached the maximum number of dofs. Stop." << endl;
}
break;
}
if ( myid == 0 )
// 19. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
refiner.Apply(pmesh);
if (refiner.Stop())
{
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
if (myid == 0)
{
cout << "Stopping criterion satisfied. Stop." << endl;
}
break;
}
}
// 14. Save the refined mesh and the solution in parallel. This output can be
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_r_name, sol_i_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_r_name << "sol_r." << setfill('0') << setw(6) << myid;
sol_i_name << "sol_i." << setfill('0') << setw(6) << myid;
// 20. Update the space to reflect the new state of the mesh. Also,
// interpolate the solution x so that it lies in the new space but
// represents the same function. This saves solver iterations later
// since we'll have a good initial guess of x in the next step.
// Internally, FiniteElementSpace::Update() calculates an
// interpolation matrix which is then used by GridFunction::Update().
fespace.Update();
x.Update();
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_r_ofs(sol_r_name.str().c_str());
ofstream sol_i_ofs(sol_i_name.str().c_str());
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
}
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u.real()
<< "window_title 'Solution: Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u.imag()
<< "window_title 'Solution: Imaginary Part'" << flush;
}
if (visualization && exact_sol)
{
*u_exact -= u;
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Error: Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Error: Imaginary Part'" << flush;
}
if (visualization)
{
ParGridFunction u_t(fespace);
u_t = u.real();
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << u_t
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
if (myid == 0)
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
int num_frames = 32;
int i = 0;
while (sol_sock)
// 21. Load balance the mesh, and update the space and solution. Currently
// available only for nonconforming meshes.
if (pmesh.Nonconforming())
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
pmesh.Rebalance();
add(cos( 2.0 * M_PI * t), u.real(),
sin(-2.0 * M_PI * t), u.imag(), u_t);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock << "solution\n" << *pmesh << u_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
// Update the space and the GridFunction. This time the update matrix
// redistributes the GridFunction among the processors.
fespace.Update();
x.Update();
}
// 22. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
}
// 16. Free the used memory.
delete a;
delete u_exact;
delete pcOp;
delete fespace;
delete fec;
delete pmesh;
{
ostringstream mref_name, mesh_name, sol_name;
mref_name << "ex22p_reference_mesh." << setfill('0') << setw(6) << myid;
mesh_name << "ex22p_deformed_mesh." << setfill('0') << setw(6) << myid;
sol_name << "ex22p_displacement." << setfill('0') << setw(6) << myid;
ofstream mesh_ref_out(mref_name.str().c_str());
mesh_ref_out.precision(16);
pmesh.Print(mesh_ref_out);
ofstream mesh_out(mesh_name.str().c_str());
mesh_out.precision(16);
GridFunction nodes(&fespace), *nodes_p = &nodes;
pmesh.GetNodes(nodes);
nodes += x;
int own_nodes = 0;
pmesh.SwapNodes(nodes_p, own_nodes);
pmesh.Print(mesh_out);
pmesh.SwapNodes(nodes_p, own_nodes);
ofstream x_out(sol_name.str().c_str());
x_out.precision(16);
x.Save(x_out);
}
MPI_Finalize();
return 0;
}
bool check_for_inline_mesh(const char * mesh_file)
{
string file(mesh_file);
size_t p0 = file.find_last_of("/");
string s0 = file.substr((p0==string::npos)?0:(p0+1),7);
return s0 == "inline-";
}
complex<double> u0_exact(const Vector &x)
{
int dim = x.Size();
complex<double> i(0.0, 1.0);
complex<double> alpha = (epsilon_ * omega_ - i * sigma_);
complex<double> kappa = std::sqrt(mu_ * omega_* alpha);
return std::exp(-i * kappa * x[dim - 1]);
}
double u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
double u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
}
+2 -1
View File
@@ -102,7 +102,8 @@ int main(int argc, char *argv[])
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
int ref_levels =
(int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
+334
View File
@@ -0,0 +1,334 @@
// MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p
//
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
// mpirun -np 4 ex3p -m ../data/fichera.mesh
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
//
// Description: This example code solves a simple electromagnetic diffusion
// problem corresponding to the second order definite Maxwell
// equation curl curl E + E = f with boundary condition
// E x n = <given tangential field>. Here, we use a given exact
// solution E and compute the corresponding r.h.s. f.
// We discretize with Nedelec finite elements in 2D or 3D.
//
// The example demonstrates the use of H(curl) finite element
// spaces with the curl-curl and the (vector finite element) mass
// bilinear form, as well as the computation of discretization
// error when the exact solution is known. Static condensation is
// also illustrated.
//
// We recommend viewing examples 1-2 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 1;
bool static_cond = false;
bool visualization = 1;
#ifdef MFEM_USE_STRUMPACK
bool use_strumpack = false;
#endif
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
" solution.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&use_strumpack, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use STRUMPACK's double complex linear solver.");
#endif
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels =
(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
// spaces on them.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
pmesh->ReorientTetMesh();
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
VectorFunctionCoefficient f(sdim, f_exact);
ParLinearForm *b = new ParLinearForm(fespace);
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary edges will be used
// when eliminating the non-homogeneous boundary condition to modify the
// r.h.s. vector b.
ParGridFunction x(fespace);
VectorFunctionCoefficient E(sdim, E_exact);
x.ProjectCoefficient(E);
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl + sigma I, by adding the curl-curl and the
// mass domain integrators.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *sigma = new ConstantCoefficient(-1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (myid == 0)
{
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
StopWatch chrono;
chrono.Clear();
chrono.Start();
#ifdef MFEM_USE_STRUMPACK
if (use_strumpack)
{
Operator * Arow = new STRUMPACKRowLocMatrix(A);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
//Solver * precond = strumpack;
strumpack->Mult(B, X);
delete strumpack;
delete Arow;
}
else
#endif
{
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
// preconditioner from hypre.
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
HypreSolver *ams = new HypreAMS(A, prec_fespace);
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(500);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*ams);
pcg->Mult(B, X);
delete pcg;
delete ams;
}
chrono.Stop();
cout << "Solver time " << chrono.RealTime() << endl;
// 13. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 14. Compute and print the L^2 norm of the error.
{
double err = x.ComputeL2Error(E);
if (myid == 0)
{
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
}
}
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 17. Free the used memory.
delete a;
delete sigma;
delete muinv;
delete b;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
void E_exact(const Vector &x, Vector &E)
{
if (dim == 3)
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(2));
E(2) = sin(kappa * x(0));
}
else
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(0));
if (x.Size() == 3) { E(2) = 0.0; }
}
}
void f_exact(const Vector &x, Vector &f)
{
if (dim == 3)
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
}
else
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
if (x.Size() == 3) { f(2) = 0.0; }
}
}
+3 -12
View File
@@ -21,7 +21,7 @@
//
// The example demonstrates the use of the BlockMatrix class, as
// well as the collective saving of several grid functions in a
// VisIt (visit.llnl.gov) and ParaView (paraview.org) formats.
// VisIt (visit.llnl.gov) visualization format.
//
// We recommend viewing examples 1-4 before viewing this example.
@@ -264,16 +264,7 @@ int main(int argc, char *argv[])
visit_dc.RegisterField("pressure", &p);
visit_dc.Save();
// 14. Save data in the ParaView format
ParaViewDataCollection paraview_dc("PVExample5S", mesh);
paraview_dc.SetLevelsOfDetail(2);
paraview_dc.SetCycle(1);
paraview_dc.SetTime(0.0); // set the time
paraview_dc.RegisterField("velocity",&u);
paraview_dc.RegisterField("pressure",&p);
paraview_dc.Save();
// 15. Send the solution by socket to a GLVis server.
// 14. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -286,7 +277,7 @@ int main(int argc, char *argv[])
p_sock << "solution\n" << *mesh << p << "window_title 'Pressure'" << endl;
}
// 16. Free the used memory.
// 15. Free the used memory.
delete fform;
delete gform;
delete invM;
+4 -12
View File
@@ -21,7 +21,7 @@
//
// The example demonstrates the use of the BlockMatrix class, as
// well as the collective saving of several grid functions in a
// VisIt (visit.llnl.gov) and ParaView (paraview.org) formats.
// VisIt (visit.llnl.gov) visualization format.
//
// We recommend viewing examples 1-4 before viewing this example.
@@ -239,6 +239,7 @@ int main(int argc, char *argv[])
// 12. Solve the linear system with MINRES.
// Check the norm of the unpreconditioned residual.
int maxIter(500);
double rtol(1.e-6);
double atol(1.e-10);
@@ -325,16 +326,7 @@ int main(int argc, char *argv[])
DataCollection::PARALLEL_FORMAT);
visit_dc.Save();
// 16. Save data in the ParaView format
ParaViewDataCollection paraview_dc("PVExample5P", pmesh);
paraview_dc.SetLevelsOfDetail(1);
paraview_dc.SetCycle(1);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("velocity",u);
paraview_dc.RegisterField("pressure",p);
paraview_dc.Save();
// 17. Send the solution by socket to a GLVis server.
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -354,7 +346,7 @@ int main(int argc, char *argv[])
<< endl;
}
// 18. Free the used memory.
// 17. Free the used memory.
delete fform;
delete gform;
delete u;
+15 -16
View File
@@ -16,11 +16,9 @@
// ex6 -m ../data/amr-quad.mesh
//
// Device sample runs:
// ex6 -pa -d cuda
// ex6 -pa -d occa-cuda
// ex6 -pa -d raja-omp
// ex6 -pa -d ceed-cpu
// ex6 -pa -d ceed-cuda
// > ex6 -pa -d cuda
// > ex6 -pa -d occa-cuda
// > ex6 -pa -d raja-omp
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
@@ -51,7 +49,7 @@ int main(int argc, char *argv[])
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool pa = false;
const char *device_config = "cpu";
const char *device = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
@@ -61,7 +59,7 @@ int main(int argc, char *argv[])
"Finite element order (polynomial degree).");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -74,19 +72,14 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
// 2. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
device.Print();
// 3. Read the mesh from the given mesh file. We can handle triangular,
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
int sdim = mesh.SpaceDimension();
// 4. Since a NURBS mesh can currently only be refined uniformly, we need to
// 3. Since a NURBS mesh can currently only be refined uniformly, we need to
// convert it to a piecewise-polynomial curved mesh. First we refine the
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext)
@@ -98,11 +91,15 @@ int main(int argc, char *argv[])
mesh.SetCurvature(2);
}
// 5. Define a finite element space on the mesh. The polynomial order is
// 4. Define a finite element space on the mesh. The polynomial order is
// one (linear) by default, but this can be changed on the command line.
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(&mesh, &fec);
// 5. Set device config parameters from the command line options.
Device::Configure(device);
Device::Print();
// 6. As in Example 1, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
@@ -171,7 +168,8 @@ int main(int argc, char *argv[])
x.ProjectBdrCoefficient(zero, ess_bdr);
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 15. Assemble the stiffness matrix.
// 15. Switch to the device and assemble the stiffness matrix.
Device::Enable();
a.Assemble();
// 16. Create the linear system: eliminate boundary conditions, constrain
@@ -206,6 +204,7 @@ int main(int argc, char *argv[])
// 18. After solving the linear system, reconstruct the solution as a
// finite element GridFunction. Constrained nodes are interpolated
// from true DOFs (it may therefore happen that x.Size() >= X.Size()).
Device::Disable();
a.RecoverFEMSolution(X, b, x);
// 19. Send solution by socket to the GLVis server.
+19 -20
View File
@@ -16,11 +16,9 @@
// mpirun -np 4 ex6p -m ../data/amr-quad.mesh
//
// Device sample runs:
// mpirun -np 4 ex6p -pa -d cuda
// mpirun -np 4 ex6p -pa -d occa-cuda
// mpirun -np 4 ex6p -pa -d raja-omp
// mpirun -np 4 ex6p -pa -d ceed-cpu
// mpirun -np 4 ex6p -pa -d ceed-cuda
// > mpirun -np 4 ex6p -pa -d cuda
// > mpirun -np 4 ex6p -pa -d occa-cuda
// > mpirun -np 4 ex6p -pa -d raja-omp
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
@@ -57,7 +55,7 @@ int main(int argc, char *argv[])
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool pa = false;
const char *device_config = "cpu";
const char *device = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
@@ -67,7 +65,7 @@ int main(int argc, char *argv[])
"Finite element order (polynomial degree).");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -87,19 +85,14 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 4. Read the (serial) mesh from the given mesh file on all processors. We
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 5. Refine the serial mesh on all processors to increase the resolution.
// 4. Refine the serial mesh on all processors to increase the resolution.
// Also project a NURBS mesh to a piecewise-quadratic curved mesh. Make
// sure that the mesh is non-conforming.
if (mesh->NURBSext)
@@ -109,7 +102,7 @@ int main(int argc, char *argv[])
}
mesh->EnsureNCMesh();
// 6. Define a parallel mesh by partitioning the serial mesh.
// 5. Define a parallel mesh by partitioning the serial mesh.
// Once the parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
delete mesh;
@@ -119,11 +112,15 @@ int main(int argc, char *argv[])
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// 7. Define a finite element space on the mesh. The polynomial order is
// 6. Define a finite element space on the mesh. The polynomial order is
// one (linear) by default, but this can be changed on the command line.
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec);
// 7. Set device config parameters from the command line options.
Device::Configure(device);
if (myid == 0) { Device::Print(); }
// 8. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
@@ -203,10 +200,11 @@ int main(int argc, char *argv[])
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
b.Assemble();
// 15. Assemble the stiffness matrix. Note that MFEM doesn't care at this
// point that the mesh is nonconforming and parallel. The FE space is
// considered 'cut' along hanging edges/faces, and also across
// processor boundaries.
// 15. Switch to the device and assemble the stiffness matrix. Note that
// MFEM doesn't care at this point that the mesh is nonconforming and
// parallel. The FE space is considered 'cut' along hanging
// edges/faces, and also across processor boundaries.
Device::Enable();
a.Assemble();
// 16. Create the parallel linear system: eliminate boundary conditions.
@@ -234,6 +232,7 @@ int main(int argc, char *argv[])
// 18. Switch back to the host and extract the parallel grid function
// corresponding to the finite element approximation X. This is the
// local solution on each processor.
Device::Disable();
a.RecoverFEMSolution(X, b, x);
// 19. Send the solution by socket to a GLVis server.
+1 -24
View File
@@ -26,8 +26,7 @@
// conditions through periodic meshes, as well as the use of GLVis
// for persistent visualization of a time-evolving solution. The
// saving of time-dependent data files for external visualization
// with VisIt (visit.llnl.gov) and ParaView (paraview.org) is also
// illustrated.
// with VisIt (visit.llnl.gov) is also illustrated.
#include "mfem.hpp"
#include <fstream>
@@ -90,7 +89,6 @@ int main(int argc, char *argv[])
double dt = 0.01;
bool visualization = true;
bool visit = false;
bool paraview = false;
bool binary = false;
int vis_steps = 5;
@@ -119,9 +117,6 @@ int main(int argc, char *argv[])
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
@@ -242,16 +237,6 @@ int main(int argc, char *argv[])
dc->Save();
}
ParaViewDataCollection *pd = NULL;
if (paraview)
{
pd = new ParaViewDataCollection("PVExample9S", &mesh);
pd->RegisterField("solution", &u);
pd->SetLevelsOfDetail(2);
pd->SetCycle(0);
pd->SetTime(0.0);
}
socketstream sout;
if (visualization)
{
@@ -309,13 +294,6 @@ int main(int argc, char *argv[])
dc->SetTime(t);
dc->Save();
}
if (paraview)
{
pd->SetCycle(ti);
pd->SetTime(t);
pd->Save();
}
}
}
@@ -329,7 +307,6 @@ int main(int argc, char *argv[])
// 10. Free the used memory.
delete ode_solver;
delete pd;
delete dc;
return 0;
+1 -25
View File
@@ -26,8 +26,7 @@
// conditions through periodic meshes, as well as the use of GLVis
// for persistent visualization of a time-evolving solution. The
// saving of time-dependent data files for external visualization
// with VisIt (visit.llnl.gov) and ParaView (paraview.org) is also
// illustrated.
// with VisIt (visit.llnl.gov) is also illustrated.
#include "mfem.hpp"
#include <fstream>
@@ -96,7 +95,6 @@ int main(int argc, char *argv[])
double dt = 0.01;
bool visualization = true;
bool visit = false;
bool paraview = false;
bool binary = false;
int vis_steps = 5;
@@ -127,9 +125,6 @@ int main(int argc, char *argv[])
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
@@ -286,17 +281,6 @@ int main(int argc, char *argv[])
dc->Save();
}
ParaViewDataCollection *pd = NULL;
if (paraview)
{
pd = new ParaViewDataCollection("PVExample9P", pmesh);
pd->RegisterField("solution", u);
pd->SetLevelsOfDetail(2);
pd->SetCycle(0);
pd->SetTime(0.0);
pd->Save();
}
socketstream sout;
if (visualization)
{
@@ -368,13 +352,6 @@ int main(int argc, char *argv[])
dc->SetTime(t);
dc->Save();
}
if (paraview)
{
pd->SetCycle(ti);
pd->SetTime(t);
pd->Save();
}
}
}
@@ -401,7 +378,6 @@ int main(int argc, char *argv[])
delete fes;
delete pmesh;
delete ode_solver;
delete pd;
delete dc;
MPI_Finalize();
-59
View File
@@ -1,59 +0,0 @@
# Copyright (c) 2019, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
set(GINKGO_EXAMPLES_SRCS)
list(APPEND GINKGO_EXAMPLES_SRCS
ex1.cpp
)
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add "test_ginkgo" target, see below.
add_custom_target(test_ginkgo
${CMAKE_CTEST_COMMAND} -R ginkgo USES_TERMINAL)
# Add one executable per cpp file, adding "ginkgo_" as prefix. Sets
# "test_ginkgo" as a target that depends on the given examples.
set(PFX ginkgo_)
add_mfem_examples(GINKGO_EXAMPLES_SRCS ${PFX} "" test_ginkgo)
# Testing.
# The GINKGO tests can be run separately using the target "test_ginkgo"
# which builds the examples and runs:
# ctest -R ginkgo
# Command line options for the tests.
set(EX1_COMMON_OPTS ex1 -m ../data/star.mesh --use_gko_solver)
set(EX1_TEST_OPTS ${EX9_COMMON_OPTS})
# Add the tests: one test per source file.
foreach(SRC_FILE ${GINKGO_EXAMPLES_SRCS})
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
set(TEST_NAME ${PFX}${TEST_NAME})
set(THIS_TEST_OPTIONS "-no-vis")
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
if (NOT (${TEST_NAME} MATCHES ".*p$"))
add_test(NAME ${TEST_NAME}_ser
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
else()
add_test(NAME ${TEST_NAME}_np=4
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
-259
View File
@@ -1,259 +0,0 @@
// MFEM Example 1
// GINKGO Modification
//
// Compile with: make ex1
//
// Sample runs: ex1 -m ../data/square-disc.mesh
// ex1 -m ../data/star.mesh
// ex1 -m ../data/star-mixed.mesh
// ex1 -m ../data/escher.mesh
// ex1 -m ../data/fichera.mesh
// ex1 -m ../data/fichera-mixed.mesh
// ex1 -m ../data/toroid-wedge.mesh
// ex1 -m ../data/square-disc-p2.vtk -o 2
// ex1 -m ../data/square-disc-p3.mesh -o 3
// ex1 -m ../data/square-disc-nurbs.mesh -o -1
// ex1 -m ../data/star-mixed-p2.mesh -o 2
// ex1 -m ../data/disc-nurbs.mesh -o -1
// ex1 -m ../data/pipe-nurbs.mesh -o -1
// ex1 -m ../data/fichera-mixed-p2.mesh -o 2
// ex1 -m ../data/star-surf.mesh
// ex1 -m ../data/square-disc-surf.mesh
// ex1 -m ../data/inline-segment.mesh
// ex1 -m ../data/amr-quad.mesh
// ex1 -m ../data/amr-hex.mesh
// ex1 -m ../data/fichera-amr.mesh
// ex1 -m ../data/mobius-strip.mesh
// ex1 -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// ex1 -pa -d cuda
// ex1 -pa -d raja-cuda
// ex1 -pa -d occa-cuda
// ex1 -pa -d raja-omp
// ex1 -pa -d occa-omp
// ex1 -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of essential boundary conditions, static condensation, and the
// optional connection to the GLVis tool for visualization.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#ifndef MFEM_USE_GINKGO
#error This example requires that MFEM is built with MFEM_USE_GINKGO=YES
#endif
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
bool use_ginkgo_solver= 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) or -1 for"
" isoparametric space.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&use_ginkgo_solver, "-gko", "--use_gko_solver", "-no-gko",
"--no-gko-solver",
"Solve using ginkgo.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
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,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
// largest number that gives a final mesh with no more than 50,000
// elements.
{
int ref_levels =
(int)floor(log(50000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a finite element space on the mesh. Here we use continuous
// Lagrange finite elements of the specified order. If order < 1, we
// instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
}
else if (mesh->GetNodes())
{
fec = mesh->GetNodes()->OwnFEC();
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
else
{
fec = new H1_FECollection(order = 1, dim);
}
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
cout << "Number of finite element unknowns: "
<< fespace->GetTrueVSize() << endl;
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking all
// the boundary attributes from the mesh as essential (Dirichlet) and
// converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
// the basis functions in the finite element fespace.
LinearForm *b = new LinearForm(fespace);
ConstantCoefficient one(1.0);
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 8. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
GridFunction x(fespace);
x = 0.0;
// 9. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
BilinearForm *a = new BilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DiffusionIntegrator(one));
// 10. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
OperatorPtr A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
cout << "Size of linear system: " << A->Height() << endl;
// 11. Solve the linear system A X = B.
if (!pa)
{
if (use_ginkgo_solver)
{
#ifdef MFEM_USE_GINKGO
// Solve the linear system with CG + ILU from Ginkgo.
std::string executor = "reference";
auto exec = gko::ReferenceExecutor::create();
auto ilu_precond =
gko::preconditioner::Ilu<gko::solver::LowerTrs<>,
gko::solver::UpperTrs<>, false>::build()
.on(exec);
GinkgoWrappers::CGSolver ginkgo_solver(executor, 1, 2000, 1e-12, 0.0,
ilu_precond.release() );
ginkgo_solver.solve(&((SparseMatrix&)(*A)), X, B);
#endif
}
else
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#endif
}
}
else // No preconditioning for now in partial assembly mode.
{
CG(*A, B, X, 1, 2000, 1e-12, 0.0);
}
// 12. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
// 13. Save the refined mesh and the solution. This output can be viewed later
// using GLVis: "glvis -m refined.mesh -g sol.gf".
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
x.Save(sol_ofs);
// 14. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << x << flush;
}
// 15. Free the used memory.
delete a;
delete b;
delete fespace;
if (order > 0) { delete fec; }
delete mesh;
return 0;
}
-81
View File
@@ -1,81 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/ginkgo/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
# Currently there are only serial Ginkgo examples
SEQ_EXAMPLES = ex1
PAR_EXAMPLES =
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
ifeq ($(MFEM_USE_GINKGO),NO)
$(EXAMPLES):
$(error MFEM is not configured with GINKO)
endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI_NP = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP)
RUN_MPI = $(RUN_MPI_NP) $(MFEM_MPI_NP)
SERIAL_NAME := Serial GINKGO example
PARALLEL_NAME := Parallel GINKGO example
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
%-test-seq: %
@$(call mfem-test,$<,, $(SERIAL_NAME))
# Testing: Example-specific execution options:
ex1-test-par: ex1
@$(call mfem-test,$<, $(RUN_MPI_NP) 1, $(PARALLEL_NAME))
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f refined.mesh sol.gf mesh.* sol.*
-63
View File
@@ -1,63 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
set(HIOP_EXAMPLES_SRCS)
list(APPEND HIOP_EXAMPLES_SRCS ex9.cpp)
if (MFEM_USE_MPI)
list(APPEND HIOP_EXAMPLES_SRCS ex9p.cpp)
endif()
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add "test_hiop" target, see below.
add_custom_target(test_hiop
${CMAKE_CTEST_COMMAND} -R hiop USES_TERMINAL)
# Add one executable per cpp file, adding "hiop_" as prefix. Sets
# "test_hiop" as a target that depends on the given examples.
set(PFX hiop_)
add_mfem_examples(HIOP_EXAMPLES_SRCS ${PFX} "" test_hiop)
# Testing.
# The HIOP tests can be run separately using the target "test_hiop"
# which builds the examples and runs:
# ctest -R hiop
# Command line options for the tests.
# Example 9:
set(EX9_COMMON_OPTS -m ../../data/periodic-segment.mesh -p 0 -dt 0.005)
set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 )
set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS})
# Add the tests: one test per source file.
foreach(SRC_FILE ${HIOP_EXAMPLES_SRCS})
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
set(TEST_NAME ${PFX}${TEST_NAME})
set(THIS_TEST_OPTIONS "-no-vis")
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
if (NOT (${TEST_NAME} MATCHES ".*p$"))
add_test(NAME ${TEST_NAME}_ser
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
else()
add_test(NAME ${TEST_NAME}_np=4
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
-684
View File
@@ -1,684 +0,0 @@
// MFEM Example 9 with Nonlinear Constrained Optimization
//
// Compile with: make ex9
//
// Sample runs:
//
// ex9 -m ../../data/periodic-segment.mesh -r 3 -p 0 -o 2 -dt 0.002 -opt 1
// ex9 -m ../../data/periodic-segment.mesh -r 3 -p 0 -o 2 -dt 0.002 -opt 2
//
// ex9 -m ../../data/periodic-square.mesh -p 0 -r 2 -dt 0.01 -tf 10 -opt 1
// ex9 -m ../../data/periodic-square.mesh -p 0 -r 2 -dt 0.01 -tf 10 -opt 2
//
// ex9 -m ../../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9 -opt 1
// ex9 -m ../../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9 -opt 2
//
// ex9 -m ../../data/amr-quad.mesh -p 1 -r 1 -dt 0.002 -tf 9 -opt 1
// ex9 -m ../../data/amr-quad.mesh -p 1 -r 1 -dt 0.002 -tf 9 -opt 2
//
// ex9 -m ../../data/disc-nurbs.mesh -p 1 -r 2 -dt 0.005 -tf 9 -opt 1
// ex9 -m ../../data/disc-nurbs.mesh -p 1 -r 2 -dt 0.005 -tf 9 -opt 2
//
// ex9 -m ../../data/disc-nurbs.mesh -p 2 -r 2 -dt 0.01 -tf 9 -opt 1
// ex9 -m ../../data/disc-nurbs.mesh -p 2 -r 2 -dt 0.01 -tf 9 -opt 2
//
// ex9 -m ../../data/periodic-square.mesh -p 3 -r 3 -dt 0.0025 -tf 9 -opt 1
// ex9 -m ../../data/periodic-square.mesh -p 3 -r 3 -dt 0.0025 -tf 9 -opt 2
//
// ex9 -m ../../data/periodic-cube.mesh -p 0 -r 2 -o 2 -dt 0.02 -tf 8 -opt 1
// ex9 -m ../../data/periodic-cube.mesh -p 0 -r 2 -o 2 -dt 0.02 -tf 8 -opt 2
// Description: This example modifies the standard MFEM ex9 by adding nonlinear
// constrained optimization capabilities through the SLBQP and
// HIOP solvers. It demonstrates how a user can define a custom
// class OptimizationProblem that includes linear/nonlinear
// equality/inequality constraints. This optimization is applied
// as post-processing to the solution of the transport equation.
//
// Description of ex9:
// This example code solves the time-dependent advection equation
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
// u0(x)=u(0,x) is a given initial condition.
//
// The example demonstrates the use of Discontinuous Galerkin (DG)
// bilinear forms in MFEM (face integrators), the use of explicit
// ODE time integrators, the definition of periodic boundary
// conditions through periodic meshes, as well as the use of GLVis
// for persistent visualization of a time-evolving solution. The
// saving of time-dependent data files for external visualization
// with VisIt (visit.llnl.gov) is also illustrated.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Choice for the problem setup. The fluid velocity, initial condition and
// inflow boundary condition are chosen based on this parameter.
int problem;
// Nonlinear optimizer.
int optimizer_type;
// Velocity coefficient
bool invert_velocity = false;
void velocity_function(const Vector &x, Vector &v);
// Initial condition
double u0_function(const Vector &x);
// Inflow boundary condition
double inflow_function(const Vector &x);
// Mesh bounding box
Vector bb_min, bb_max;
/// Computes C(x) = sum w_i x_i, where w is a given Vector.
class LinearScaleOperator : public Operator
{
private:
const Vector &w;
mutable DenseMatrix grad;
public:
LinearScaleOperator(const Vector &weight)
: Operator(1, weight.Size()), w(weight), grad(1, width)
{
for (int i = 0; i < width; i++) { grad(0, i) = w(i); }
}
virtual void Mult(const Vector &x, Vector &y) const
{
y(0) = w * x;
}
virtual Operator &GetGradient(const Vector &x) const
{
return grad;
}
};
/// Nonlinear monotone bounded operator to test nonlinear ineq constraints.
/// Computes D(x) = tanh(sum(x_i)).
class TanhSumOperator : public Operator
{
private:
mutable DenseMatrix grad;
public:
TanhSumOperator(int size) : Operator(1, size), grad(1, width) { }
virtual void Mult(const Vector &x, Vector &y) const
{
y(0) = std::tanh(x.Sum());
}
virtual Operator &GetGradient(const Vector &x) const
{
const double ts = std::tanh(x.Sum());
const double dtanh = 1.0 - ts * ts;
for (int i = 0; i < width; i++) { grad(0, i) = dtanh; }
return grad;
}
};
/** Monotone and conservative a-posteriori correction for transport solutions:
* Find x that minimizes 0.5 || x - x_HO ||^2, subject to
* sum w_i x_i = mass,
* tanh(sum(x_i_min)) <= tanh(sum(x_i)) <= tanh(sum(x_i_max)),
* x_i_min <= x_i <= x_i_max,
*/
class OptimizedTransportProblem : public OptimizationProblem
{
private:
const Vector &x_HO;
Vector massvec, d_lo, d_hi;
const LinearScaleOperator LSoper;
const TanhSumOperator TSoper;
public:
OptimizedTransportProblem(const Vector &xho, const Vector &w, double mass,
const Vector &xmin, const Vector &xmax)
: OptimizationProblem(xho.Size(), NULL, NULL),
x_HO(xho), massvec(1), d_lo(1), d_hi(1),
LSoper(w), TSoper(w.Size())
{
C = &LSoper;
massvec(0) = mass;
SetEqualityConstraint(massvec);
D = &TSoper;
d_lo(0) = std::tanh(xmin.Sum());
d_hi(0) = std::tanh(xmax.Sum());
MFEM_ASSERT(d_lo(0) < d_hi(0),
"The bounds produce an infeasible optimization problem");
SetInequalityConstraint(d_lo, d_hi);
SetSolutionBounds(xmin, xmax);
}
virtual double CalcObjective(const Vector &x) const
{
double res = 0.0;
for (int i = 0; i < input_size; i++)
{
const double d = x(i) - x_HO(i);
res += d * d;
}
return 0.5 * res;
}
virtual void CalcObjectiveGrad(const Vector &x, Vector &grad) const
{
for (int i = 0; i < input_size; i++) { grad(i) = x(i) - x_HO(i); }
}
};
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass
and advection matrices, and b describes the flow on the boundary. This can
be written as a general ODE, du/dt = M^{-1} (K u + b), and this class is
used to evaluate the right-hand side. */
class FE_Evolution : public TimeDependentOperator
{
private:
SparseMatrix &M, &K;
const Vector &b;
DSmoother M_prec;
CGSolver M_solver;
mutable Vector z;
double dt;
BilinearForm &bf;
Vector &M_rowsums;
public:
FE_Evolution(SparseMatrix &_M, SparseMatrix &_K, const Vector &_b,
BilinearForm &_bf, Vector &M_rs);
void SetTimeStep(double _dt) { dt = _dt; }
void SetK(SparseMatrix &_K) { K = _K; }
virtual void Mult(const Vector &x, Vector &y) const;
virtual ~FE_Evolution() { }
};
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
problem = 0;
optimizer_type = 1;
const char *mesh_file = "../../data/periodic-hexagon.mesh";
int ref_levels = 2;
int order = 3;
int ode_solver_type = 3;
double t_final = 1.0;
double dt = 0.01;
bool visualization = true;
bool visit = false;
bool binary = false;
int vis_steps = 5;
int precision = 8;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use. See options in velocity_function().");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&optimizer_type, "-opt", "--optimizer",
"Nonlinear optimizer: 1 - SLBQP,\n\t"
" 2 - HIOP.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle geometrically
// periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
ODESolver *ode_solver = NULL;
switch (ode_solver_type)
{
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(1.0); break;
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
case 6: ode_solver = new RK6Solver; break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
delete mesh;
return 3;
}
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter. If the mesh is of NURBS type, we convert it to
// a (piecewise-polynomial) high-order mesh.
for (int lev = 0; lev < ref_levels; lev++)
{
mesh->UniformRefinement();
}
if (mesh->NURBSext)
{
mesh->SetCurvature(max(order, 1));
}
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 5. Define the discontinuous DG finite element space of the given
// polynomial order on the refined mesh.
DG_FECollection fec(order, dim, BasisType::Positive);
FiniteElementSpace fes(mesh, &fec);
cout << "Number of unknowns: " << fes.GetVSize() << endl;
// 6. Set up and assemble the bilinear and linear forms corresponding to the
// DG discretization. The DGTraceIntegrator involves integrals over mesh
// interior faces.
VectorFunctionCoefficient velocity(dim, velocity_function);
FunctionCoefficient inflow(inflow_function);
FunctionCoefficient u0(u0_function);
BilinearForm m(&fes);
m.AddDomainIntegrator(new MassIntegrator);
BilinearForm k(&fes);
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k.AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
k.AddBdrFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
LinearForm b(&fes);
b.AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
m.Assemble();
m.Finalize();
int skip_zeros = 0;
k.Assemble(skip_zeros);
k.Finalize(skip_zeros);
b.Assemble();
// 7. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
// GLVis visualization.
GridFunction u(&fes);
u.ProjectCoefficient(u0);
{
ofstream omesh("ex9.mesh");
omesh.precision(precision);
mesh->Print(omesh);
ofstream osol("ex9-init.gf");
osol.precision(precision);
u.Save(osol);
}
// Create data collection for solution output: either VisItDataCollection for
// ascii data files, or SidreDataCollection for binary data files.
DataCollection *dc = NULL;
if (visit)
{
if (binary)
{
#ifdef MFEM_USE_SIDRE
dc = new SidreDataCollection("Example9", mesh);
#else
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
#endif
}
else
{
dc = new VisItDataCollection("Example9", mesh);
dc->SetPrecision(precision);
}
dc->RegisterField("solution", &u);
dc->SetCycle(0);
dc->SetTime(0.0);
dc->Save();
}
socketstream sout;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
sout.open(vishost, visport);
if (!sout)
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
visualization = false;
cout << "GLVis visualization disabled.\n";
}
else
{
sout.precision(precision);
sout << "solution\n" << *mesh << u;
sout << "pause\n";
sout << flush;
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
Vector M_rowsums(m.Size());
m.SpMat().GetRowSums(M_rowsums);
// 8. Define the time-dependent evolution operator describing the ODE
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(m.SpMat(), k.SpMat(), b, k, M_rowsums);
double t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
// Compute initial volume.
const double vol0 = M_rowsums * u;
bool done = false;
for (int ti = 0; !done; )
{
double dt_real = min(dt, t_final - t);
adv.SetTimeStep(dt_real);
ode_solver->Step(u, t, dt_real);
ti++;
done = (t >= t_final - 1e-8*dt);
if (done || ti % vis_steps == 0)
{
cout << "time step: " << ti << ", time: " << t << endl;
if (visualization)
{
sout << "solution\n" << *mesh << u << flush;
}
if (visit)
{
dc->SetCycle(ti);
dc->SetTime(t);
dc->Save();
}
}
}
// Print the error vs exact solution.
const double max_error = u.ComputeMaxError(u0),
l1_error = u.ComputeL1Error(u0),
l2_error = u.ComputeL2Error(u0);
std::cout << "Linf error = " << max_error << endl
<< "L1 error = " << l1_error << endl
<< "L2 error = " << l2_error << endl;
// Print error in volume.
const double vol = M_rowsums * u;
std::cout << "Vol error = " << vol - vol0 << endl;
// 9. Save the final solution. This output can be viewed later using GLVis:
// "glvis -m ex9.mesh -g ex9-final.gf".
{
ofstream osol("ex9-final.gf");
osol.precision(precision);
u.Save(osol);
}
// 10. Free the used memory.
delete ode_solver;
delete dc;
delete mesh;
return 0;
}
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(SparseMatrix &_M, SparseMatrix &_K,
const Vector &_b, BilinearForm &_bf, Vector &M_rs)
: TimeDependentOperator(_M.Size()),
M(_M), K(_K), b(_b), M_prec(), M_solver(), z(_M.Size()),
bf(_bf), M_rowsums(M_rs)
{
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(M);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
}
void FE_Evolution::Mult(const Vector &x, Vector &y) const
{
// Compute bounds y_min, y_max for y from x on the ldofs.
const int dofs = x.Size();
Vector y_min(dofs), y_max(dofs);
const int *In = bf.SpMat().GetI(), *Jn = bf.SpMat().GetJ();
for (int i = 0, k = 0; i < dofs; i++)
{
double x_i_min = +std::numeric_limits<double>::infinity();
double x_i_max = -std::numeric_limits<double>::infinity();
for (int end = In[i+1]; k < end; k++)
{
const int j = Jn[k];
if (x(j) > x_i_max) { x_i_max = x(j); }
if (x(j) < x_i_min) { x_i_min = x(j); }
}
y_min(i) = x_i_min;
y_max(i) = x_i_max;
}
for (int i = 0; i < dofs; i++)
{
y_min(i) = (y_min(i) - x(i) ) / dt;
y_max(i) = (y_max(i) - x(i) ) / dt;
}
// Compute the high-order solution y = M^{-1} (K x + b).
K.Mult(x, z);
z += b;
M_solver.Mult(z, y);
// The solution y is an increment; it should not introduce new mass.
const double mass_y = 0.0;
// Perform optimization.
Vector y_out(dofs);
const int max_iter = 500;
const double rtol = 1.e-7;
double atol = 1.e-7;
OptimizationSolver *optsolver = NULL;
if (optimizer_type == 2)
{
#ifdef MFEM_USE_HIOP
HiopNlpOptimizer *tmp_opt_ptr = new HiopNlpOptimizer();
optsolver = tmp_opt_ptr;
#else
MFEM_ABORT("MFEM is not built with HiOp support!");
#endif
}
else
{
SLBQPOptimizer *slbqp = new SLBQPOptimizer();
slbqp->SetBounds(y_min, y_max);
slbqp->SetLinearConstraint(M_rowsums, mass_y);
atol = 1.e-15;
optsolver = slbqp;
}
OptimizedTransportProblem ot_prob(y, M_rowsums, mass_y, y_min, y_max);
optsolver->SetOptimizationProblem(ot_prob);
optsolver->SetMaxIter(max_iter);
optsolver->SetAbsTol(atol);
optsolver->SetRelTol(rtol);
optsolver->SetPrintLevel(0);
optsolver->Mult(y, y_out);
y = y_out;
delete optsolver;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
{
// Translations in 1D, 2D, and 3D
switch (dim)
{
case 1: v(0) = (invert_velocity) ? -1.0 : 1.0; break;
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
break;
}
break;
}
case 1:
case 2:
{
// Clockwise rotation in 2D around the origin
const double w = M_PI/2;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
}
break;
}
case 3:
{
// Clockwise twisting rotation in 2D around the origin
const double w = M_PI/2;
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
d = d*d;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
}
break;
}
}
}
// Initial condition
double u0_function(const Vector &x)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
case 1:
{
switch (dim)
{
case 1:
return (X(0) > -0.15 && X(0) < 0.15) ? 1.0 : 0.0;
//return exp(-40.*pow(X(0)-0.0,2));
case 2:
case 3:
{
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
if (dim == 3)
{
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
rx *= s;
ry *= s;
}
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
}
}
}
case 2:
{
double x_ = X(0), y_ = X(1), rho, phi;
rho = hypot(x_, y_);
phi = atan2(y_, x_);
return pow(sin(M_PI*rho),2)*sin(3*phi);
}
case 3:
{
const double f = M_PI;
return sin(f*X(0))*sin(f*X(1));
}
}
return 0.0;
}
// Inflow boundary condition (zero for the problems considered in this example)
double inflow_function(const Vector &x)
{
switch (problem)
{
case 0:
case 1:
case 2:
case 3: return 0.0;
}
return 0.0;
}
-802
View File
@@ -1,802 +0,0 @@
// MFEM Example 9 with Nonlinear Constrained Optimization - Parallel Version
//
// Compile with: make ex9p
//
// Sample runs:
//
// mpirun -np 4 ex9p -m ../../data/periodic-segment.mesh -rs 3 -p 0 -o 2 -dt 0.002 -opt 1
// mpirun -np 4 ex9p -m ../../data/periodic-segment.mesh -rs 3 -p 0 -o 2 -dt 0.002 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 0 -rs 2 -dt 0.01 -tf 10 -opt 1
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 0 -rs 2 -dt 0.01 -tf 10 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 1 -rs 2 -dt 0.005 -tf 9 -opt 1
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 1 -rs 2 -dt 0.005 -tf 9 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/amr-quad.mesh -p 1 -rs 1 -dt 0.002 -tf 9 -opt 1
// mpirun -np 4 ex9p -m ../../data/amr-quad.mesh -p 1 -rs 1 -dt 0.002 -tf 9 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 1 -rs 2 -dt 0.005 -tf 9 -opt 1
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 1 -rs 2 -dt 0.005 -tf 9 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 2 -rs 2 -dt 0.01 -tf 9 -opt 1
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 2 -rs 2 -dt 0.01 -tf 9 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 3 -rs 3 -dt 0.0025 -tf 9 -opt 1
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 3 -rs 3 -dt 0.0025 -tf 9 -opt 2
//
// mpirun -np 4 ex9p -m ../../data/periodic-cube.mesh -p 0 -rs 2 -o 2 -dt 0.02 -tf 8 -opt 1
// mpirun -np 4 ex9p -m ../../data/periodic-cube.mesh -p 0 -rs 2 -o 2 -dt 0.02 -tf 8 -opt 2
// Description: This example modifies the standard MFEM ex9 by adding nonlinear
// constrained optimization capabilities through the SLBQP and
// HIOP solvers. It demonstrates how a user can define a custom
// class OptimizationProblem that includes linear/nonlinear
// equality/inequality constraints. This optimization is applied
// as post-processing to the solution of the transport equation.
//
// Description of ex9:
// This example code solves the time-dependent advection equation
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
// u0(x)=u(0,x) is a given initial condition.
//
// The example demonstrates the use of Discontinuous Galerkin (DG)
// bilinear forms in MFEM (face integrators), the use of explicit
// ODE time integrators, the definition of periodic boundary
// conditions through periodic meshes, as well as the use of GLVis
// for persistent visualization of a time-evolving solution. The
// saving of time-dependent data files for external visualization
// with VisIt (visit.llnl.gov) is also illustrated.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Choice for the problem setup. The fluid velocity, initial condition and
// inflow boundary condition are chosen based on this parameter.
int problem;
// Nonlinear optimizer.
int optimizer_type;
// Velocity coefficient
bool invert_velocity = false;
void velocity_function(const Vector &x, Vector &v);
// Initial condition
double u0_function(const Vector &x);
// Inflow boundary condition
double inflow_function(const Vector &x);
// Mesh bounding box
Vector bb_min, bb_max;
/// Computes C(x) = sum w_i x_i, where w is a given Vector.
class LinearScaleOperator : public Operator
{
private:
ParFiniteElementSpace &pfes;
// Local weights.
const Vector &w;
// Gradient for the tdofs.
mutable DenseMatrix grad;
public:
LinearScaleOperator(ParFiniteElementSpace &space, const Vector &weight)
: Operator(1, space.TrueVSize()),
pfes(space), w(weight), grad(1, width)
{
Vector w_glob(width);
pfes.Dof_TrueDof_Matrix()->MultTranspose(w, w_glob);
for (int i = 0; i < width; i++) { grad(0, i) = w_glob(i); }
}
virtual void Mult(const Vector &x, Vector &y) const
{
Vector x_loc(w.Size());
pfes.GetProlongationMatrix()->Mult(x, x_loc);
const double loc_res = w * x_loc;
MPI_Allreduce(&loc_res, &y(0), 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
}
virtual Operator &GetGradient(const Vector &x) const
{
return grad;
}
};
/// Nonlinear monotone bounded operator to test nonlinear ineq constraints.
/// Computes D(x) = tanh(sum(x_i)).
class TanhSumOperator : public Operator
{
private:
// Gradient for the tdofs.
mutable DenseMatrix grad;
public:
TanhSumOperator(ParFiniteElementSpace &space)
: Operator(1, space.TrueVSize()), grad(1, width) { }
virtual void Mult(const Vector &x, Vector &y) const
{
double sum_loc = x.Sum();
MPI_Allreduce(&sum_loc, &y(0), 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
y(0) = std::tanh(y(0));
}
virtual Operator &GetGradient(const Vector &x) const
{
double sum_loc = x.Sum();
double dtanh;
MPI_Allreduce(&sum_loc, &dtanh, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
dtanh = 1.0 - pow(std::tanh(dtanh), 2);
for (int i = 0; i < width; i++) { grad(0, i) = dtanh; }
return grad;
}
};
/** Monotone and conservative a-posteriori correction for transport solutions:
* Find x that minimizes 0.5 || x - x_HO ||^2, subject to
* sum w_i x_i = mass,
* tanh(sum(x_i_min)) <= tanh(sum(x_i)) <= tanh(sum(x_i_max)),
* x_i_min <= x_i <= x_i_max,
*/
class OptimizedTransportProblem : public OptimizationProblem
{
private:
const Vector &x_HO;
Vector massvec, d_lo, d_hi;
const LinearScaleOperator LSoper;
const TanhSumOperator TSoper;
public:
OptimizedTransportProblem(ParFiniteElementSpace &space,
const Vector &xho, const Vector &w, double mass,
const Vector &xmin, const Vector &xmax)
: OptimizationProblem(xho.Size(), NULL, NULL),
x_HO(xho), massvec(1), d_lo(1), d_hi(1),
LSoper(space, w), TSoper(space)
{
C = &LSoper;
massvec(0) = mass;
SetEqualityConstraint(massvec);
D = &TSoper;
double lsums[2], gsums[2];
lsums[0] = xmin.Sum();
lsums[1] = xmax.Sum();
MPI_Allreduce(lsums, gsums, 2, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
d_lo(0) = std::tanh(gsums[0]);
d_hi(0) = std::tanh(gsums[1]);
MFEM_ASSERT(d_lo(0) < d_hi(0),
"The bounds produce an infeasible optimization problem");
SetInequalityConstraint(d_lo, d_hi);
SetSolutionBounds(xmin, xmax);
}
virtual double CalcObjective(const Vector &x) const
{
double loc_res = 0.0;
for (int i = 0; i < input_size; i++)
{
const double d = x(i) - x_HO(i);
loc_res += d * d;
}
loc_res *= 0.5;
double res;
MPI_Allreduce(&loc_res, &res, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
return res;
}
virtual void CalcObjectiveGrad(const Vector &x, Vector &grad) const
{
for (int i = 0; i < input_size; i++) { grad(i) = x(i) - x_HO(i); }
}
};
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass
and advection matrices, and b describes the flow on the boundary. This can
be written as a general ODE, du/dt = M^{-1} (K u + b), and this class is
used to evaluate the right-hand side. */
class FE_Evolution : public TimeDependentOperator
{
private:
HypreParMatrix &M, &K;
const Vector &b;
HypreSmoother M_prec;
CGSolver M_solver;
mutable Vector z;
double dt;
ParBilinearForm &pbf;
Vector &M_rowsums;
public:
FE_Evolution(HypreParMatrix &_M, HypreParMatrix &_K,
const Vector &_b, ParBilinearForm &_pbf, Vector &M_rs);
void SetTimeStep(double _dt) { dt = _dt; }
void SetK(HypreParMatrix &_K) { K = _K; }
virtual void Mult(const Vector &x, Vector &y) const;
virtual ~FE_Evolution() { }
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
problem = 0;
optimizer_type = 1;
const char *mesh_file = "../../data/periodic-hexagon.mesh";
int ser_ref_levels = 2;
int par_ref_levels = 0;
int order = 3;
int ode_solver_type = 3;
double t_final = 1.0;
double dt = 0.01;
bool visualization = true;
bool visit = false;
bool binary = false;
int vis_steps = 5;
int precision = 8;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use. See options in velocity_function().");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&optimizer_type, "-opt", "--optimizer",
"Nonlinear optimizer: 1 - SLBQP,\n\t"
" 2 - HIOP.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.Parse();
if (!args.Good())
{
if (myid == 0) { args.PrintUsage(cout); }
MPI_Finalize();
return 1;
}
if (myid == 0) { args.PrintOptions(cout); }
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
ODESolver *ode_solver = NULL;
switch (ode_solver_type)
{
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(1.0); break;
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
case 6: ode_solver = new RK6Solver; break;
default:
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
delete mesh;
MPI_Finalize();
return 3;
}
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter. If the mesh is of NURBS type, we convert it
// to a (piecewise-polynomial) high-order mesh.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
if (mesh->NURBSext)
{
mesh->SetCurvature(max(order, 1));
}
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 7. Define the parallel discontinuous DG finite element space on the
// parallel refined mesh of the given polynomial order.
DG_FECollection fec(order, dim, BasisType::Positive);
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of unknowns: " << global_vSize << endl;
}
// 8. Set up and assemble the parallel bilinear and linear forms (and the
// parallel hypre matrices) corresponding to the DG discretization. The
// DGTraceIntegrator involves integrals over mesh interior faces.
VectorFunctionCoefficient velocity(dim, velocity_function);
FunctionCoefficient inflow(inflow_function);
FunctionCoefficient u0(u0_function);
ParBilinearForm *m = new ParBilinearForm(fes);
m->AddDomainIntegrator(new MassIntegrator);
ParBilinearForm *k = new ParBilinearForm(fes);
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k->AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
k->AddBdrFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
ParLinearForm *b = new ParLinearForm(fes);
b->AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
m->Assemble();
m->Finalize();
int skip_zeros = 0;
k->Assemble(skip_zeros);
k->Finalize(skip_zeros);
b->Assemble();
HypreParMatrix *M = m->ParallelAssemble();
HypreParMatrix *K = k->ParallelAssemble();
HypreParVector *B = b->ParallelAssemble();
// 9. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
// GLVis visualization.
ParGridFunction *u = new ParGridFunction(fes);
u->ProjectCoefficient(u0);
HypreParVector *U = u->GetTrueDofs();
{
ostringstream mesh_name, sol_name;
mesh_name << "ex9-mesh." << setfill('0') << setw(6) << myid;
sol_name << "ex9-init." << setfill('0') << setw(6) << myid;
ofstream omesh(mesh_name.str().c_str());
omesh.precision(precision);
pmesh->Print(omesh);
ofstream osol(sol_name.str().c_str());
osol.precision(precision);
u->Save(osol);
}
// Create data collection for solution output: either VisItDataCollection for
// ascii data files, or SidreDataCollection for binary data files.
DataCollection *dc = NULL;
if (visit)
{
if (binary)
{
#ifdef MFEM_USE_SIDRE
dc = new SidreDataCollection("Example9-Parallel", pmesh);
#else
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
#endif
}
else
{
dc = new VisItDataCollection("Example9-Parallel", pmesh);
dc->SetPrecision(precision);
// To save the mesh using MFEM's parallel mesh format:
// dc->SetFormat(DataCollection::PARALLEL_FORMAT);
}
dc->RegisterField("solution", u);
dc->SetCycle(0);
dc->SetTime(0.0);
dc->Save();
}
socketstream sout;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
sout.open(vishost, visport);
if (!sout)
{
if (myid == 0)
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
visualization = false;
if (myid == 0)
{
cout << "GLVis visualization disabled.\n";
}
}
else
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout.precision(precision);
sout << "solution\n" << *pmesh << *u;
sout << "pause\n";
sout << flush;
if (myid == 0)
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
Vector M_rowsums(m->Size());
m->SpMat().GetRowSums(M_rowsums);
// 10. Define the time-dependent evolution operator describing the ODE
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(*M, *K, *B, *k, M_rowsums);
double t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
*u = *U;
// Compute initial volume.
const double vol0_loc = M_rowsums * (*u);
double vol0;
MPI_Allreduce(&vol0_loc, &vol0, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
bool done = false;
for (int ti = 0; !done; )
{
double dt_real = min(dt, t_final - t);
adv.SetTimeStep(dt_real);
ode_solver->Step(*U, t, dt_real);
ti++;
done = (t >= t_final - 1e-8*dt);
if (done || ti % vis_steps == 0)
{
if (myid == 0)
{
cout << "time step: " << ti << ", time: " << t << endl;
}
// 11. Extract the parallel grid function corresponding to the finite
// element approximation U (the local solution on each processor).
*u = *U;
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << *u << flush;
}
if (visit)
{
dc->SetCycle(ti);
dc->SetTime(t);
dc->Save();
}
}
}
// Print the error vs exact solution.
const double max_error = u->ComputeMaxError(u0),
l1_error = u->ComputeL1Error(u0),
l2_error = u->ComputeL2Error(u0);
if (myid == 0)
{
std::cout << "Linf error = " << max_error << endl
<< "L1 error = " << l1_error << endl
<< "L2 error = " << l2_error << endl;
}
// Print error in volume.
const double vol_loc = M_rowsums * (*u);
double vol;
MPI_Allreduce(&vol_loc, &vol, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
if (myid == 0)
{
std::cout << "Vol error = " << vol - vol0 << endl;
}
// 12. Save the final solution in parallel. This output can be viewed later
// using GLVis: "glvis -np <np> -m ex9-mesh -g ex9-final".
{
*u = *U;
ostringstream sol_name;
sol_name << "ex9-final." << setfill('0') << setw(6) << myid;
ofstream osol(sol_name.str().c_str());
osol.precision(precision);
u->Save(osol);
}
// 13. Free the used memory.
delete U;
delete u;
delete B;
delete b;
delete K;
delete k;
delete M;
delete m;
delete fes;
delete pmesh;
delete ode_solver;
delete dc;
MPI_Finalize();
return 0;
}
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(HypreParMatrix &_M, HypreParMatrix &_K,
const Vector &_b, ParBilinearForm &_pbf,
Vector &M_rs)
: TimeDependentOperator(_M.Height()),
M(_M), K(_K), b(_b), M_solver(M.GetComm()), z(_M.Height()),
pbf(_pbf), M_rowsums(M_rs)
{
M_prec.SetType(HypreSmoother::Jacobi);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(M);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
}
void FE_Evolution::Mult(const Vector &x, Vector &y) const
{
// Get values on the ldofs.
ParFiniteElementSpace *pfes = pbf.ParFESpace();
ParGridFunction x_gf(pfes);
pfes->GetProlongationMatrix()->Mult(x, x_gf);
// Compute bounds y_min, y_max for y from from x on the ldofs.
const int ldofs = x_gf.Size();
Vector y_min(ldofs), y_max(ldofs);
x_gf.ExchangeFaceNbrData();
Vector &x_nd = x_gf.FaceNbrData();
const int *In = pbf.SpMat().GetI(), *Jn = pbf.SpMat().GetJ();
for (int i = 0, k = 0; i < ldofs; i++)
{
double x_i_min = +std::numeric_limits<double>::infinity();
double x_i_max = -std::numeric_limits<double>::infinity();
for (int end = In[i+1]; k < end; k++)
{
const int j = Jn[k];
const double x_j = (j < ldofs) ? x(j): x_nd(j-ldofs);
if (x_j > x_i_max) { x_i_max = x_j; }
if (x_j < x_i_min) { x_i_min = x_j; }
}
y_min(i) = x_i_min;
y_max(i) = x_i_max;
}
for (int i = 0; i < ldofs; i++)
{
y_min(i) = (y_min(i) - x_gf(i) ) / dt;
y_max(i) = (y_max(i) - x_gf(i) ) / dt;
}
Vector y_min_tdofs(y.Size()), y_max_tdofs(y.Size());
// Move the bounds to the tdofs.
pfes->GetRestrictionMatrix()->Mult(y_min, y_min_tdofs);
pfes->GetRestrictionMatrix()->Mult(y_max, y_max_tdofs);
// Compute the high-order solution y = M^{-1} (K x + b) on the tdofs.
K.Mult(x, z);
z += b;
M_solver.Mult(z, y);
// The solution y is an increment; it should not introduce new mass.
const double mass_y = 0.0;
// Perform optimization on the tdofs.
Vector y_out(y.Size());
const int max_iter = 500;
const double rtol = 1.e-7;
double atol = 1.e-7;
OptimizationSolver* optsolver = NULL;
if (optimizer_type == 2)
{
#ifdef MFEM_USE_HIOP
HiopNlpOptimizer *tmp_opt_ptr = new HiopNlpOptimizer(MPI_COMM_WORLD);
optsolver = tmp_opt_ptr;
#else
MFEM_ABORT("MFEM is not built with HiOp support!");
#endif
}
else
{
SLBQPOptimizer *slbqp = new SLBQPOptimizer(MPI_COMM_WORLD);
slbqp->SetBounds(y_min_tdofs, y_max_tdofs);
slbqp->SetLinearConstraint(M_rowsums, mass_y);
atol = 1.e-15;
optsolver = slbqp;
}
OptimizedTransportProblem ot_prob(*pfes, y, M_rowsums, mass_y,
y_min_tdofs, y_max_tdofs);
optsolver->SetOptimizationProblem(ot_prob);
optsolver->SetMaxIter(max_iter);
optsolver->SetAbsTol(atol);
optsolver->SetRelTol(rtol);
optsolver->SetPrintLevel(0);
optsolver->Mult(y, y_out);
y = y_out;
delete optsolver;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
{
// Translations in 1D, 2D, and 3D
switch (dim)
{
case 1: v(0) = (invert_velocity) ? -1.0 : 1.0; break;
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
break;
}
break;
}
case 1:
case 2:
{
// Clockwise rotation in 2D around the origin
const double w = M_PI/2;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
}
break;
}
case 3:
{
// Clockwise twisting rotation in 2D around the origin
const double w = M_PI/2;
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
d = d*d;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
}
break;
}
}
}
// Initial condition
double u0_function(const Vector &x)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
case 1:
{
switch (dim)
{
case 1:
return (X(0) > -0.15 && X(0) < 0.15) ? 1.0 : 0.0;
//return exp(-40.*pow(X(0)-0.0,2));
case 2:
case 3:
{
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
if (dim == 3)
{
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
rx *= s;
ry *= s;
}
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
}
}
}
case 2:
{
double x_ = X(0), y_ = X(1), rho, phi;
rho = hypot(x_, y_);
phi = atan2(y_, x_);
return pow(sin(M_PI*rho),2)*sin(3*phi);
}
case 3:
{
const double f = M_PI;
return sin(f*X(0))*sin(f*X(1));
}
}
return 0.0;
}
// Inflow boundary condition (zero for the problems considered in this example)
double inflow_function(const Vector &x)
{
switch (problem)
{
case 0:
case 1:
case 2:
case 3: return 0.0;
}
return 0.0;
}
-66
View File
@@ -1,66 +0,0 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/hiop/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ex9
PAR_EXAMPLES = ex9p
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
ifeq ($(MFEM_USE_HIOP),NO)
$(EXAMPLES):
$(error MFEM is not configured with HIOP)
endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.* Example9*
+2 -9
View File
@@ -41,13 +41,6 @@ endif
ifeq ($(MFEM_USE_PUMI),YES)
SUBDIRS += pumi
endif
ifeq ($(MFEM_USE_HIOP),YES)
SUBDIRS += hiop
endif
ifeq ($(MFEM_USE_GINKGO),YES)
SUBDIRS += ginkgo
endif
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
SUBDIRS_CLEAN = $(addsuffix /clean,$(SUBDIRS))
@@ -124,7 +117,7 @@ clean-build:
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
@rm -rf Example5* Example9* Example15* Example16* PVExample*
@rm -rf Example5* Example9* Example15* Example16*
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f deformed.* velocity.* elastic_energy.* mode_*
@@ -132,4 +125,4 @@ clean-exec:
@rm -f vortex-mesh.* vortex.mesh vortex-?-init.* vortex-?-final.*
@rm -f deformation.* pressure.*
@rm -f ex20.dat ex20p_?????.dat gnuplot_ex20.inp gnuplot_ex20p.inp
@rm -f ex21*.mesh ex21*.sol ex21p_*.*
@rm -f ex22*.mesh ex22*.sol ex22p_*.*
+9 -58
View File
@@ -27,11 +27,8 @@
// method HyperelasticOperator::ImplicitSolve is the only
// requirement for high-order implicit (SDIRK) time integration.
// If using PETSc to solve the nonlinear problem, use the option
// files provided (see rc_ex10p, rc_ex10p_mf, rc_ex10p_mfop) that
// customize the Newton-Krylov method.
// When option --jfnk is used, PETSc will use a Jacobian-free
// Newton-Krylov method, using a user-defined preconditioner
// constructed with the PetscPreconditionerFactory class.
// file provided (rc_ex10p) that customizes the
// Newton-Krylov method.
//
// We recommend viewing examples 2 and 9 before viewing this
// example.
@@ -89,15 +86,12 @@ protected:
Solver *J_solver;
/// Preconditioner for the Jacobian solve in the Newton method
Solver *J_prec;
/// Preconditioner factory for JFNK
PetscPreconditionerFactory *J_factory;
mutable Vector z; // auxiliary vector
public:
HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
double visc, double mu, double K,
bool use_petsc, bool petsc_use_jfnk);
double visc, double mu, double K, bool use_petsc);
/// Compute the right-hand side of the ODE system.
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
@@ -142,21 +136,8 @@ public:
virtual Operator &GetGradient(const Vector &k) const;
virtual ~ReducedSystemOperator();
};
/** Auxiliary class to provide preconditioners for matrix-free methods */
class PreconditionerFactory : public PetscPreconditionerFactory
{
private:
// const ReducedSystemOperator& op; // unused for now (generates warning)
public:
PreconditionerFactory(const ReducedSystemOperator& op_, const string& name_)
: PetscPreconditionerFactory(name_) /* , op(op_) */ {}
virtual mfem::Solver* NewPreconditioner(const mfem::OperatorHandle&);
virtual ~PreconditionerFactory() {}
};
/** Function representing the elastic energy density for the given hyperelastic
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
@@ -206,7 +187,6 @@ int main(int argc, char *argv[])
int vis_steps = 1;
bool use_petsc = true;
const char *petscrc_file = "";
bool petsc_use_jfnk = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -241,9 +221,6 @@ int main(int argc, char *argv[])
"Use or not PETSc to solve the nonlinear system.");
args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
"PetscOptions file to use.");
args.AddOption(&petsc_use_jfnk, "-jfnk", "--jfnk", "-no-jfnk",
"--no-jfnk",
"Use JFNK with user-defined preconditioner factory.");
args.Parse();
if (!args.Good())
{
@@ -367,8 +344,7 @@ int main(int argc, char *argv[])
// 9. Initialize the hyperelastic operator, the GLVis visualization and print
// the initial energies.
HyperelasticOperator *oper = new HyperelasticOperator(fespace, ess_bdr, visc,
mu, K, use_petsc,
petsc_use_jfnk);
mu, K, use_petsc);
socketstream vis_v, vis_w;
if (visualization)
@@ -544,7 +520,7 @@ Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
add(*v, dt, k, w);
add(*x, dt, w, z);
localJ->Add(dt*dt, H->GetLocalGradient(z));
// if we are using PETSc, the HypreParCSR Jacobian will be converted to
// if we are using PETSc, the HypreParCSR jacobian will be converted to
// PETSc's AIJ on the fly
Jacobian = M->ParallelAssemble(localJ);
delete localJ;
@@ -561,8 +537,7 @@ ReducedSystemOperator::~ReducedSystemOperator()
HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
Array<int> &ess_bdr, double visc,
double mu, double K, bool use_petsc,
bool use_petsc_factory)
double mu, double K, bool use_petsc)
: TimeDependentOperator(2*f.TrueVSize(), 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), M_solver(f.GetComm()),
@@ -615,8 +590,6 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
J_minres->SetPreconditioner(*J_prec);
J_solver = J_minres;
J_factory = NULL;
newton_solver.iterative_mode = false;
newton_solver.SetSolver(*J_solver);
newton_solver.SetOperator(*reduced_oper);
@@ -627,20 +600,12 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
}
else
{
// if using PETSc, we create the same solver (Newton + MINRES + Jacobi)
// if using PETSc, we create the same solver (NEWTON+MINRES+Jacobi)
// by command line options (see rc_ex10p)
J_solver = NULL;
J_prec = NULL;
J_factory = NULL;
pnewton_solver = new PetscNonlinearSolver(f.GetComm(),
*reduced_oper);
// we can setup a factory to construct a "physics-based" preconditioner
if (use_petsc_factory)
{
J_factory = new PreconditionerFactory(*reduced_oper, "JFNK preconditioner");
pnewton_solver->SetPreconditionerFactory(J_factory);
}
pnewton_solver->SetPrintLevel(1); // print Newton iterations
pnewton_solver->SetRelTol(rel_tol);
pnewton_solver->SetAbsTol(0.0);
@@ -726,26 +691,12 @@ HyperelasticOperator::~HyperelasticOperator()
{
delete J_solver;
delete J_prec;
delete J_factory;
delete reduced_oper;
delete model;
delete Mmat;
delete pnewton_solver;
}
// This method gets called every time we need a preconditioner "oh"
// contains the PetscParMatrix that wraps the operator constructed in
// the GetGradient() method (see also PetscSolver::SetJacobianType()).
// In this example, we just return a customizable PetscPreconditioner
// using that matrix. However, the OperatorHandle argument can be
// ignored, and any "physics-based" solver can be constructed since we
// have access to the HyperElasticOperator class.
Solver* PreconditionerFactory::NewPreconditioner(const mfem::OperatorHandle& oh)
{
PetscParMatrix *pP;
oh.Get(pP);
return new PetscPreconditioner(*pP,"jfnk_");
}
double ElasticEnergyCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
@@ -759,8 +710,8 @@ double ElasticEnergyCoefficient::Eval(ElementTransformation &T,
void InitialDeformation(const Vector &x, Vector &y)
{
// set the initial configuration to be the same as the reference,
// stress free, configuration
// set the initial configuration to be the same as the reference, stress
// free, configuration
y = x;
}
-7
View File
@@ -84,10 +84,6 @@ EX9_E_ARGS := -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts r
EX9_ES_ARGS := -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts rc_ex9p_expl --no-step
EX9_IS_ARGS := -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts rc_ex9p_impl --implicit -tf 0.5
EX10_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p -tf 30 -s 3 -rs 2 -dt 3
EX10_MF_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_mf -tf 6 -s 3 -rs 0 -dt 3
EX10_MFOP_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_mfop -tf 6 -s 3 -rs 0 -dt 3
EX10_JFNK_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_jfnk --jfnk -tf 6 -s 3 -rs 0 -dt 3
ex1p-test-par: ex1p
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX1_ARGS_W))
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX1_ARGS_P))
@@ -111,9 +107,6 @@ ex9p-test-par: ex9p
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX9_IS_ARGS))
ex10p-test-par: ex10p
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_ARGS))
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_MF_ARGS))
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_MFOP_ARGS))
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_JFNK_ARGS))
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
-5
View File
@@ -1,5 +0,0 @@
# matrix-free Jacobian action, preconditioner constructed using PetscPreconditionerFactory
-snes_monitor
-snes_mf_operator
-ksp_type minres
-jfnk_pc_type jacobi
-4
View File
@@ -1,4 +0,0 @@
# matrix free -> no preconditioner
-snes_monitor
-snes_mf
-ksp_type minres
-5
View File
@@ -1,5 +0,0 @@
# matrix-free Jacobian action, preconditioner constructed from the matrix obtained by the GetGradient() method
-snes_monitor
-snes_mf_operator
-ksp_type minres
-pc_type jacobi
+4 -4
View File
@@ -42,12 +42,12 @@ add_mfem_examples(SUNDIALS_EXAMPLES_SRCS ${PFX} "" test_sundials)
# ctest -R sundials
# Command line options for the tests.
# Example 9: test CVODE with CV_ADAMS (non-stiff implicit) time stepping
set(EX9_COMMON_OPTS -m ../../data/periodic-hexagon.mesh -p 0 -s 7)
# Example 9: test explicit CVODE time stepping
set(EX9_COMMON_OPTS -m ../../data/periodic-hexagon.mesh -p 0 -s 11)
set(EX9_TEST_OPTS ${EX9_COMMON_OPTS} -r 2 -dt 0.0018 -vs 25)
set(EX9P_TEST_OPTS ${EX9_COMMON_OPTS} -rp 1 -dt 0.0009 -vs 50)
# Example 10: test CVODE with CV_BDF (stiff implicit) time stepping
set(EX10_COMMON_OPTS -m ../../data/beam-quad.mesh -o 2 -s 5 -dt 0.15 -tf 6 -vs 10)
# Example 10: test implicit CVODE time stepping
set(EX10_COMMON_OPTS -m ../../data/beam-quad.mesh -o 2 -s 5 -dt 0.15 -vs 10)
set(EX10_TEST_OPTS ${EX10_COMMON_OPTS} -r 2)
set(EX10P_TEST_OPTS ${EX10_COMMON_OPTS} -rp 1)
# Example 16: use the default options
+210 -204
View File
@@ -4,16 +4,16 @@
// Compile with: make ex10
//
// Sample runs:
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 12 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 16 -dt 0.3 -vs 5
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 12 -dt 0.2 -vs 5
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 5 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 7 -dt 0.3 -vs 5
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 5 -dt 0.2 -vs 5
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 2 -dt 3 -nls kinsol
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 2 -dt 3 -nls kinsol
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 2 -dt 3 -nls kinsol
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 14 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 17 -dt 0.01 -vs 30
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 14 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-quad-amr.mesh -r 2 -o 2 -s 12 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 15 -dt 5e-3 -vs 60
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 16 -dt 0.01 -vs 30
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 15 -dt 0.01 -vs 30
// ex10 -m ../../data/beam-quad-amr.mesh -r 2 -o 2 -s 5 -dt 0.15 -vs 10
//
// Description: This examples solves a time dependent nonlinear elasticity
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
@@ -53,6 +53,7 @@ using namespace std;
using namespace mfem;
class ReducedSystemOperator;
class SundialsJacSolver;
/** After spatial discretization, the hyperelastic model can be written as a
* system of ODEs:
@@ -91,17 +92,12 @@ protected:
mutable Vector z; // auxiliary vector
SparseMatrix *grad_H;
SparseMatrix *Jacobian;
double saved_gamma; // saved gamma value from implicit setup
public:
/// Solver type to use in the ImplicitSolve() method, used by SDIRK methods.
enum NonlinearSolverType
{
NEWTON = 0, ///< Use MFEM's plain NewtonSolver
KINSOL = 1 ///< Use SUNDIALS' KINSOL (through MFEM's class KINSolver)
KINSOL = 1 ///< Use SUNDIALS' KINSOL (through MFEM's class KinSolver)
};
HyperelasticOperator(FiniteElementSpace &f, Array<int> &ess_bdr,
@@ -110,41 +106,15 @@ public:
/// Compute the right-hand side of the ODE system.
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by HyperelasticOperator
M dv/dt = -(H(x) + S*v)
dx/dt = v,
this class facilitates the solution of linear systems of the form
(M + γS) yv + γJ yx = M bv, J=(dH/dx)(x)
- γ yv + yx = bx
for given bv, bx, x, and γ = GetTimeStep(). */
/** Linear solve applicable to the SUNDIALS format.
Solves (Mass - dt J) y = Mass b, where in our case:
Mass = | M 0 | J = | -S -grad_H | y = | v_hat | b = | b_v |
| 0 I | | I 0 | | x_hat | | b_x |
The result replaces the rhs b.
We substitute x_hat = b_x + dt v_hat and solve
(M + dt S + dt^2 grad_H) v_hat = M b_v - dt grad_H b_x. */
/** Setup the linear system. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSetup(const Vector &y, const Vector &fy,
int jok, int *jcur, double gamma);
/** Solve the linear system. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSolve(const Vector &b, Vector &x, double tol);
/** Connect the Jacobian linear system solver (SundialsJacSolver) used by
SUNDIALS' CVODE and ARKODE time integrators to the internal objects
created by HyperelasticOperator. This method is called by the InitSystem
method of SundialsJacSolver. */
void InitSundialsJacSolver(SundialsJacSolver &sjsolv);
double ElasticEnergy(const Vector &x) const;
double KineticEnergy(const Vector &v) const;
@@ -182,6 +152,53 @@ public:
virtual ~ReducedSystemOperator();
};
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by HyperelasticOperator
M dv/dt = -(H(x) + S*v)
dx/dt = v,
this class facilitates the solution of linear systems of the form
(M + γS) yv + γJ yx = M bv, J=(dH/dx)(x)
- γ yv + yx = bx
for given bv, bx, x, and γ = GetTimeStep(). */
class SundialsJacSolver : public SundialsODELinearSolver
{
private:
BilinearForm *M, *S;
NonlinearForm *H;
SparseMatrix *grad_H, *Jacobian;
Solver *J_solver;
public:
SundialsJacSolver()
: M(), S(), H(), grad_H(), Jacobian(), J_solver() { }
/// Connect the solver to the objects created inside HyperelasticOperator.
void SetOperators(BilinearForm &M_, BilinearForm &S_,
NonlinearForm &H_, Solver &solver)
{
M = &M_; S = &S_; H = &H_; J_solver = &solver;
}
/** Linear solve applicable to the SUNDIALS format.
Solves (Mass - dt J) y = Mass b, where in our case:
Mass = | M 0 | J = | -S -grad_H | y = | v_hat | b = | b_v |
| 0 I | | I 0 | | x_hat | | b_x |
The result replaces the rhs b.
We substitute x_hat = b_x + dt v_hat and solve
(M + dt S + dt^2 grad_H) v_hat = M b_v - dt grad_H b_x. */
int InitSystem(void *sundials_mem);
int SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred, int &jac_cur,
Vector &v_temp1, Vector &v_temp2, Vector &v_temp3);
int SolveSystem(void *sundials_mem, Vector &b, const Vector &weight,
const Vector &y_cur, const Vector &f_cur);
int FreeSystem(void *sundials_mem);
};
/** Function representing the elastic energy density for the given hyperelastic
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
@@ -226,12 +243,6 @@ int main(int argc, char *argv[])
// Relative and absolute tolerances for CVODE and ARKODE.
const double reltol = 1e-1, abstol = 1e-1;
// Since this example uses the loose tolerances defined above, it is
// necessary to lower the linear solver tolerance for CVODE which is relative
// to the above tolerances.
const double cvode_eps_lin = 1e-4;
// Similarly, the nonlinear tolerance for ARKODE needs to be tightened.
const double arkode_eps_nonlin = 1e-6;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -241,24 +252,15 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Backward Euler,\n\t"
"2 - SDIRK2, L-stable\n\t"
"3 - SDIRK3, L-stable\n\t"
"4 - Implicit Midpoint,\n\t"
"5 - SDIRK2, A-stable,\n\t"
"6 - SDIRK3, A-stable,\n\t"
"7 - Forward Euler,\n\t"
"8 - RK2,\n\t"
"9 - RK3 SSP,\n\t"
"10 - RK4,\n\t"
"11 - CVODE implicit BDF, approximate Jacobian,\n\t"
"12 - CVODE implicit BDF, specified Jacobian,\n\t"
"13 - CVODE implicit ADAMS, approximate Jacobian,\n\t"
"14 - CVODE implicit ADAMS, specified Jacobian,\n\t"
"15 - ARKODE implicit, approximate Jacobian,\n\t"
"16 - ARKODE implicit, specified Jacobian,\n\t"
"17 - ARKODE explicit, 4th order.");
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
" 4 - CVODE implicit, approximate Jacobian,\n\t"
" 5 - CVODE implicit, specified Jacobian,\n\t"
" 6 - ARKODE implicit, approximate Jacobian,\n\t"
" 7 - ARKODE implicit, specified Jacobian,\n\t"
" 11 - Forward Euler, 12 - RK2,\n\t"
" 13 - RK3 SSP, 14 - RK4,\n\t"
" 15 - CVODE (adaptive order) explicit,\n\t"
" 16 - ARKODE default (4th order) explicit.");
args.AddOption(&nls, "-nls", "--nonlinear-solver",
"Nonlinear systems solver: "
"\"newton\" (plain Newton) or \"kinsol\" (KINSOL).");
@@ -285,19 +287,72 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 17)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
return 1;
}
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Setup the nonlinear solver
// 3. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
ODESolver *ode_solver;
CVODESolver *cvode = NULL;
ARKODESolver *arkode = NULL;
SundialsJacSolver *sjsolver = NULL;
switch (ode_solver_type)
{
// Implicit L-stable methods
case 1: ode_solver = new BackwardEulerSolver; break;
case 2: ode_solver = new SDIRK23Solver(2); break;
case 3: ode_solver = new SDIRK33Solver; break;
case 4:
case 5:
cvode = new CVODESolver(CV_BDF, CV_NEWTON);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
if (ode_solver_type == 5)
{
sjsolver = new SundialsJacSolver;
cvode->SetLinearSolver(*sjsolver);
}
ode_solver = cvode; break;
case 6:
case 7:
arkode = new ARKODESolver(ARKODESolver::IMPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 7)
{
// Custom Jacobian inversion.
sjsolver = new SundialsJacSolver;
arkode->SetLinearSolver(*sjsolver);
}
ode_solver = arkode; break;
// Explicit methods
case 11: ode_solver = new ForwardEulerSolver; break;
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 13: ode_solver = new RK3SSPSolver; break;
case 14: ode_solver = new RK4Solver; break;
case 15:
cvode = new CVODESolver(CV_ADAMS, CV_FUNCTIONAL);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 16:
arkode = new ARKODESolver(ARKODESolver::IMPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
// Implicit A-stable methods (not L-stable)
case 22: ode_solver = new ImplicitMidpointSolver; break;
case 23: ode_solver = new SDIRK23Solver; break;
case 24: ode_solver = new SDIRK34Solver; break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
delete mesh;
return 3;
}
map<string,HyperelasticOperator::NonlinearSolverType> nls_map;
nls_map["newton"] = HyperelasticOperator::NEWTON;
nls_map["kinsol"] = HyperelasticOperator::KINSOL;
@@ -384,82 +439,11 @@ int main(int argc, char *argv[])
cout << "initial kinetic energy (KE) = " << ke0 << endl;
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
// 8. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
double t = 0.0;
oper.SetTime(t);
ode_solver->Init(oper);
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKStepSolver *arkode = NULL;
switch (ode_solver_type)
{
// Implicit L-stable methods
case 1: ode_solver = new BackwardEulerSolver; break;
case 2: ode_solver = new SDIRK23Solver(2); break;
case 3: ode_solver = new SDIRK33Solver; break;
// Implicit A-stable methods (not L-stable)
case 4: ode_solver = new ImplicitMidpointSolver; break;
case 5: ode_solver = new SDIRK23Solver; break;
case 6: ode_solver = new SDIRK34Solver; break;
// Explicit methods
case 7: ode_solver = new ForwardEulerSolver; break;
case 8: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 9: ode_solver = new RK3SSPSolver; break;
case 10: ode_solver = new RK4Solver; break;
// CVODE BDF
case 11:
case 12:
cvode = new CVODESolver(CV_BDF);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
if (ode_solver_type == 11)
{
cvode->UseSundialsLinearSolver();
}
ode_solver = cvode; break;
// CVODE Adams
case 13:
case 14:
cvode = new CVODESolver(CV_ADAMS);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
if (ode_solver_type == 13)
{
cvode->UseSundialsLinearSolver();
}
ode_solver = cvode; break;
// ARKStep Implicit methods
case 15:
case 16:
arkode = new ARKStepSolver(ARKStepSolver::IMPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
arkode->SetMaxStep(dt);
if (ode_solver_type == 15)
{
arkode->UseSundialsLinearSolver();
}
ode_solver = arkode; break;
// ARKStep Explicit methods
case 17:
arkode = new ARKStepSolver(ARKStepSolver::EXPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 11) { ode_solver->Init(oper); }
// 9. Perform time-integration (looping over the time iterations, ti, with a
// 8. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
bool last_step = false;
for (int ti = 1; !last_step; ti++)
@@ -494,7 +478,7 @@ int main(int argc, char *argv[])
}
}
// 10. Save the displaced mesh, the velocity and elastic energy.
// 9. Save the displaced mesh, the velocity and elastic energy.
{
v.SetFromTrueVector(); x.SetFromTrueVector();
GridFunction *nodes = &x;
@@ -513,8 +497,9 @@ int main(int argc, char *argv[])
w.Save(ee_ofs);
}
// 11. Free the used memory.
// 10. Free the used memory.
delete ode_solver;
delete sjsolver;
delete mesh;
return 0;
@@ -594,14 +579,81 @@ ReducedSystemOperator::~ReducedSystemOperator()
}
int SundialsJacSolver::InitSystem(void *sundials_mem)
{
TimeDependentOperator *td_oper = GetTimeDependentOperator(sundials_mem);
HyperelasticOperator *he_oper;
// During development, we use dynamic_cast<> to ensure the setup is correct:
he_oper = dynamic_cast<HyperelasticOperator*>(td_oper);
MFEM_VERIFY(he_oper, "operator is not HyperelasticOperator");
// When the implementation is finalized, we can switch to static_cast<>:
// he_oper = static_cast<HyperelasticOperator*>(td_oper);
he_oper->InitSundialsJacSolver(*this);
return 0;
}
int SundialsJacSolver::SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred,
int &jac_cur, Vector &v_temp1,
Vector &v_temp2, Vector &v_temp3)
{
int sc = y_pred.Size() / 2;
const Vector x(y_pred.GetData() + sc, sc);
double dt = GetTimeStep(sundials_mem);
// J = M + dt*(S + dt*grad(H))
delete Jacobian;
Jacobian = Add(1.0, M->SpMat(), dt, S->SpMat());
grad_H = dynamic_cast<SparseMatrix *>(&H->GetGradient(x));
Jacobian->Add(dt * dt, *grad_H);
J_solver->SetOperator(*Jacobian);
jac_cur = 1;
return 0;
}
int SundialsJacSolver::SolveSystem(void *sundials_mem, Vector &b,
const Vector &weight, const Vector &y_cur,
const Vector &f_cur)
{
int sc = b.Size() / 2;
// Vector x(y_cur.GetData() + sc, sc);
Vector b_v(b.GetData() + 0, sc);
Vector b_x(b.GetData() + sc, sc);
Vector rhs(sc);
double dt = GetTimeStep(sundials_mem);
// rhs = M b_v - dt*grad(H) b_x
grad_H->Mult(b_x, rhs);
rhs *= -dt;
M->AddMult(b_v, rhs);
J_solver->iterative_mode = false;
J_solver->Mult(rhs, b_v);
b_x.Add(dt, b_v);
return 0;
}
int SundialsJacSolver::FreeSystem(void *sundials_mem)
{
delete Jacobian;
return 0;
}
HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
Array<int> &ess_bdr, double visc,
double mu, double K,
NonlinearSolverType nls_type)
: TimeDependentOperator(2*f.GetTrueVSize(), 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), z(height/2),
grad_H(NULL), Jacobian(NULL)
viscosity(visc), z(height/2)
{
const double rel_tol = 1e-8;
const int skip_zero_entries = 0;
@@ -650,24 +702,23 @@ HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
if (nls_type == KINSOL)
{
KINSolver *kinsolver = new KINSolver(KIN_NONE, true);
KinSolver *kinsolver = new KinSolver(KIN_NONE, true);
kinsolver->SetMaxSetupCalls(4);
newton_solver = kinsolver;
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(200);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(0);
kinsolver->SetMaxSetupCalls(4);
}
else
{
newton_solver = new NewtonSolver();
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(10);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(-1);
}
newton_solver->SetSolver(*J_solver);
newton_solver->iterative_mode = false;
newton_solver->SetOperator(*reduced_oper);
}
void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
@@ -717,53 +768,9 @@ void HyperelasticOperator::ImplicitSolve(const double dt,
add(v, dt, dv_dt, dx_dt);
}
int HyperelasticOperator::SUNImplicitSetup(const Vector &y,
const Vector &fy, int jok, int *jcur,
double gamma)
void HyperelasticOperator::InitSundialsJacSolver(SundialsJacSolver &sjsolv)
{
int sc = y.Size() / 2;
const Vector x(y.GetData() + sc, sc);
// J = M + dt*(S + dt*grad(H))
if (Jacobian) { delete Jacobian; }
Jacobian = Add(1.0, M.SpMat(), gamma, S.SpMat());
grad_H = dynamic_cast<SparseMatrix *>(&H.GetGradient(x));
Jacobian->Add(gamma * gamma, *grad_H);
// Set Jacobian solve operator
J_solver->SetOperator(*Jacobian);
// Indicate that the Jacobian was updated
*jcur = 1;
// Save gamma for use in solve
saved_gamma = gamma;
// Return success
return 0;
}
int HyperelasticOperator::SUNImplicitSolve(const Vector &b, Vector &x,
double tol)
{
int sc = b.Size() / 2;
Vector b_v(b.GetData() + 0, sc);
Vector b_x(b.GetData() + sc, sc);
Vector x_v(x.GetData() + 0, sc);
Vector x_x(x.GetData() + sc, sc);
Vector rhs(sc);
// rhs = M b_v - dt*grad(H) b_x
grad_H->Mult(b_x, rhs);
rhs *= -saved_gamma;
M.AddMult(b_v, rhs);
J_solver->iterative_mode = false;
J_solver->Mult(rhs, x_v);
add(b_x, saved_gamma, x_v, x_x);
return 0;
sjsolv.SetOperators(M, S, H, *J_solver);
}
double HyperelasticOperator::ElasticEnergy(const Vector &x) const
@@ -785,7 +792,6 @@ void HyperelasticOperator::GetElasticEnergyDensity(
HyperelasticOperator::~HyperelasticOperator()
{
delete Jacobian;
delete newton_solver;
delete J_solver;
delete J_prec;
+229 -219
View File
@@ -4,16 +4,16 @@
// Compile with: make ex10p
//
// Sample runs:
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 12 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 16 -dt 0.25 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rp 0 -o 2 -s 12 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 5 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 7 -dt 0.25 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rp 0 -o 2 -s 5 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 2 -dt 3 -nls kinsol
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 2 -dt 3 -nls kinsol
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rs 1 -o 2 -s 2 -dt 3 -nls kinsol
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 14 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 17 -dt 5e-3 -vs 60
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rp 0 -o 2 -s 14 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-quad-amr.mesh -rp 1 -o 2 -s 12 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 15 -dt 3e-3 -vs 120
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 16 -dt 5e-3 -vs 60
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rp 0 -o 2 -s 15 -dt 5e-3 -vs 60
// mpirun -np 4 ex10p -m ../../data/beam-quad-amr.mesh -rp 1 -o 2 -s 5 -dt 0.15 -vs 10
//
// Description: This examples solves a time dependent nonlinear elasticity
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
@@ -53,6 +53,7 @@ using namespace std;
using namespace mfem;
class ReducedSystemOperator;
class SundialsJacSolver;
/** After spatial discretization, the hyperelastic model can be written as a
* system of ODEs:
@@ -93,17 +94,12 @@ protected:
mutable Vector z; // auxiliary vector
const SparseMatrix *local_grad_H;
HypreParMatrix *Jacobian;
double saved_gamma; // saved gamma value from implicit setup
public:
/// Solver type to use in the ImplicitSolve() method, used by SDIRK methods.
enum NonlinearSolverType
{
NEWTON = 0, ///< Use MFEM's plain NewtonSolver
KINSOL = 1 ///< Use SUNDIALS' KINSOL (through MFEM's class KINSolver)
KINSOL = 1 ///< Use SUNDIALS' KINSOL (through MFEM's class KinSolver)
};
HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
@@ -112,41 +108,15 @@ public:
/// Compute the right-hand side of the ODE system.
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by HyperelasticOperator
M dv/dt = -(H(x) + S*v)
dx/dt = v,
this class facilitates the solution of linear systems of the form
(M + γS) yv + γJ yx = M bv, J=(dH/dx)(x)
- γ yv + yx = bx
for given bv, bx, x, and γ = GetTimeStep(). */
/** Linear solve applicable to the SUNDIALS format.
Solves (Mass - dt J) y = Mass b, where in our case:
Mass = | M 0 | J = | -S -grad_H | y = | v_hat | b = | b_v |
| 0 I | | I 0 | | x_hat | | b_x |
The result replaces the rhs b.
We substitute x_hat = b_x + dt v_hat and solve
(M + dt S + dt^2 grad_H) v_hat = M b_v - dt grad_H b_x. */
/** Setup the linear system. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSetup(const Vector &y, const Vector &fy,
int jok, int *jcur, double gamma);
/** Solve the linear system. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSolve(const Vector &b, Vector &x, double tol);
/** Connect the Jacobian linear system solver (SundialsJacSolver) used by
SUNDIALS' CVODE and ARKODE time integrators to the internal objects
created by HyperelasticOperator. This method is called by the InitSystem
method of SundialsJacSolver. */
void InitSundialsJacSolver(SundialsJacSolver &sjsolv);
double ElasticEnergy(const ParGridFunction &x) const;
double KineticEnergy(const ParGridFunction &v) const;
@@ -187,6 +157,57 @@ public:
virtual ~ReducedSystemOperator();
};
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by HyperelasticOperator
M dv/dt = -(H(x) + S*v)
dx/dt = v,
this class facilitates the solution of linear systems of the form
(M + γS) yv + γJ yx = M bv, J=(dH/dx)(x)
- γ yv + yx = bx
for given bv, bx, x, and γ = GetTimeStep(). */
class SundialsJacSolver : public SundialsODELinearSolver
{
private:
ParBilinearForm *M, *S;
ParNonlinearForm *H;
const SparseMatrix *local_grad_H;
HypreParMatrix *Jacobian;
Solver *J_solver;
const Array<int> *ess_tdof_list;
public:
SundialsJacSolver()
: M(), S(), H(), local_grad_H(), Jacobian(), J_solver() { }
/// Connect the solver to the objects created inside HyperelasticOperator.
void SetOperators(ParBilinearForm &M_, ParBilinearForm &S_,
ParNonlinearForm &H_, Solver &solver,
const Array<int> &ess_tdof_list_)
{
M = &M_; S = &S_; H = &H_; J_solver = &solver;
ess_tdof_list = &ess_tdof_list_;
}
/** Linear solve applicable to the SUNDIALS format.
Solves (Mass - dt J) y = Mass b, where in our case:
Mass = | M 0 | J = | -S -grad_H | y = | v_hat | b = | b_v |
| 0 I | | I 0 | | x_hat | | b_x |
The result replaces the rhs b.
We substitute x_hat = b_x + dt v_hat and solve
(M + dt S + dt^2 grad_H) v_hat = M b_v - dt grad_H b_x. */
int InitSystem(void *sundials_mem);
int SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred, int &jac_cur,
Vector &v_temp1, Vector &v_temp2, Vector &v_temp3);
int SolveSystem(void *sundials_mem, Vector &b, const Vector &weight,
const Vector &y_cur, const Vector &f_cur);
int FreeSystem(void *sundials_mem);
};
/** Function representing the elastic energy density for the given hyperelastic
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
@@ -238,12 +259,6 @@ int main(int argc, char *argv[])
// Relative and absolute tolerances for CVODE and ARKODE.
const double reltol = 1e-1, abstol = 1e-1;
// Since this example uses the loose tolerances defined above, it is
// necessary to lower the linear solver tolerance for CVODE which is relative
// to the above tolerances.
const double cvode_eps_lin = 1e-4;
// Similarly, the nonlinear tolerance for ARKODE needs to be tightened.
const double arkode_eps_nonlin = 1e-6;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -255,24 +270,15 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Backward Euler,\n\t"
"2 - SDIRK2, L-stable\n\t"
"3 - SDIRK3, L-stable\n\t"
"4 - Implicit Midpoint,\n\t"
"5 - SDIRK2, A-stable,\n\t"
"6 - SDIRK3, A-stable,\n\t"
"7 - Forward Euler,\n\t"
"8 - RK2,\n\t"
"9 - RK3 SSP,\n\t"
"10 - RK4,\n\t"
"11 - CVODE implicit BDF, approximate Jacobian,\n\t"
"12 - CVODE implicit BDF, specified Jacobian,\n\t"
"13 - CVODE implicit ADAMS, approximate Jacobian,\n\t"
"14 - CVODE implicit ADAMS, specified Jacobian,\n\t"
"15 - ARKODE implicit, approximate Jacobian,\n\t"
"16 - ARKODE implicit, specified Jacobian,\n\t"
"17 - ARKODE explicit, 4th order.");
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
" 4 - CVODE implicit, approximate Jacobian,\n\t"
" 5 - CVODE implicit, specified Jacobian,\n\t"
" 6 - ARKODE implicit, approximate Jacobian,\n\t"
" 7 - ARKODE implicit, specified Jacobian,\n\t"
" 11 - Forward Euler, 12 - RK2,\n\t"
" 13 - RK3 SSP, 14 - RK4,\n\t"
" 15 - CVODE (adaptive order) explicit,\n\t"
" 16 - ARKODE default (4th order) explicit.");
args.AddOption(&nls, "-nls", "--nonlinear-solver",
"Nonlinear systems solver: "
"\"newton\" (plain Newton) or \"kinsol\" (KINSOL).");
@@ -306,24 +312,76 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 17)
{
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
MPI_Finalize();
return 1;
}
// 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.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Nonlinear solver
// 4. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
ODESolver *ode_solver;
CVODESolver *cvode = NULL;
ARKODESolver *arkode = NULL;
SundialsJacSolver *sjsolver = NULL;
switch (ode_solver_type)
{
// Implicit L-stable methods
case 1: ode_solver = new BackwardEulerSolver; break;
case 2: ode_solver = new SDIRK23Solver(2); break;
case 3: ode_solver = new SDIRK33Solver; break;
case 4:
case 5:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_BDF, CV_NEWTON);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
if (ode_solver_type == 5)
{
sjsolver = new SundialsJacSolver;
cvode->SetLinearSolver(*sjsolver); // Custom Jacobian inversion.
}
ode_solver = cvode; break;
case 6:
case 7:
arkode = new ARKODESolver(MPI_COMM_WORLD, ARKODESolver::IMPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 7)
{
sjsolver = new SundialsJacSolver;
arkode->SetLinearSolver(*sjsolver); // Custom Jacobian inversion.
}
ode_solver = arkode; break;
// Explicit methods
case 11: ode_solver = new ForwardEulerSolver; break;
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 13: ode_solver = new RK3SSPSolver; break;
case 14: ode_solver = new RK4Solver; break;
case 15:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS, CV_FUNCTIONAL);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 16:
arkode = new ARKODESolver(MPI_COMM_WORLD, ARKODESolver::EXPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
// Implicit A-stable methods (not L-stable)
case 22: ode_solver = new ImplicitMidpointSolver; break;
case 23: ode_solver = new SDIRK23Solver; break;
case 24: ode_solver = new SDIRK34Solver; break;
default:
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
delete mesh;
MPI_Finalize();
return 3;
}
map<string,HyperelasticOperator::NonlinearSolverType> nls_map;
nls_map["newton"] = HyperelasticOperator::NEWTON;
nls_map["kinsol"] = HyperelasticOperator::KINSOL;
@@ -333,6 +391,7 @@ int main(int argc, char *argv[])
{
cout << "Unknown type of nonlinear solver: " << nls << endl;
}
delete ode_solver;
delete mesh;
MPI_Finalize();
return 4;
@@ -436,82 +495,11 @@ int main(int argc, char *argv[])
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
}
// 10. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
double t = 0.0;
oper.SetTime(t);
ode_solver->Init(oper);
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKStepSolver *arkode = NULL;
switch (ode_solver_type)
{
// Implicit L-stable methods
case 1: ode_solver = new BackwardEulerSolver; break;
case 2: ode_solver = new SDIRK23Solver(2); break;
case 3: ode_solver = new SDIRK33Solver; break;
// Implicit A-stable methods (not L-stable)
case 4: ode_solver = new ImplicitMidpointSolver; break;
case 5: ode_solver = new SDIRK23Solver; break;
case 6: ode_solver = new SDIRK34Solver; break;
// Explicit methods
case 7: ode_solver = new ForwardEulerSolver; break;
case 8: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 9: ode_solver = new RK3SSPSolver; break;
case 10: ode_solver = new RK4Solver; break;
// CVODE BDF
case 11:
case 12:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_BDF);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
if (ode_solver_type == 11)
{
cvode->UseSundialsLinearSolver();
}
ode_solver = cvode; break;
// CVODE Adams
case 13:
case 14:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
if (ode_solver_type == 13)
{
cvode->UseSundialsLinearSolver();
}
ode_solver = cvode; break;
// ARKStep Implicit methods
case 15:
case 16:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::IMPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
arkode->SetMaxStep(dt);
if (ode_solver_type == 15)
{
arkode->UseSundialsLinearSolver();
}
ode_solver = arkode; break;
// ARKStep Explicit methods
case 17:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::EXPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 11) { ode_solver->Init(oper); }
// 11. Perform time-integration
// 10. Perform time-integration
// (looping over the time iterations, ti, with a time-step dt).
bool last_step = false;
for (int ti = 1; !last_step; ti++)
@@ -550,7 +538,7 @@ int main(int argc, char *argv[])
}
}
// 12. Save the displaced mesh, the velocity and elastic energy.
// 11. Save the displaced mesh, the velocity and elastic energy.
{
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
GridFunction *nodes = &x_gf;
@@ -575,8 +563,9 @@ int main(int argc, char *argv[])
w_gf.Save(ee_ofs);
}
// 13. Free the used memory.
// 12. Free the used memory.
delete ode_solver;
delete sjsolver;
delete pmesh;
MPI_Finalize();
@@ -664,14 +653,92 @@ ReducedSystemOperator::~ReducedSystemOperator()
}
int SundialsJacSolver::InitSystem(void *sundials_mem)
{
TimeDependentOperator *td_oper = GetTimeDependentOperator(sundials_mem);
HyperelasticOperator *he_oper;
// During development, we use dynamic_cast<> to ensure the setup is correct:
he_oper = dynamic_cast<HyperelasticOperator*>(td_oper);
MFEM_VERIFY(he_oper, "operator is not HyperelasticOperator");
// When the implementation is finalized, we can switch to static_cast<>:
// he_oper = static_cast<HyperelasticOperator*>(td_oper);
he_oper->InitSundialsJacSolver(*this);
return 0;
}
int SundialsJacSolver::SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred,
int &jac_cur, Vector &v_temp1,
Vector &v_temp2, Vector &v_temp3)
{
int sc = y_pred.Size() / 2;
const Vector x(y_pred.GetData() + sc, sc);
double dt = GetTimeStep(sundials_mem);
// J = M + dt*(S + dt*grad(H))
delete Jacobian;
SparseMatrix *localJ = Add(1.0, M->SpMat(), dt, S->SpMat());
local_grad_H = &H->GetLocalGradient(x);
localJ->Add(dt*dt, *local_grad_H);
Jacobian = M->ParallelAssemble(localJ);
delete localJ;
HypreParMatrix *Je = Jacobian->EliminateRowsCols(*ess_tdof_list);
delete Je;
J_solver->SetOperator(*Jacobian);
jac_cur = 1;
return 0;
}
int SundialsJacSolver::SolveSystem(void *sundials_mem, Vector &b,
const Vector &weight, const Vector &y_cur,
const Vector &f_cur)
{
int sc = b.Size() / 2;
ParFiniteElementSpace *fes = H->ParFESpace();
// Vector x(y_cur.GetData() + sc, sc);
Vector b_v(b.GetData() + 0, sc);
Vector b_x(b.GetData() + sc, sc);
Vector rhs(sc);
double dt = GetTimeStep(sundials_mem);
// We can assume that b_v and b_x have zeros at essential tdofs.
// rhs = M b_v - dt*grad(H) b_x
ParGridFunction lb_x(fes), lrhs(fes);
lb_x.Distribute(b_x);
local_grad_H->Mult(lb_x, lrhs);
lrhs.ParallelAssemble(rhs);
rhs *= -dt;
M->TrueAddMult(b_v, rhs);
rhs.SetSubVector(*ess_tdof_list, 0.0);
J_solver->iterative_mode = false;
J_solver->Mult(rhs, b_v);
b_x.Add(dt, b_v);
return 0;
}
int SundialsJacSolver::FreeSystem(void *sundials_mem)
{
delete Jacobian;
return 0;
}
HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
Array<int> &ess_bdr, double visc,
double mu, double K,
NonlinearSolverType nls_type)
: TimeDependentOperator(2*f.TrueVSize(), 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), M_solver(f.GetComm()), z(height/2),
local_grad_H(NULL), Jacobian(NULL)
viscosity(visc), M_solver(f.GetComm()), z(height/2)
{
const double rel_tol = 1e-8;
const int skip_zero_entries = 0;
@@ -721,24 +788,23 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
if (nls_type == KINSOL)
{
KINSolver *kinsolver = new KINSolver(f.GetComm(), KIN_NONE, true);
KinSolver *kinsolver = new KinSolver(f.GetComm(), KIN_NONE, true);
kinsolver->SetMaxSetupCalls(4);
newton_solver = kinsolver;
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(200);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(0);
kinsolver->SetMaxSetupCalls(4);
}
else
{
newton_solver = new NewtonSolver(f.GetComm());
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(10);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(-1);
}
newton_solver->SetSolver(*J_solver);
newton_solver->iterative_mode = false;
newton_solver->SetOperator(*reduced_oper);
}
void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
@@ -792,64 +858,9 @@ void HyperelasticOperator::ImplicitSolve(const double dt,
add(v, dt, dv_dt, dx_dt);
}
int HyperelasticOperator::SUNImplicitSetup(const Vector &y,
const Vector &fy, int jok, int *jcur,
double gamma)
void HyperelasticOperator::InitSundialsJacSolver(SundialsJacSolver &sjsolv)
{
int sc = y.Size() / 2;
const Vector x(y.GetData() + sc, sc);
// J = M + dt*(S + dt*grad(H))
if (Jacobian) { delete Jacobian; }
SparseMatrix *localJ = Add(1.0, M.SpMat(), gamma, S.SpMat());
local_grad_H = &H.GetLocalGradient(x);
localJ->Add(gamma*gamma, *local_grad_H);
Jacobian = M.ParallelAssemble(localJ);
delete localJ;
HypreParMatrix *Je = Jacobian->EliminateRowsCols(ess_tdof_list);
delete Je;
// Set Jacobian solve operator
J_solver->SetOperator(*Jacobian);
// Indicate that the Jacobian was updated
*jcur = 1;
// Save gamma for use in solve
saved_gamma = gamma;
// Return success
return 0;
}
int HyperelasticOperator::SUNImplicitSolve(const Vector &b, Vector &x,
double tol)
{
int sc = b.Size() / 2;
ParFiniteElementSpace *fes = H.ParFESpace();
Vector b_v(b.GetData() + 0, sc);
Vector b_x(b.GetData() + sc, sc);
Vector x_v(x.GetData() + 0, sc);
Vector x_x(x.GetData() + sc, sc);
Vector rhs(sc);
// We can assume that b_v and b_x have zeros at essential tdofs.
// rhs = M b_v - dt*grad(H) b_x
ParGridFunction lb_x(fes), lrhs(fes);
lb_x.Distribute(b_x);
local_grad_H->Mult(lb_x, lrhs);
lrhs.ParallelAssemble(rhs);
rhs *= -saved_gamma;
M.TrueAddMult(b_v, rhs);
rhs.SetSubVector(ess_tdof_list, 0.0);
J_solver->iterative_mode = false;
J_solver->Mult(rhs, x_v);
add(b_x, saved_gamma, x_v, x_x);
return 0;
sjsolv.SetOperators(M, S, H, *J_solver, ess_tdof_list);
}
double HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
@@ -875,7 +886,6 @@ void HyperelasticOperator::GetElasticEnergyDensity(
HyperelasticOperator::~HyperelasticOperator()
{
delete Jacobian;
delete newton_solver;
delete J_solver;
delete J_prec;
+165 -124
View File
@@ -7,9 +7,9 @@
// ex16 -m ../../data/inline-tri.mesh
// ex16 -m ../../data/disc-nurbs.mesh -tf 2
// ex16 -s 12 -a 0.0 -k 1.0
// ex16 -s 8 -a 1.0 -k 0.0 -dt 1e-4 -tf 5e-2 -vs 25
// ex16 -s 9 -a 0.5 -k 0.5 -o 4 -dt 1e-4 -tf 2e-2 -vs 25
// ex16 -s 10 -dt 1.0e-4 -tf 4.0e-2 -vs 40
// ex16 -s 1 -a 1.0 -k 0.0 -dt 1e-4 -tf 5e-2 -vs 25
// ex16 -s 2 -a 0.5 -k 0.5 -o 4 -dt 1e-4 -tf 2e-2 -vs 25
// ex16 -s 3 -dt 1.0e-4 -tf 4.0e-2 -vs 40
// ex16 -m ../../data/fichera-q2.mesh
// ex16 -m ../../data/escher.mesh
// ex16 -m ../../data/beam-tet.mesh -tf 10 -dt 0.1
@@ -58,6 +58,7 @@ protected:
SparseMatrix Mmat, Kmat;
SparseMatrix *T; // T = M + dt K
double current_dt;
CGSolver M_solver; // Krylov solver for inverting the mass matrix M
DSmoother M_prec; // Preconditioner for the mass matrix M
@@ -74,30 +75,13 @@ public:
const Vector &u);
virtual void Mult(const Vector &u, Vector &du_dt) const;
/** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
virtual void ImplicitSolve(const double dt, const Vector &u, Vector &k);
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by ConductionOperator
M du/dt = -K(u),
this class facilitates the solution of linear systems of the form
(M + γK) y = M b,
for given b, u (not used), and γ = GetTimeStep(). */
/** Setup the system (M + dt K) x = M b. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSetup(const Vector &x, const Vector &fx,
int jok, int *jcur, double gamma);
/** Solve the system (M + dt K) x = M b. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSolve(const Vector &b, Vector &x, double tol);
/** Solve the system (M + dt K) y = M b. The result y replaces the input b.
This method is used by the implicit SUNDIALS solvers. */
void SundialsSolve(const double dt, Vector &b);
/// Update the diffusion BilinearForm K using the given true-dof vector `u`.
void SetParameters(const Vector &u);
@@ -105,6 +89,33 @@ public:
virtual ~ConductionOperator();
};
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by ConductionOperator
M du/dt = -K(u),
this class facilitates the solution of linear systems of the form
(M + γK) y = M b,
for given b, u (not used), and γ = GetTimeStep(). */
class SundialsJacSolver : public SundialsODELinearSolver
{
private:
ConductionOperator *oper;
public:
SundialsJacSolver() : oper(NULL) { }
int InitSystem(void *sundials_mem);
int SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred, int &jac_cur,
Vector &v_temp1, Vector &v_temp2, Vector &v_temp3);
int SolveSystem(void *sundials_mem, Vector &b, const Vector &weight,
const Vector &y_cur, const Vector &f_cur);
int FreeSystem(void *sundials_mem);
};
double InitialTemperature(const Vector &x);
int main(int argc, char *argv[])
@@ -113,7 +124,7 @@ int main(int argc, char *argv[])
const char *mesh_file = "../../data/star.mesh";
int ref_levels = 2;
int order = 2;
int ode_solver_type = 9; // CVODE implicit BDF
int ode_solver_type = 11; // 11 = CVODE implicit
double t_final = 0.5;
double dt = 1.0e-2;
double alpha = 1.0e-2;
@@ -136,19 +147,12 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
"2 - RK2,\n\t"
"3 - RK3 SSP,\n\t"
"4 - RK4,\n\t"
"5 - Backward Euler,\n\t"
"6 - SDIRK 2,\n\t"
"7 - SDIRK 3,\n\t"
"8 - CVODE (implicit Adams),\n\t"
"9 - CVODE (implicit BDF),\n\t"
"10 - ARKODE (default explicit),\n\t"
"11 - ARKODE (explicit Fehlberg-6-4-5),\n\t"
"12 - ARKODE (default impicit).");
"ODE solver:\n"
"\t 1/11 - CVODE (explicit/implicit),\n"
"\t 2/12 - ARKODE (default explicit/implicit),\n"
"\t 3 - ARKODE (Fehlberg-6-4-5)\n"
"\t 4 - Forward Euler, 5 - RK2, 6 - RK3 SSP, 7 - RK4,\n"
"\t 8 - Backward Euler, 9 - SDIRK23, 10 - SDIRK33.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -171,11 +175,6 @@ int main(int argc, char *argv[])
args.PrintUsage(cout);
return 1;
}
if (ode_solver_type < 1 || ode_solver_type > 12)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
return 3;
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle triangular,
@@ -183,7 +182,61 @@ int main(int argc, char *argv[])
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Refine the mesh to increase the resolution. In this example we do
// 3. Define the ODE solver used for time integration. Several
// SUNDIALS solvers are available, as well as included both
// explicit and implicit MFEM ODE solvers.
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKODESolver *arkode = NULL;
SundialsJacSolver sun_solver; // Used by the implicit SUNDIALS ode solvers.
switch (ode_solver_type)
{
// SUNDIALS solvers
case 1:
cvode = new CVODESolver(CV_ADAMS, CV_FUNCTIONAL);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 11:
cvode = new CVODESolver(CV_BDF, CV_NEWTON);
cvode->SetLinearSolver(sun_solver);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 2:
case 3:
arkode = new ARKODESolver(ARKODESolver::EXPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 3) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
case 12:
arkode = new ARKODESolver(ARKODESolver::IMPLICIT);
arkode->SetLinearSolver(sun_solver);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
// Other MFEM explicit methods
case 4: ode_solver = new ForwardEulerSolver; break;
case 5: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 6: ode_solver = new RK3SSPSolver; break;
case 7: ode_solver = new RK4Solver; break;
// MFEM implicit L-stable methods
case 8: ode_solver = new BackwardEulerSolver; break;
case 9: ode_solver = new SDIRK23Solver(2); break;
case 10: ode_solver = new SDIRK33Solver; break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
delete mesh;
return 3;
}
// Since we want to update the diffusion coefficient after every time step,
// we need to use the "one-step" mode of the SUNDIALS solvers.
if (cvode) { cvode->SetStepMode(CV_ONE_STEP); }
if (arkode) { arkode->SetStepMode(ARK_ONE_STEP); }
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter.
for (int lev = 0; lev < ref_levels; lev++)
@@ -191,7 +244,7 @@ int main(int argc, char *argv[])
mesh->UniformRefinement();
}
// 4. Define the vector finite element space representing the current and the
// 5. Define the vector finite element space representing the current and the
// initial temperature, u_ref.
H1_FECollection fe_coll(order, dim);
FiniteElementSpace fespace(mesh, &fe_coll);
@@ -201,14 +254,14 @@ int main(int argc, char *argv[])
GridFunction u_gf(&fespace);
// 5. Set the initial conditions for u. All boundaries are considered
// 6. Set the initial conditions for u. All boundaries are considered
// natural.
FunctionCoefficient u_0(InitialTemperature);
u_gf.ProjectCoefficient(u_0);
Vector u;
u_gf.GetTrueDofs(u);
// 6. Initialize the conduction operator and the visualization.
// 7. Initialize the conduction operator and the visualization.
ConductionOperator oper(fespace, alpha, kappa, u);
u_gf.SetFromTrueDofs(u);
@@ -254,65 +307,13 @@ int main(int argc, char *argv[])
}
}
// 7. Define the ODE solver used for time integration.
double t = 0.0;
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKStepSolver *arkode = NULL;
switch (ode_solver_type)
{
// MFEM explicit methods
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
// MFEM implicit L-stable methods
case 5: ode_solver = new BackwardEulerSolver; break;
case 6: ode_solver = new SDIRK23Solver(2); break;
case 7: ode_solver = new SDIRK33Solver; break;
// CVODE
case 8:
cvode = new CVODESolver(CV_ADAMS);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 9:
cvode = new CVODESolver(CV_BDF);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
// ARKODE
case 10:
case 11:
arkode = new ARKStepSolver(ARKStepSolver::EXPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 11) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
case 12:
arkode = new ARKStepSolver(ARKStepSolver::IMPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 8) { ode_solver->Init(oper); }
// Since we want to update the diffusion coefficient after every time step,
// we need to use the "one-step" mode of the SUNDIALS solvers.
if (cvode) { cvode->SetStepMode(CV_ONE_STEP); }
if (arkode) { arkode->SetStepMode(ARK_ONE_STEP); }
// 8. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
cout << "Integrating the ODE ..." << endl;
tic_toc.Clear();
tic_toc.Start();
ode_solver->Init(oper);
double t = 0.0;
bool last_step = false;
for (int ti = 1; !last_step; ti++)
@@ -370,7 +371,7 @@ int main(int argc, char *argv[])
ConductionOperator::ConductionOperator(FiniteElementSpace &f, double al,
double kap, const Vector &u)
: TimeDependentOperator(f.GetTrueVSize(), 0.0), fespace(f), M(NULL), K(NULL),
T(NULL), z(height)
T(NULL), current_dt(0.0), z(height)
{
const double rel_tol = 1e-8;
@@ -416,14 +417,32 @@ void ConductionOperator::ImplicitSolve(const double dt,
// Solve the equation:
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
// for du_dt
if (T) { delete T; }
T = Add(1.0, Mmat, dt, Kmat);
T_solver.SetOperator(*T);
if (!T)
{
T = Add(1.0, Mmat, dt, Kmat);
current_dt = dt;
T_solver.SetOperator(*T);
}
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
Kmat.Mult(u, z);
z.Neg();
T_solver.Mult(z, du_dt);
}
void ConductionOperator::SundialsSolve(const double dt, Vector &b)
{
// Solve the system (M + dt K) y = M b. The result y replaces the input b.
if (!T || dt != current_dt)
{
delete T;
T = Add(1.0, Mmat, dt, Kmat);
current_dt = dt;
T_solver.SetOperator(*T);
}
Mmat.Mult(b, z);
T_solver.Mult(z, b);
}
void ConductionOperator::SetParameters(const Vector &u)
{
GridFunction u_alpha_gf(&fespace);
@@ -441,26 +460,8 @@ void ConductionOperator::SetParameters(const Vector &u)
K->AddDomainIntegrator(new DiffusionIntegrator(u_coeff));
K->Assemble();
K->FormSystemMatrix(ess_tdof_list, Kmat);
}
int ConductionOperator::SUNImplicitSetup(const Vector &x,
const Vector &fx, int jok, int *jcur,
double gamma)
{
// Setup the ODE Jacobian T = M + gamma K.
if (T) { delete T; }
T = Add(1.0, Mmat, gamma, Kmat);
T_solver.SetOperator(*T);
*jcur = 1;
return (0);
}
int ConductionOperator::SUNImplicitSolve(const Vector &b, Vector &x, double tol)
{
// Solve the system A x = z => (M - gamma K) x = M b.
Mmat.Mult(b, z);
T_solver.Mult(z, x);
return (0);
delete T;
T = NULL; // re-compute T on the next ImplicitSolve or SundialsSolve
}
ConductionOperator::~ConductionOperator()
@@ -470,6 +471,46 @@ ConductionOperator::~ConductionOperator()
delete K;
}
int SundialsJacSolver::InitSystem(void *sundials_mem)
{
TimeDependentOperator *td_oper = GetTimeDependentOperator(sundials_mem);
// During development, we use dynamic_cast<> to ensure the setup is correct:
oper = dynamic_cast<ConductionOperator*>(td_oper);
MFEM_VERIFY(oper, "operator is not ConductionOperator");
// When the implementation is finalized, we can switch to static_cast<>:
// oper = static_cast<ConductionOperator*>(td_oper);
return 0;
}
int SundialsJacSolver::SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred,
int &jac_cur, Vector &v_temp1,
Vector &v_temp2, Vector &v_temp3)
{
jac_cur = 1;
return 0;
}
int SundialsJacSolver::SolveSystem(void *sundials_mem, Vector &b,
const Vector &weight, const Vector &y_cur,
const Vector &f_cur)
{
oper->SundialsSolve(GetTimeStep(sundials_mem), b);
return 0;
}
int SundialsJacSolver::FreeSystem(void *sundials_mem)
{
return 0;
}
double InitialTemperature(const Vector &x)
{
if (x.Norml2() < 0.5)
+161 -116
View File
@@ -8,9 +8,9 @@
// mpirun -np 4 ex16p -m ../../data/inline-tri.mesh
// mpirun -np 4 ex16p -m ../../data/disc-nurbs.mesh -tf 2
// mpirun -np 4 ex16p -s 12 -a 0.0 -k 1.0
// mpirun -np 4 ex16p -s 8 -a 1.0 -k 0.0 -dt 4e-6 -tf 2e-2 -vs 50
// mpirun -np 8 ex16p -s 9 -a 0.5 -k 0.5 -o 4 -dt 8e-6 -tf 2e-2 -vs 50
// mpirun -np 4 ex16p -s 10 -dt 2.0e-4 -tf 4.0e-2
// mpirun -np 4 ex16p -s 1 -a 1.0 -k 0.0 -dt 4e-6 -tf 2e-2 -vs 50
// mpirun -np 8 ex16p -s 2 -a 0.5 -k 0.5 -o 4 -dt 8e-6 -tf 2e-2 -vs 50
// mpirun -np 4 ex16p -s 3 -dt 2.0e-4 -tf 4.0e-2
// mpirun -np 16 ex16p -m ../../data/fichera-q2.mesh
// mpirun -np 16 ex16p -m ../../data/escher-p2.mesh
// mpirun -np 8 ex16p -m ../../data/beam-tet.mesh -tf 10 -dt 0.1
@@ -77,19 +77,13 @@ public:
const Vector &u);
virtual void Mult(const Vector &u, Vector &du_dt) const;
/** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
virtual void ImplicitSolve(const double dt, const Vector &u, Vector &k);
/** Setup the system (M + dt K) x = M b. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSetup(const Vector &x, const Vector &fx,
int jok, int *jcur, double gamma);
/** Solve the system (M + dt K) x = M b. This method is used by the implicit
SUNDIALS solvers. */
virtual int SUNImplicitSolve(const Vector &b, Vector &x, double tol);
/** Solve the system (M + dt K) y = M b. The result y replaces the input b.
This method is used by the implicit SUNDIALS solvers. */
void SundialsSolve(const double dt, Vector &b);
/// Update the diffusion BilinearForm K using the given true-dof vector `u`.
void SetParameters(const Vector &u);
@@ -97,6 +91,33 @@ public:
virtual ~ConductionOperator();
};
/// Custom Jacobian system solver for the SUNDIALS time integrators.
/** For the ODE system represented by ConductionOperator
M du/dt = -K(u),
this class facilitates the solution of linear systems of the form
(M + γK) y = M b,
for given b, u (not used), and γ = GetTimeStep(). */
class SundialsJacSolver : public SundialsODELinearSolver
{
private:
ConductionOperator *oper;
public:
SundialsJacSolver() : oper(NULL) { }
int InitSystem(void *sundials_mem);
int SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred, int &jac_cur,
Vector &v_temp1, Vector &v_temp2, Vector &v_temp3);
int SolveSystem(void *sundials_mem, Vector &b, const Vector &weight,
const Vector &y_cur, const Vector &f_cur);
int FreeSystem(void *sundials_mem);
};
double InitialTemperature(const Vector &x);
int main(int argc, char *argv[])
@@ -112,7 +133,7 @@ int main(int argc, char *argv[])
int ser_ref_levels = 2;
int par_ref_levels = 1;
int order = 2;
int ode_solver_type = 9; // CVODE implicit BDF
int ode_solver_type = 11; // 11 = CVODE implicit
double t_final = 0.5;
double dt = 1.0e-2;
double alpha = 1.0e-2;
@@ -137,19 +158,12 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
"2 - RK2,\n\t"
"3 - RK3 SSP,\n\t"
"4 - RK4,\n\t"
"5 - Backward Euler,\n\t"
"6 - SDIRK 2,\n\t"
"7 - SDIRK 3,\n\t"
"8 - CVODE (implicit Adams),\n\t"
"9 - CVODE (implicit BDF),\n\t"
"10 - ARKODE (default explicit),\n\t"
"11 - ARKODE (explicit Fehlberg-6-4-5),\n\t"
"12 - ARKODE (default impicit).");
"ODE solver:\n"
"\t 1/11 - CVODE (explicit/implicit),\n"
"\t 2/12 - ARKODE (default explicit/implicit),\n"
"\t 3 - ARKODE (Fehlberg-6-4-5)\n"
"\t 4 - Forward Euler, 5 - RK2, 6 - RK3 SSP, 7 - RK4,\n"
"\t 8 - Backward Euler, 9 - SDIRK23, 10 - SDIRK33.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -179,24 +193,67 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 12)
{
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
MPI_Finalize();
return 1;
}
// 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.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh in serial to increase the resolution. In this example
// 4. Define the ODE solver used for time integration. Several
// SUNDIALS solvers are available, as well as included both
// explicit and implicit MFEM ODE solvers.
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKODESolver *arkode = NULL;
SundialsJacSolver sun_solver; // Used by the implicit SUNDIALS ode solvers.
switch (ode_solver_type)
{
// SUNDIALS solvers
case 1:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS, CV_FUNCTIONAL);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 11:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_BDF, CV_NEWTON);
cvode->SetLinearSolver(sun_solver);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 2:
case 3:
arkode = new ARKODESolver(MPI_COMM_WORLD, ARKODESolver::EXPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 3) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
case 12:
arkode = new ARKODESolver(MPI_COMM_WORLD, ARKODESolver::IMPLICIT);
arkode->SetLinearSolver(sun_solver);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
// Other MFEM explicit methods
case 4: ode_solver = new ForwardEulerSolver; break;
case 5: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 6: ode_solver = new RK3SSPSolver; break;
case 7: ode_solver = new RK4Solver; break;
// MFEM implicit L-stable methods
case 8: ode_solver = new BackwardEulerSolver; break;
case 9: ode_solver = new SDIRK23Solver(2); break;
case 10: ode_solver = new SDIRK33Solver; break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
delete mesh;
return 3;
}
// Since we want to update the diffusion coefficient after every time step,
// we need to use the "one-step" mode of the SUNDIALS solvers.
if (cvode) { cvode->SetStepMode(CV_ONE_STEP); }
if (arkode) { arkode->SetStepMode(ARK_ONE_STEP); }
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter.
for (int lev = 0; lev < ser_ref_levels; lev++)
@@ -204,7 +261,7 @@ int main(int argc, char *argv[])
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
@@ -214,7 +271,7 @@ int main(int argc, char *argv[])
pmesh->UniformRefinement();
}
// 6. Define the vector finite element space representing the current and the
// 7. Define the vector finite element space representing the current and the
// initial temperature, u_ref.
H1_FECollection fe_coll(order, dim);
ParFiniteElementSpace fespace(pmesh, &fe_coll);
@@ -227,14 +284,14 @@ int main(int argc, char *argv[])
ParGridFunction u_gf(&fespace);
// 7. Set the initial conditions for u. All boundaries are considered
// 8. Set the initial conditions for u. All boundaries are considered
// natural.
FunctionCoefficient u_0(InitialTemperature);
u_gf.ProjectCoefficient(u_0);
Vector u;
u_gf.GetTrueDofs(u);
// 8. Initialize the conduction operator and the VisIt visualization.
// 9. Initialize the conduction operator and the VisIt visualization.
ConductionOperator oper(fespace, alpha, kappa, u);
u_gf.SetFromTrueDofs(u);
@@ -293,60 +350,6 @@ int main(int argc, char *argv[])
}
}
// 9. Define the ODE solver used for time integration.
double t = 0.0;
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKStepSolver *arkode = NULL;
switch (ode_solver_type)
{
// MFEM explicit methods
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(0.5); break; // midpoint method
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
// MFEM implicit L-stable methods
case 5: ode_solver = new BackwardEulerSolver; break;
case 6: ode_solver = new SDIRK23Solver(2); break;
case 7: ode_solver = new SDIRK33Solver; break;
// CVODE
case 8:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 9:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_BDF);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
// ARKODE
case 10:
case 11:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::EXPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 11) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
case 12:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::IMPLICIT);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 8) { ode_solver->Init(oper); }
// Since we want to update the diffusion coefficient after every time step,
// we need to use the "one-step" mode of the SUNDIALS solvers.
if (cvode) { cvode->SetStepMode(CV_ONE_STEP); }
if (arkode) { arkode->SetStepMode(ARK_ONE_STEP); }
// 10. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
if (myid == 0)
@@ -355,6 +358,8 @@ int main(int argc, char *argv[])
}
tic_toc.Clear();
tic_toc.Start();
ode_solver->Init(oper);
double t = 0.0;
bool last_step = false;
for (int ti = 1; !last_step; ti++)
@@ -423,7 +428,7 @@ int main(int argc, char *argv[])
ConductionOperator::ConductionOperator(ParFiniteElementSpace &f, double al,
double kap, const Vector &u)
: TimeDependentOperator(f.GetTrueVSize(), 0.0), fespace(f), M(NULL), K(NULL),
T(NULL),
T(NULL), current_dt(0.0),
M_solver(f.GetComm()), T_solver(f.GetComm()), z(height)
{
const double rel_tol = 1e-8;
@@ -471,32 +476,30 @@ void ConductionOperator::ImplicitSolve(const double dt,
// Solve the equation:
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
// for du_dt
if (T) { delete T; }
T = Add(1.0, Mmat, dt, Kmat);
T_solver.SetOperator(*T);
if (!T)
{
T = Add(1.0, Mmat, dt, Kmat);
current_dt = dt;
T_solver.SetOperator(*T);
}
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
Kmat.Mult(u, z);
z.Neg();
T_solver.Mult(z, du_dt);
}
int ConductionOperator::SUNImplicitSetup(const Vector &x,
const Vector &fx, int jok, int *jcur,
double gamma)
void ConductionOperator::SundialsSolve(const double dt, Vector &b)
{
// Setup the ODE Jacobian T = M + gamma K.
if (T) { delete T; }
T = Add(1.0, Mmat, gamma, Kmat);
T_solver.SetOperator(*T);
*jcur = 1;
return (0);
}
int ConductionOperator::SUNImplicitSolve(const Vector &b, Vector &x, double tol)
{
// Solve the system A x = z => (M - gamma K) x = M b.
// Solve the system (M + dt K) y = M b. The result y replaces the input b.
if (!T || dt != current_dt)
{
delete T;
T = Add(1.0, Mmat, dt, Kmat);
current_dt = dt;
T_solver.SetOperator(*T);
}
Mmat.Mult(b, z);
T_solver.Mult(z, x);
return (0);
T_solver.Mult(z, b);
}
void ConductionOperator::SetParameters(const Vector &u)
@@ -516,6 +519,8 @@ void ConductionOperator::SetParameters(const Vector &u)
K->AddDomainIntegrator(new DiffusionIntegrator(u_coeff));
K->Assemble(0); // keep sparsity pattern of M and K the same
K->FormSystemMatrix(ess_tdof_list, Kmat);
delete T;
T = NULL; // re-compute T on the next ImplicitSolve or SundialsSolve
}
ConductionOperator::~ConductionOperator()
@@ -525,6 +530,46 @@ ConductionOperator::~ConductionOperator()
delete K;
}
int SundialsJacSolver::InitSystem(void *sundials_mem)
{
TimeDependentOperator *td_oper = GetTimeDependentOperator(sundials_mem);
// During development, we use dynamic_cast<> to ensure the setup is correct:
oper = dynamic_cast<ConductionOperator*>(td_oper);
MFEM_VERIFY(oper, "operator is not ConductionOperator");
// When the implementation is finalized, we can switch to static_cast<>:
// oper = static_cast<ConductionOperator*>(td_oper);
return 0;
}
int SundialsJacSolver::SetupSystem(void *sundials_mem, int conv_fail,
const Vector &y_pred, const Vector &f_pred,
int &jac_cur, Vector &v_temp1,
Vector &v_temp2, Vector &v_temp3)
{
jac_cur = 1;
return 0;
}
int SundialsJacSolver::SolveSystem(void *sundials_mem, Vector &b,
const Vector &weight, const Vector &y_cur,
const Vector &f_cur)
{
oper->SundialsSolve(GetTimeStep(sundials_mem), b);
return 0;
}
int SundialsJacSolver::FreeSystem(void *sundials_mem)
{
return 0;
}
double InitialTemperature(const Vector &x)
{
if (x.Norml2() < 0.5)
+63 -74
View File
@@ -4,14 +4,14 @@
// Compile with: make ex9
//
// Sample runs:
// ex9 -m ../../data/periodic-segment.mesh -p 0 -r 2 -s 7 -dt 0.005
// ex9 -m ../../data/periodic-square.mesh -p 1 -r 2 -s 8 -dt 0.005 -tf 9
// ex9 -m ../../data/periodic-hexagon.mesh -p 0 -r 2 -s 7 -dt 0.0018 -vs 25
// ex9 -m ../../data/periodic-hexagon.mesh -p 0 -r 2 -s 9 -dt 0.01 -vs 15
// ex9 -m ../../data/amr-quad.mesh -p 1 -r 2 -s 9 -dt 0.002 -tf 9
// ex9 -m ../../data/star-q3.mesh -p 1 -r 2 -s 9 -dt 0.005 -tf 9
// ex9 -m ../../data/disc-nurbs.mesh -p 1 -r 3 -s 7 -dt 0.005 -tf 9
// ex9 -m ../../data/periodic-cube.mesh -p 0 -r 2 -s 8 -dt 0.02 -tf 8 -o 2
// ex9 -m ../../data/periodic-segment.mesh -p 0 -r 2 -s 11 -dt 0.005
// ex9 -m ../../data/periodic-square.mesh -p 1 -r 2 -s 12 -dt 0.005 -tf 9
// ex9 -m ../../data/periodic-hexagon.mesh -p 0 -r 2 -s 11 -dt 0.0018 -vs 25
// ex9 -m ../../data/periodic-hexagon.mesh -p 0 -r 2 -s 13 -dt 0.01 -vs 15
// ex9 -m ../../data/amr-quad.mesh -p 1 -r 2 -s 13 -dt 0.002 -tf 9
// ex9 -m ../../data/star-q3.mesh -p 1 -r 2 -s 13 -dt 0.005 -tf 9
// ex9 -m ../../data/disc-nurbs.mesh -p 1 -r 3 -s 11 -dt 0.005 -tf 9
// ex9 -m ../../data/periodic-cube.mesh -p 0 -r 2 -s 12 -dt 0.02 -tf 8 -o 2
//
// Description: This example code solves the time-dependent advection equation
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
@@ -109,15 +109,11 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
"2 - RK2 SSP,\n\t"
"3 - RK3 SSP,\n\t"
"4 - RK4,\n\t"
"6 - RK6,\n\t"
"7 - CVODE (adaptive order implicit Adams),\n\t"
"8 - ARKODE default (4th order) explicit,\n\t"
"9 - ARKODE RK8.");
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
" 11 - CVODE (adaptive order) explicit,\n\t"
" 12 - ARKODE default (4th order) explicit,\n\t"
" 13 - ARKODE RK8.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -139,41 +135,65 @@ int main(int argc, char *argv[])
args.PrintUsage(cout);
return 1;
}
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 9)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
return 3;
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle geometrically
// periodic meshes in this code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Refine the mesh to increase the resolution. In this example we do
// 3. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKODESolver *arkode = NULL;
switch (ode_solver_type)
{
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(1.0); break;
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
case 6: ode_solver = new RK6Solver; break;
case 11:
cvode = new CVODESolver(CV_ADAMS, CV_FUNCTIONAL);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 12:
case 13:
arkode = new ARKODESolver(ARKODESolver::EXPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 13) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
delete mesh;
return 3;
}
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter. If the mesh is of NURBS type, we convert it to
// a (piecewise-polynomial) high-order mesh.
for (int lev = 0; lev < ref_levels; lev++)
{
mesh.UniformRefinement();
mesh->UniformRefinement();
}
if (mesh.NURBSext)
if (mesh->NURBSext)
{
mesh.SetCurvature(max(order, 1));
mesh->SetCurvature(max(order, 1));
}
mesh.GetBoundingBox(bb_min, bb_max, max(order, 1));
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 4. Define the discontinuous DG finite element space of the given
// 5. Define the discontinuous DG finite element space of the given
// polynomial order on the refined mesh.
DG_FECollection fec(order, dim);
FiniteElementSpace fes(&mesh, &fec);
FiniteElementSpace fes(mesh, &fec);
cout << "Number of unknowns: " << fes.GetVSize() << endl;
// 5. Set up and assemble the bilinear and linear forms corresponding to the
// 6. Set up and assemble the bilinear and linear forms corresponding to the
// DG discretization. The DGTraceIntegrator involves integrals over mesh
// interior faces.
VectorFunctionCoefficient velocity(dim, velocity_function);
@@ -200,7 +220,7 @@ int main(int argc, char *argv[])
k.Finalize(skip_zeros);
b.Assemble();
// 6. Define the initial conditions, save the corresponding grid function to
// 7. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
// GLVis visualization.
GridFunction u(&fes);
@@ -209,7 +229,7 @@ int main(int argc, char *argv[])
{
ofstream omesh("ex9.mesh");
omesh.precision(precision);
mesh.Print(omesh);
mesh->Print(omesh);
ofstream osol("ex9-init.gf");
osol.precision(precision);
u.Save(osol);
@@ -223,14 +243,14 @@ int main(int argc, char *argv[])
if (binary)
{
#ifdef MFEM_USE_SIDRE
dc = new SidreDataCollection("Example9", &mesh);
dc = new SidreDataCollection("Example9", mesh);
#else
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
#endif
}
else
{
dc = new VisItDataCollection("Example9", &mesh);
dc = new VisItDataCollection("Example9", mesh);
dc->SetPrecision(precision);
}
dc->RegisterField("solution", &u);
@@ -255,7 +275,7 @@ int main(int argc, char *argv[])
else
{
sout.precision(precision);
sout << "solution\n" << mesh << u;
sout << "solution\n" << *mesh << u;
sout << "pause\n";
sout << flush;
cout << "GLVis visualization paused."
@@ -263,46 +283,15 @@ int main(int argc, char *argv[])
}
}
// 7. Define the time-dependent evolution operator describing the ODE
// right-hand side, and define the ODE solver used for time integration.
// 8. Define the time-dependent evolution operator describing the ODE
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(m.SpMat(), k.SpMat(), b);
double t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
// Create the time integrator
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKStepSolver *arkode = NULL;
switch (ode_solver_type)
{
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(1.0); break;
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
case 6: ode_solver = new RK6Solver; break;
case 7:
cvode = new CVODESolver(CV_ADAMS);
cvode->Init(adv);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
cvode->UseSundialsLinearSolver();
ode_solver = cvode; break;
case 8:
case 9:
arkode = new ARKStepSolver(ARKStepSolver::EXPLICIT);
arkode->Init(adv);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 9) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 7) { ode_solver->Init(adv); }
// 8. Perform time-integration (looping over the time iterations, ti,
// with a time-step dt).
bool done = false;
for (int ti = 0; !done; )
{
@@ -320,7 +309,7 @@ int main(int argc, char *argv[])
if (visualization)
{
sout << "solution\n" << mesh << u << flush;
sout << "solution\n" << *mesh << u << flush;
}
if (visit)
+56 -67
View File
@@ -4,14 +4,14 @@
// Compile with: make ex9p
//
// Sample runs:
// mpirun -np 4 ex9p -m ../../data/periodic-segment.mesh -p 1 -rp 1 -s 7 -dt 0.0025
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 1 -rp 1 -s 8 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/periodic-hexagon.mesh -p 0 -rp 1 -s 7 -dt 0.0009 -vs 25
// mpirun -np 4 ex9p -m ../../data/periodic-hexagon.mesh -p 0 -rp 1 -s 9 -dt 0.005 -vs 15
// mpirun -np 4 ex9p -m ../../data/amr-quad.mesh -p 1 -rp 1 -s 9 -dt 0.001 -tf 9
// mpirun -np 4 ex9p -m ../../data/star-q3.mesh -p 1 -rp 1 -s 9 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 1 -rp 2 -s 7 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/periodic-cube.mesh -p 0 -rp 1 -s 8 -dt 0.01 -tf 8 -o 2
// mpirun -np 4 ex9p -m ../../data/periodic-segment.mesh -p 1 -rp 1 -s 11 -dt 0.0025
// mpirun -np 4 ex9p -m ../../data/periodic-square.mesh -p 1 -rp 1 -s 12 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/periodic-hexagon.mesh -p 0 -rp 1 -s 11 -dt 0.0009 -vs 25
// mpirun -np 4 ex9p -m ../../data/periodic-hexagon.mesh -p 0 -rp 1 -s 13 -dt 0.005 -vs 15
// mpirun -np 4 ex9p -m ../../data/amr-quad.mesh -p 1 -rp 1 -s 13 -dt 0.001 -tf 9
// mpirun -np 4 ex9p -m ../../data/star-q3.mesh -p 1 -rp 1 -s 13 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 1 -rp 2 -s 11 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/periodic-cube.mesh -p 0 -rp 1 -s 12 -dt 0.01 -tf 8 -o 2
//
// Description: This example code solves the time-dependent advection equation
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
@@ -117,15 +117,11 @@ int main(int argc, char *argv[])
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
"2 - RK2 SSP,\n\t"
"3 - RK3 SSP,\n\t"
"4 - RK4,\n\t"
"6 - RK6,\n\t"
"7 - CVODE (adaptive order implicit Adams),\n\t"
"8 - ARKODE default (4th order) explicit,\n\t"
"9 - ARKODE RK8.");
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
" 11 - CVODE (adaptive order) explicit,\n\t"
" 12 - ARKODE default (4th order) explicit,\n\t"
" 13 - ARKODE RK8.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -155,23 +151,47 @@ int main(int argc, char *argv[])
{
args.PrintOptions(cout);
}
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 9)
{
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
MPI_Finalize();
return 3;
}
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh in serial to increase the resolution. In this example
// 4. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKODESolver *arkode = NULL;
switch (ode_solver_type)
{
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(1.0); break;
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
case 6: ode_solver = new RK6Solver; break;
case 11:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS, CV_FUNCTIONAL);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
ode_solver = cvode; break;
case 12:
case 13:
arkode = new ARKODESolver(MPI_COMM_WORLD, ARKODESolver::EXPLICIT);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 13) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
default:
if (myid == 0)
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
delete mesh;
MPI_Finalize();
return 3;
}
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter. If the mesh is of NURBS type, we convert it
// to a (piecewise-polynomial) high-order mesh.
@@ -185,7 +205,7 @@ int main(int argc, char *argv[])
}
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 5. Define the parallel mesh by a partitioning of the serial mesh. Refine
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
@@ -195,7 +215,7 @@ int main(int argc, char *argv[])
pmesh->UniformRefinement();
}
// 6. Define the parallel discontinuous DG finite element space on the
// 7. Define the parallel discontinuous DG finite element space on the
// parallel refined mesh of the given polynomial order.
DG_FECollection fec(order, dim);
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
@@ -206,7 +226,7 @@ int main(int argc, char *argv[])
cout << "Number of unknowns: " << global_vSize << endl;
}
// 7. Set up and assemble the parallel bilinear and linear forms (and the
// 8. Set up and assemble the parallel bilinear and linear forms (and the
// parallel hypre matrices) corresponding to the DG discretization. The
// DGTraceIntegrator involves integrals over mesh interior faces.
VectorFunctionCoefficient velocity(dim, velocity_function);
@@ -237,7 +257,7 @@ int main(int argc, char *argv[])
HypreParMatrix *K = k->ParallelAssemble();
HypreParVector *B = b->ParallelAssemble();
// 8. Define the initial conditions, save the corresponding grid function to
// 9. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
// GLVis visualization.
ParGridFunction *u = new ParGridFunction(fes);
@@ -310,46 +330,15 @@ int main(int argc, char *argv[])
}
}
// 9. Define the time-dependent evolution operator describing the ODE
// right-hand side, and define the ODE solver used for time integration.
// 10. Define the time-dependent evolution operator describing the ODE
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(*M, *K, *B);
double t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
// Create the time integrator
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
ARKStepSolver *arkode = NULL;
switch (ode_solver_type)
{
case 1: ode_solver = new ForwardEulerSolver; break;
case 2: ode_solver = new RK2Solver(1.0); break;
case 3: ode_solver = new RK3SSPSolver; break;
case 4: ode_solver = new RK4Solver; break;
case 6: ode_solver = new RK6Solver; break;
case 7:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS);
cvode->Init(adv);
cvode->SetSStolerances(reltol, abstol);
cvode->SetMaxStep(dt);
cvode->UseSundialsLinearSolver();
ode_solver = cvode; break;
case 8:
case 9:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::EXPLICIT);
arkode->Init(adv);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
if (ode_solver_type == 9) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 7) { ode_solver->Init(adv); }
// 10. Perform time-integration (looping over the time iterations, ti,
// with a time-step dt).
bool done = false;
for (int ti = 0; !done; )
{
+3 -3
View File
@@ -60,15 +60,15 @@ PARALLEL_NAME := Parallel SUNDIALS example
@$(call mfem-test,$<,, $(SERIAL_NAME))
# Testing: Specific execution options:
# Example 9: test CVODE with CV_ADAMS (non-stiff implicit) time stepping
EX9_COMMON_ARGS := -m ../../data/periodic-hexagon.mesh -p 0 -s 7
# Example 9: test explicit CVODE time stepping
EX9_COMMON_ARGS := -m ../../data/periodic-hexagon.mesh -p 0 -s 11
EX9_ARGS := $(EX9_COMMON_ARGS) -r 2 -dt 0.0018 -vs 25
EX9P_ARGS := $(EX9_COMMON_ARGS) -rp 1 -dt 0.0009 -vs 50
ex9-test-seq: ex9
@$(call mfem-test,$<,, $(SERIAL_NAME),$(EX9_ARGS))
ex9p-test-par: ex9p
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME),$(EX9P_ARGS))
# Example 10: test CVODE with CV_BDF (stiff implicit) time stepping
# Example 10: test implicit CVODE time stepping
EX10_COMMON_ARGS := -m ../../data/beam-quad.mesh -o 2 -s 5 -dt 0.15 -tf 6 -vs 10
EX10_ARGS := $(EX10_COMMON_ARGS) -r 2
EX10P_ARGS := $(EX10_COMMON_ARGS) -rp 1
+2 -26
View File
@@ -13,14 +13,8 @@ set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
bilininteg_diffusion.cpp
bilininteg_divergence.cpp
bilininteg_gradient.cpp
bilininteg_mass.cpp
bilininteg_vecdiffusion.cpp
bilininteg_vecmass.cpp
bilininteg_ext.cpp
coefficient.cpp
complex_fem.cpp
datacollection.cpp
eltrans.cpp
estimators.cpp
@@ -34,21 +28,17 @@ set(SRCS
linearform.cpp
lininteg.cpp
nonlinearform.cpp
nonlinearform_ext.cpp
nonlininteg.cpp
nonlininteg_vectorconvection.cpp
staticcond.cpp
tmop.cpp
tmop_tools.cpp
gslib.cpp
)
set(HDRS
bilinearform.hpp
bilinearform_ext.hpp
bilininteg.hpp
bilininteg_ext.hpp
coefficient.hpp
complex_fem.hpp
datacollection.hpp
eltrans.hpp
estimators.hpp
@@ -63,7 +53,6 @@ set(HDRS
linearform.hpp
lininteg.hpp
nonlinearform.hpp
nonlinearform_ext.hpp
nonlininteg.hpp
staticcond.hpp
tbilinearform.hpp
@@ -75,8 +64,6 @@ set(HDRS
tfespace.hpp
tintrules.hpp
tmop.hpp
tmop_tools.hpp
gslib.hpp
)
if (MFEM_USE_SIDRE)
@@ -106,17 +93,6 @@ if (MFEM_USE_MPI)
pnonlinearform.hpp)
endif()
if (MFEM_USE_CEED)
list(APPEND SRCS
libceed/ceed.cpp
libceed/diffusion.cpp
libceed/mass.cpp)
list(APPEND HDRS
libceed/ceed.hpp
libceed/diffusion.hpp
libceed/mass.hpp)
endif()
convert_filenames_to_full_paths(SRCS)
convert_filenames_to_full_paths(HDRS)
-33
View File
@@ -1,33 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "fem.hpp"
#include "../general/forall.hpp"
#include "adnonlininteg.hpp"
namespace mfem
{
ADNonlinearFormIntegrator::ADNonlinearFormIntegrator()
{
}
ADNonlinearFormIntegrator::~ADNonlinearFormIntegrator()
{
}
}
-78
View File
@@ -1,78 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_ADNONLININTEG
#define MFEM_ADNONLININTEG
#include "../config/config.hpp"
#include "fe.hpp"
#include "coefficient.hpp"
#include "fespace.hpp"
#include "nonlininteg.hpp"
#include "tadvectro.hpp"
#include "taddensmat.hpp"
namespace mfem
{
#if define(MFEM_USE_ADEPT)||define(MFEM_USE_CODIPACK)
/** The abstract base class ADNonlinearFormIntegrator is
a generalization of the NonlinearFormIntegrator class suitable
for algorithmic differentiation.
All derived classes must implement ADAssembleElementVector(...);
and ADGetElementEnergy(...); */
class ADNonlinearFormIntegrator: public NonlinearFormIntegrator
{
protected:
#ifdef MFEM_USE_ADEPT
#elseif MFEM_USE_CODIPACK
#endif
public:
ADNonlinearFormIntegrator();
virtual ~ADNonlinearFormIntegrator();
/// Methods called by the AD routines
virtual void ADGetElementEnergy(const mfem::FiniteElement & el,
mfem::ElementTransformation & Tr,
const mfem::TADVector<adouble> & elfun);
virtual void ADAssembleElementVector(const mfem::FiniteElement & el,
mfem::ElementTransformation & Tr,
const mfem::TADVector<adouble> & elfun,
mfem::TADVector<adouble> &elvec);
/// Perform the local action of the NonlinearFormIntegrator
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect) override;
virtual void AssembleElementGrad(const mfem::FiniteElement & el,
mfem::ElementTransformation & Tr,
const mfem::Vector & elfun,
mfem::DenseMatrix & elmat) override;
virtual double GetElementEnergy(const mfem::FiniteElement & el,
mfem::ElementTransformation & Tr,
const mfem::Vector & elfun) override;
};
#endif
}
+64 -476
View File
@@ -55,7 +55,7 @@ void BilinearForm::AllocMat()
int *I = dof_dof.GetI();
int *J = dof_dof.GetJ();
double *data = new double[I[height]];
double *data = mfem::New<double>(I[height]);
mat = new SparseMatrix(I, J, data, height, height, true, true, true);
*mat = 0.0;
@@ -122,7 +122,11 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
switch (assembly)
{
case AssemblyLevel::FULL:
// ext = new FABilinearFormExtension(this);
if (Device::IsEnabled())
{
mfem_error("Full assembly not supported yet in device mode!");
// ext = new FABilinearFormExtension(this);
}
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
@@ -204,7 +208,7 @@ void BilinearForm::UseSparsity(SparseMatrix &A)
<< A.Height() << " x " << A.Width());
MFEM_ASSERT(A.Finalized(), "matrix A must be Finalized");
UseSparsity(A.GetI(), A.GetJ(), A.ColumnsAreSorted());
UseSparsity(A.GetI(), A.GetJ(), A.areColumnsSorted());
}
double& BilinearForm::Elem (int i, int j)
@@ -294,33 +298,6 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
}
}
void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
{
if (bbfi.Size())
{
const FiniteElement &be = *fes->GetBE(i);
ElementTransformation *eltrans = fes->GetBdrElementTransformation(i);
bbfi[0]->AssembleElementMatrix(be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
{
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
fes->GetBdrElementVDofs(i, vdofs);
elmat.SetSize(vdofs.Size());
elmat = 0.0;
}
}
void BilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleElementMatrix(i, elmat, vdofs, skip_zeros);
}
void BilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &vdofs, int skip_zeros)
{
@@ -343,12 +320,6 @@ void BilinearForm::AssembleElementMatrix(
}
}
void BilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleBdrElementMatrix(i, elmat, vdofs, skip_zeros);
}
void BilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &vdofs, int skip_zeros)
{
@@ -373,6 +344,11 @@ void BilinearForm::AssembleBdrElementMatrix(
void BilinearForm::Assemble(int skip_zeros)
{
if (Device::IsEnabled() && (assembly != AssemblyLevel::PARTIAL))
{
mfem_error("Chosen assembly level not supported yet in device mode!");
}
if (ext)
{
ext->Assemble();
@@ -608,31 +584,6 @@ void BilinearForm::ConformingAssemble()
width = mat->Width();
}
void BilinearForm::AssembleDiagonal(Vector &diag) const
{
if (ext)
{
MFEM_ASSERT(diag.Size() == fes->GetTrueVSize(),
"Vector for holding diagonal has wrong size!");
const Operator *P = fes->GetProlongationMatrix();
if (!IsIdentityProlongation(P))
{
Vector local_diag(P->Height());
ext->AssembleDiagonal(local_diag);
P->MultTranspose(local_diag, diag);
}
else
{
ext->AssembleDiagonal(diag);
}
}
else
{
MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
"matrix and use SparseMatrix::GetDiag?");
}
}
void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
Vector &b, OperatorHandle &A, Vector &X,
Vector &B, int copy_interior)
@@ -642,7 +593,9 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
FormSystemMatrix(ess_tdof_list, A);
// Transform the system and perform the elimination in B, based on the
@@ -668,8 +621,8 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
{
// A, X and B point to the same data as mat, x and b
EliminateVDofsInRHS(ess_tdof_list, x, b);
X.NewMemoryAndSize(x.GetMemory(), x.Size(), false);
B.NewMemoryAndSize(b.GetMemory(), b.Size(), false);
X.NewDataAndSize(x.GetData(), x.Size());
B.NewDataAndSize(b.GetData(), b.Size());
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
@@ -770,10 +723,6 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
else
{
// X and x point to the same data
// If the validity flags of X's Memory were changed (e.g. if it was
// moved to device memory) then we need to tell x about that.
x.SyncMemory(X);
}
}
else // non-conforming space
@@ -983,18 +932,6 @@ void BilinearForm::EliminateVDofsInRHS(
mat->PartMult(vdofs, x, b);
}
void BilinearForm::Mult(const Vector &x, Vector &y) const
{
if (ext)
{
ext->Mult(x, y);
}
else
{
mat->Mult(x, y);
}
}
void BilinearForm::Update(FiniteElementSpace *nfes)
{
bool full_update;
@@ -1070,11 +1007,7 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
trial_fes = tr_fes;
test_fes = te_fes;
mat = NULL;
mat_e = NULL;
extern_bfs = 0;
assembly = AssemblyLevel::FULL;
ext = NULL;
}
MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
@@ -1085,49 +1018,12 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
trial_fes = tr_fes;
test_fes = te_fes;
mat = NULL;
mat_e = NULL;
extern_bfs = 1;
// Copy the pointers to the integrators
dbfi = mbf->dbfi;
bbfi = mbf->bbfi;
tfbfi = mbf->tfbfi;
btfbfi = mbf->btfbfi;
bbfi_marker = mbf->bbfi_marker;
btfbfi_marker = mbf->btfbfi_marker;
assembly = AssemblyLevel::FULL;
ext = NULL;
}
void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
{
if (ext)
{
MFEM_ABORT("the assembly level has already been set!");
}
assembly = assembly_level;
switch (assembly)
{
case AssemblyLevel::FULL:
// ext = new FAMixedBilinearFormExtension(this);
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
mfem_error("Element assembly not supported yet... stay tuned!");
// ext = new EAMixedBilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext = new PAMixedBilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFMixedBilinearFormExtension(this);
break;
default:
mfem_error("Unknown assembly level");
}
dom = mbf->dom;
bdr = mbf->bdr;
skt = mbf->skt;
}
double & MixedBilinearForm::Elem (int i, int j)
@@ -1140,63 +1036,31 @@ const double & MixedBilinearForm::Elem (int i, int j) const
return (*mat)(i, j);
}
void MixedBilinearForm::Mult(const Vector & x, Vector & y) const
void MixedBilinearForm::Mult (const Vector & x, Vector & y) const
{
y = 0.0;
AddMult(x, y);
mat -> Mult (x, y);
}
void MixedBilinearForm::AddMult(const Vector & x, Vector & y,
const double a) const
void MixedBilinearForm::AddMult (const Vector & x, Vector & y,
const double a) const
{
if (ext)
{
ext->AddMult(x, y, a);
}
else
{
mat->AddMult(x, y, a);
}
mat -> AddMult (x, y, a);
}
void MixedBilinearForm::MultTranspose(const Vector & x, Vector & y) const
void MixedBilinearForm::AddMultTranspose (const Vector & x, Vector & y,
const double a) const
{
y = 0.0;
AddMultTranspose(x, y);
}
void MixedBilinearForm::AddMultTranspose(const Vector & x, Vector & y,
const double a) const
{
if (ext)
{
ext->AddMultTranspose(x, y, a);
}
else
{
mat->AddMultTranspose(x, y, a);
}
mat -> AddMultTranspose (x, y, a);
}
MatrixInverse * MixedBilinearForm::Inverse() const
{
if (assembly != AssemblyLevel::FULL)
{
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this assembly level!");
return NULL;
}
else
{
return mat -> Inverse ();
}
return mat -> Inverse ();
}
void MixedBilinearForm::Finalize (int skip_zeros)
{
if (assembly == AssemblyLevel::FULL)
{
mat -> Finalize (skip_zeros);
}
mat -> Finalize (skip_zeros);
}
void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
@@ -1213,48 +1077,22 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
{
dbfi.Append (bfi);
dom.Append (bfi);
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
{
bbfi.Append (bfi);
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker)
{
bbfi.Append (bfi);
bbfi_marker.Append(&bdr_marker);
bdr.Append (bfi);
}
void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi)
{
tfbfi.Append (bfi);
}
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi)
{
btfbfi.Append(bfi);
btfbfi_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
{
btfbfi.Append(bfi);
btfbfi_marker.Append(&bdr_marker);
skt.Append (bfi);
}
void MixedBilinearForm::Assemble (int skip_zeros)
{
if (ext)
{
ext->Assemble();
return;
}
int i, k;
Array<int> tr_vdofs, te_vdofs;
ElementTransformation *eltrans;
DenseMatrix elemmat;
@@ -1266,75 +1104,48 @@ void MixedBilinearForm::Assemble (int skip_zeros)
mat = new SparseMatrix(height, width);
}
if (dbfi.Size())
if (dom.Size())
{
for (int i = 0; i < test_fes -> GetNE(); i++)
for (i = 0; i < test_fes -> GetNE(); i++)
{
trial_fes -> GetElementVDofs (i, tr_vdofs);
test_fes -> GetElementVDofs (i, te_vdofs);
eltrans = test_fes -> GetElementTransformation (i);
for (int k = 0; k < dbfi.Size(); k++)
for (k = 0; k < dom.Size(); k++)
{
dbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
dom[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (bbfi.Size())
if (bdr.Size())
{
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bbfi.Size(); k++)
for (i = 0; i < test_fes -> GetNBE(); i++)
{
if (bbfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bbfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < test_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
test_fes -> GetBdrElementVDofs (i, te_vdofs);
eltrans = test_fes -> GetBdrElementTransformation (i);
for (int k = 0; k < bbfi.Size(); k++)
for (k = 0; k < bdr.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetBE(i),
*test_fes -> GetBE(i),
*eltrans, elemmat);
bdr[k] -> AssembleElementMatrix2 (*trial_fes -> GetBE(i),
*test_fes -> GetBE(i),
*eltrans, elemmat);
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (tfbfi.Size())
if (skt.Size())
{
FaceElementTransformations *ftr;
Array<int> te_vdofs2;
const FiniteElement *trial_face_fe, *test_fe1, *test_fe2;
int nfaces = mesh->GetNumFaces();
for (int i = 0; i < nfaces; i++)
for (i = 0; i < nfaces; i++)
{
ftr = mesh->GetFaceElementTransformations(i);
trial_fes->GetFaceVDofs(i, tr_vdofs);
@@ -1354,80 +1165,18 @@ void MixedBilinearForm::Assemble (int skip_zeros)
// want to actually make a fake element.
test_fe2 = test_fe1;
}
for (int k = 0; k < tfbfi.Size(); k++)
for (int k = 0; k < skt.Size(); k++)
{
tfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
skt[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (btfbfi.Size())
{
FaceElementTransformations *ftr;
Array<int> te_vdofs2;
const FiniteElement *trial_face_fe, *test_fe1, *test_fe2;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < btfbfi.Size(); k++)
{
if (btfbfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *btfbfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary trace face integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < trial_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
ftr = mesh->GetBdrFaceTransformations(i);
if (ftr)
{
trial_fes->GetFaceVDofs(i, tr_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
trial_face_fe = trial_fes->GetFaceElement(i);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
test_fe2 = test_fe1;
for (int k = 0; k < btfbfi.Size(); k++)
{
if (btfbfi_marker[k] &&
(*btfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
btfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
}
}
void MixedBilinearForm::ConformingAssemble()
{
if (assembly != AssemblyLevel::FULL)
{
MFEM_WARNING("Conforming assemble not supported for this assembly level!");
return;
}
Finalize();
const SparseMatrix *P2 = test_fes->GetConformingProlongation();
@@ -1452,93 +1201,8 @@ void MixedBilinearForm::ConformingAssemble()
width = mat->Width();
}
void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
{
if (dbfi.Size())
{
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
const FiniteElement &test_fe = *test_fes->GetFE(i);
ElementTransformation *eltrans = test_fes->GetElementTransformation(i);
dbfi[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
{
dbfi[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
trial_fes->GetElementVDofs(i, trial_vdofs);
test_fes->GetElementVDofs(i, test_vdofs);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
}
}
void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
{
if (bbfi.Size())
{
const FiniteElement &trial_be = *trial_fes->GetBE(i);
const FiniteElement &test_be = *test_fes->GetBE(i);
ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i);
bbfi[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
{
bbfi[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
trial_fes->GetBdrElementVDofs(i, trial_vdofs);
test_fes->GetBdrElementVDofs(i, test_vdofs);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
}
}
void MixedBilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleElementMatrix(i, elmat, trial_vdofs, test_vdofs, skip_zeros);
}
void MixedBilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &trial_vdofs,
Array<int> &test_vdofs, int skip_zeros)
{
trial_fes->GetElementVDofs(i, trial_vdofs);
test_fes->GetElementVDofs(i, test_vdofs);
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
mat->AddSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros);
}
void MixedBilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleBdrElementMatrix(i, elmat, trial_vdofs, test_vdofs, skip_zeros);
}
void MixedBilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &trial_vdofs,
Array<int> &test_vdofs, int skip_zeros)
{
trial_fes->GetBdrElementVDofs(i, trial_vdofs);
test_fes->GetBdrElementVDofs(i, test_vdofs);
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
mat->AddSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros);
}
void MixedBilinearForm::EliminateTrialDofs (
const Array<int> &bdr_attr_is_ess, const Vector &sol, Vector &rhs )
Array<int> &bdr_attr_is_ess, const Vector &sol, Vector &rhs )
{
int i, j, k;
Array<int> tr_vdofs, cols_marker (trial_fes -> GetVSize());
@@ -1561,12 +1225,12 @@ void MixedBilinearForm::EliminateTrialDofs (
}
void MixedBilinearForm::EliminateEssentialBCFromTrialDofs (
const Array<int> &marked_vdofs, const Vector &sol, Vector &rhs)
Array<int> &marked_vdofs, const Vector &sol, Vector &rhs)
{
mat -> EliminateCols (marked_vdofs, &sol, &rhs);
}
void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
void MixedBilinearForm::EliminateTestDofs (Array<int> &bdr_attr_is_ess)
{
int i, j, k;
Array<int> te_vdofs;
@@ -1586,100 +1250,24 @@ void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
}
}
void MixedBilinearForm::FormRectangularSystemMatrix(const Array<int>
&trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A)
{
if (ext)
{
ext->FormRectangularSystemOperator(trial_tdof_list, test_tdof_list, A);
return;
}
const SparseMatrix *test_P = test_fes->GetConformingProlongation();
const SparseMatrix *trial_P = trial_fes->GetConformingProlongation();
mat->Finalize();
if (test_P) // TODO: Must actually check for trial_P too
{
SparseMatrix *m = RAP(*test_P, *mat, *trial_P);
delete mat;
mat = m;
}
Array<int> ess_trial_tdof_marker, ess_test_tdof_marker;
FiniteElementSpace::ListToMarker(trial_tdof_list, trial_fes->GetTrueVSize(),
ess_trial_tdof_marker);
FiniteElementSpace::ListToMarker(test_tdof_list, test_fes->GetTrueVSize(),
ess_test_tdof_marker);
mat_e = new SparseMatrix(mat->Height(), mat->Width());
mat->EliminateCols(ess_trial_tdof_marker, *mat_e);
for (int i=0; i<test_tdof_list.Size(); ++i)
{
mat->EliminateRow(test_tdof_list[i]);
}
mat_e->Finalize();
A.Reset(mat, false);
}
void MixedBilinearForm::FormRectangularLinearSystem(const Array<int>
&trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B)
{
if (ext)
{
ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, A, X,
B);
return;
}
const Operator *Po = this->GetOutputProlongation();
const Operator *Ri = this->GetRestriction();
InitTVectors(Po, Ri, x, b, X, B);
if (!mat_e)
{
FormRectangularSystemMatrix(trial_tdof_list, test_tdof_list,
A); // Set A = mat_e
}
// Eliminate essential BCs with B -= Ab xb
mat_e->AddMult(X, B, -1.0);
B.SetSubVector(test_tdof_list, 0.0);
}
void MixedBilinearForm::Update()
{
delete mat;
mat = NULL;
delete mat_e;
mat_e = NULL;
height = test_fes->GetVSize();
width = trial_fes->GetVSize();
if (ext) { ext->Update(); }
}
MixedBilinearForm::~MixedBilinearForm()
{
if (mat) { delete mat; }
if (mat_e) { delete mat_e; }
if (!extern_bfs)
{
int i;
for (i = 0; i < dbfi.Size(); i++) { delete dbfi[i]; }
for (i = 0; i < bbfi.Size(); i++) { delete bbfi[i]; }
for (i = 0; i < tfbfi.Size(); i++) { delete tfbfi[i]; }
for (i = 0; i < btfbfi.Size(); i++) { delete btfbfi[i]; }
for (i = 0; i < dom.Size(); i++) { delete dom[i]; }
for (i = 0; i < bdr.Size(); i++) { delete bdr[i]; }
for (i = 0; i < skt.Size(); i++) { delete skt[i]; }
}
delete ext;
}
@@ -1695,7 +1283,7 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
mat = new SparseMatrix(height, width);
}
if (dbfi.Size() > 0)
if (dom.Size() > 0)
{
for (int i = 0; i < test_fes->GetNE(); i++)
{
@@ -1705,17 +1293,17 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
dom_fe = trial_fes->GetFE(i);
ran_fe = test_fes->GetFE(i);
dbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < dbfi.Size(); j++)
dom[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < dom.Size(); j++)
{
dbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
dom[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
totelmat += elmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
}
}
if (tfbfi.Size())
if (skt.Size())
{
const int nfaces = test_fes->GetMesh()->GetNumFaces();
for (int i = 0; i < nfaces; i++)
@@ -1726,10 +1314,10 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
dom_fe = trial_fes->GetFaceElement(i);
ran_fe = test_fes->GetFaceElement(i);
tfbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < tfbfi.Size(); j++)
skt[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < skt.Size(); j++)
{
tfbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
skt[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
totelmat += elmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
+18 -250
View File
@@ -58,7 +58,7 @@ protected:
/// FE space on which the form lives. Not owned.
FiniteElementSpace *fes;
/// The assembly level of the form (full, partial, etc.)
/// The form assembly level (full, partial, etc.)
AssemblyLevel assembly;
/// Element batch size used in the form action (1, 8, num_elems, etc.)
int batch;
@@ -227,7 +227,7 @@ public:
virtual const double &Elem(int i, int j) const;
/// Matrix vector multiplication.
virtual void Mult(const Vector &x, Vector &y) const;
virtual void Mult(const Vector &x, Vector &y) const { mat->Mult(x, y); }
void FullMult(const Vector &x, Vector &y) const
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
@@ -319,26 +319,12 @@ public:
/// Assembles the form i.e. sums over all domain/bdr integrators.
void Assemble(int skip_zeros = 1);
/** @brief Assemble the diagonal of the bilinear form into diag
For adaptively refined meshes, this returns P^T d_e, where d_e is the
locally assembled diagonal on each element and P^T is the transpose of
the conforming prolongation. In general this is not the correct diagonal
for an AMR mesh. */
void AssembleDiagonal(Vector &diag) const;
/// Get the finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return fes->GetConformingProlongation(); }
/// Get the finite element space restriction matrix
virtual const Operator *GetRestriction() const
{ return fes->GetConformingRestriction(); }
/// Get the output finite element space prolongation matrix
virtual const Operator *GetOutputProlongation() const
{ return GetProlongation(); }
/// Get the output finite element space restriction matrix
virtual const Operator *GetOutputRestriction() const
{ return GetRestriction(); }
/** @brief Form the linear system A X = B, corresponding to this bilinear
form and the linear form @a b(.). */
@@ -427,49 +413,9 @@ public:
void FreeElementMatrices()
{ delete element_matrices; element_matrices = NULL; }
/// Compute the element matrix of the given element
/** The element matrix is computed by calling the domain integrators
or the one stored internally by a prior call of ComputeElementMatrices()
is returned when available.
*/
void ComputeElementMatrix(int i, DenseMatrix &elmat);
/// Compute the boundary element matrix of the given boundary element
void ComputeBdrElementMatrix(int i, DenseMatrix &elmat);
/// Assemble the given element matrix
/** The element matrix @a elmat is assembled for the element @a i, i.e.
added to the system matrix. The flag @a skip_zeros skips the zero
elements of the matrix, unless they are breaking the symmetry of
the system matrix.
*/
void AssembleElementMatrix(int i, const DenseMatrix &elmat,
int skip_zeros = 1);
/// Assemble the given element matrix
/** The element matrix @a elmat is assembled for the element @a i, i.e.
added to the system matrix. The vdofs of the element are returned
in @a vdofs. The flag @a skip_zeros skips the zero elements of the
matrix, unless they are breaking the symmetry of the system matrix.
*/
void AssembleElementMatrix(int i, const DenseMatrix &elmat,
Array<int> &vdofs, int skip_zeros = 1);
/// Assemble the given boundary element matrix
/** The boundary element matrix @a elmat is assembled for the boundary
element @a i, i.e. added to the system matrix. The flag @a skip_zeros
skips the zero elements of the matrix, unless they are breaking the
symmetry of the system matrix.
*/
void AssembleBdrElementMatrix(int i, const DenseMatrix &elmat,
int skip_zeros = 1);
/// Assemble the given boundary element matrix
/** The boundary element matrix @a elmat is assembled for the boundary
element @a i, i.e. added to the system matrix. The vdofs of the element
are returned in @a vdofs. The flag @a skip_zeros skips the zero elements
of the matrix, unless they are breaking the symmetry of the system matrix.
*/
void AssembleBdrElementMatrix(int i, const DenseMatrix &elmat,
Array<int> &vdofs, int skip_zeros = 1);
@@ -539,9 +485,6 @@ public:
/// Sets diagonal policy used upon construction of the linear system
void SetDiagonalPolicy(DiagonalPolicy policy);
/// Indicate that integrators are not owned by the BilinearForm
void UseExternalIntegrators() { extern_bfs = 1; };
/// Destroys bilinear form.
virtual ~BilinearForm();
};
@@ -566,37 +509,20 @@ class MixedBilinearForm : public Matrix
{
protected:
SparseMatrix *mat; ///< Owned.
SparseMatrix *mat_e; ///< Owned.
FiniteElementSpace *trial_fes, ///< Not owned
*test_fes; ///< Not owned
/// The form assembly level (full, partial, etc.)
AssemblyLevel assembly;
/** Extension for supporting Full Assembly (FA), Element Assembly (EA),
Partial Assembly (PA), or Matrix Free assembly (MF). */
MixedBilinearFormExtension *ext;
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
#tfbfi and #btfbfi are owned by another MixedBilinearForm. */
/** @brief Indicates the BilinearFormIntegrator%s stored in #dom, #bdr, and
#skt are owned by another MixedBilinearForm. */
int extern_bfs;
/// Domain integrators.
Array<BilinearFormIntegrator*> dbfi;
Array<BilinearFormIntegrator*> dom;
/// Boundary integrators.
Array<BilinearFormIntegrator*> bbfi;
Array<Array<int>*> bbfi_marker;///< Entries are not owned.
Array<BilinearFormIntegrator*> bdr;
/// Trace face (skeleton) integrators.
Array<BilinearFormIntegrator*> tfbfi;
/// Boundary trace face (skeleton) integrators.
Array<BilinearFormIntegrator*> btfbfi;
Array<Array<int>*> btfbfi_marker;///< Entries are not owned.
DenseMatrix elemmat;
Array<int> trial_vdofs, test_vdofs;
Array<BilinearFormIntegrator*> skt;
private:
/// Copy construction is not supported; body is undefined.
@@ -631,13 +557,16 @@ public:
virtual const double &Elem(int i, int j) const;
virtual void Mult(const Vector & x, Vector & y) const;
virtual void AddMult(const Vector & x, Vector & y,
const double a = 1.0) const;
virtual void MultTranspose(const Vector & x, Vector & y) const;
virtual void AddMultTranspose(const Vector & x, Vector & y,
const double a = 1.0) const;
virtual void MultTranspose(const Vector & x, Vector & y) const
{ y = 0.0; AddMultTranspose (x, y); }
virtual MatrixInverse *Inverse() const;
virtual void Finalize(int skip_zeros = 1);
@@ -657,10 +586,6 @@ public:
/// Adds a boundary integrator. Assumes ownership of @a bfi.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi);
/// Adds a boundary integrator. Assumes ownership of @a bfi.
void AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
This type of integrator assembles terms over all faces of the mesh using
@@ -668,57 +593,19 @@ public:
test space. */
void AddTraceFaceIntegrator(BilinearFormIntegrator *bfi);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator (BilinearFormIntegrator * bfi);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
/// Access all integrators added with AddDomainIntegrator().
Array<BilinearFormIntegrator*> *GetDBFI() { return &dbfi; }
Array<BilinearFormIntegrator*> *GetDBFI() { return &dom; }
/// Access all integrators added with AddBoundaryIntegrator().
Array<BilinearFormIntegrator*> *GetBBFI() { return &bbfi; }
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBBFI_Marker() { return &bbfi_marker; }
Array<BilinearFormIntegrator*> *GetBBFI() { return &bdr; }
/// Access all integrators added with AddTraceFaceIntegrator().
Array<BilinearFormIntegrator*> *GetTFBFI() { return &tfbfi; }
/// Access all integrators added with AddBdrTraceFaceIntegrator().
Array<BilinearFormIntegrator*> *GetBTFBFI() { return &btfbfi; }
/** @brief Access all boundary markers added with AddBdrTraceFaceIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBTFBFI_Marker() { return &btfbfi_marker; }
Array<BilinearFormIntegrator*> *GetTFBFI() { return &skt; }
void operator=(const double a) { *mat = a; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
void Assemble(int skip_zeros = 1);
/// Get the input finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return trial_fes->GetProlongationMatrix(); }
/// Get the input finite element space restriction matrix
virtual const Operator *GetRestriction() const
{ return trial_fes->GetRestrictionMatrix(); }
/// Get the test finite element space prolongation matrix
virtual const Operator *GetOutputProlongation() const
{ return test_fes->GetProlongationMatrix(); }
/// Get the test finite element space restriction matrix
virtual const Operator *GetOutputRestriction() const
{ return test_fes->GetRestrictionMatrix(); }
/** For partially conforming trial and/or test FE spaces, complete the
assembly process by performing A := P2^t A P1 where A is the internal
sparse matrix; P1 and P2 are the conforming prolongation matrices of the
@@ -726,135 +613,16 @@ public:
MixedBilinearForm becomes an operator on the conforming FE spaces. */
void ConformingAssemble();
/// Compute the element matrix of the given element
void ComputeElementMatrix(int i, DenseMatrix &elmat);
/// Compute the boundary element matrix of the given boundary element
void ComputeBdrElementMatrix(int i, DenseMatrix &elmat);
/// Assemble the given element matrix
/** The element matrix @a elmat is assembled for the element @a i, i.e.
added to the system matrix. The flag @a skip_zeros skips the zero
elements of the matrix, unless they are breaking the symmetry of
the system matrix.
*/
void AssembleElementMatrix(int i, const DenseMatrix &elmat,
int skip_zeros = 1);
/// Assemble the given element matrix
/** The element matrix @a elmat is assembled for the element @a i, i.e.
added to the system matrix. The vdofs of the element are returned
in @a trial_vdofs and @a test_vdofs. The flag @a skip_zeros skips
the zero elements of the matrix, unless they are breaking the symmetry
of the system matrix.
*/
void AssembleElementMatrix(int i, const DenseMatrix &elmat,
Array<int> &trial_vdofs, Array<int> &test_vdofs,
int skip_zeros = 1);
/// Assemble the given boundary element matrix
/** The boundary element matrix @a elmat is assembled for the boundary
element @a i, i.e. added to the system matrix. The flag @a skip_zeros
skips the zero elements of the matrix, unless they are breaking the
symmetry of the system matrix.
*/
void AssembleBdrElementMatrix(int i, const DenseMatrix &elmat,
int skip_zeros = 1);
/// Assemble the given boundary element matrix
/** The boundary element matrix @a elmat is assembled for the boundary
element @a i, i.e. added to the system matrix. The vdofs of the element
are returned in @a trial_vdofs and @a test_vdofs. The flag @a skip_zeros
skips the zero elements of the matrix, unless they are breaking the
symmetry of the system matrix.
*/
void AssembleBdrElementMatrix(int i, const DenseMatrix &elmat,
Array<int> &trial_vdofs, Array<int> &test_vdofs,
int skip_zeros = 1);
void EliminateTrialDofs(const Array<int> &bdr_attr_is_ess,
void EliminateTrialDofs(Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs);
void EliminateEssentialBCFromTrialDofs(const Array<int> &marked_vdofs,
void EliminateEssentialBCFromTrialDofs(Array<int> &marked_vdofs,
const Vector &sol, Vector &rhs);
virtual void EliminateTestDofs(const Array<int> &bdr_attr_is_ess);
/** @brief Return in @a A a parallel (on truedofs) version of this operator.
This returns the same operator as FormRectangularLinearSystem(), but does
without the transformations of the right-hand side. */
void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A);
/** @brief Form the column-constrained linear system matrix A.
See FormRectangularSystemMatrix() for details.
Version of the method FormRectangularSystemMatrix() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list, OpType &A)
{
OperatorHandle Ah;
FormRectangularSystemMatrix(trial_tdof_list, test_tdof_list, Ah);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
/** @brief Form the linear system A X = B, corresponding to this mixed bilinear
form and the linear form @a b(.).
Return in @a A a *reference* to the system matrix that is column-constrained.
The reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called. */
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B);
/** @brief Form the linear system A X = B, corresponding to this bilinear
form and the linear form @a b(.).
Version of the method FormRectangularLinearSystem() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OpType &A, Vector &X, Vector &B)
{
OperatorHandle Ah;
FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, Ah, X, B);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
virtual void EliminateTestDofs(Array<int> &bdr_attr_is_ess);
void Update();
/// Return the trial FE space associated with the BilinearForm.
FiniteElementSpace *TrialFESpace() { return trial_fes; }
/// Read-only access to the associated trial FiniteElementSpace.
const FiniteElementSpace *TrialFESpace() const { return trial_fes; }
/// Return the test FE space associated with the BilinearForm.
FiniteElementSpace *TestFESpace() { return test_fes; }
/// Read-only access to the associated test FiniteElementSpace.
const FiniteElementSpace *TestFESpace() const { return test_fes; }
virtual ~MixedBilinearForm();
};
@@ -916,7 +684,7 @@ public:
{ AddTraceFaceIntegrator(di); }
/// Access all interpolators added with AddDomainInterpolator().
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
Array<BilinearFormIntegrator*> *GetDI() { return &dom; }
/** @brief Construct the internal matrix representation of the discrete
linear operator. */
+135 -273
View File
@@ -14,7 +14,6 @@
#include "../general/forall.hpp"
#include "bilinearform.hpp"
#include "libceed/ceed.hpp"
namespace mfem
{
@@ -37,19 +36,16 @@ const Operator *BilinearFormExtension::GetRestriction() const
// Data and methods for partially-assembled bilinear forms
PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form),
trialFes(a->FESpace()),
testFes(a->FESpace())
PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form) :
BilinearFormExtension(form),
trialFes(a->FESpace()), testFes(a->FESpace()),
localX(trialFes->GetNE() * trialFes->GetFE(0)->GetDof() * trialFes->GetVDim()),
localY( testFes->GetNE() * testFes->GetFE(0)->GetDof() * testFes->GetVDim()),
elem_restrict(new ElemRestriction(*a->FESpace())) { }
PABilinearFormExtension::~PABilinearFormExtension()
{
elem_restrict_lex = trialFes->GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC);
if (elem_restrict_lex)
{
localX.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType());
localY.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType());
localY.UseDevice(true); // ensure 'localY = 0.0' is done on device
}
delete elem_restrict;
}
void PABilinearFormExtension::Assemble()
@@ -58,32 +54,7 @@ void PABilinearFormExtension::Assemble()
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssemblePA(*a->FESpace());
}
}
void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (elem_restrict_lex)
{
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AssembleDiagonalPA(localY);
}
elem_restrict_lex->MultTranspose(localY, y);
}
else
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AssembleDiagonalPA(y);
}
integrators[i]->Assemble(*a->FESpace());
}
}
@@ -93,21 +64,23 @@ void PABilinearFormExtension::Update()
height = width = fes->GetVSize();
trialFes = fes;
testFes = fes;
elem_restrict_lex = trialFes->GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC);
if (elem_restrict_lex)
{
localX.SetSize(elem_restrict_lex->Height());
localY.SetSize(elem_restrict_lex->Height());
}
localX.SetSize(trialFes->GetNE() * trialFes->GetFE(0)->GetDof() *
trialFes->GetVDim());
localY.SetSize(testFes->GetNE() * testFes->GetFE(0)->GetDof() *
testFes->GetVDim());
delete elem_restrict;
elem_restrict = new ElemRestriction(*fes);
}
void PABilinearFormExtension::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
Operator *oper;
Operator::FormSystemOperator(ess_tdof_list, oper);
A.Reset(oper); // A will own oper
const Operator* trialP = trialFes->GetProlongationMatrix();
const Operator* testP = testFes->GetProlongationMatrix();
Operator *rap = this;
if (trialP) { rap = new RAPOperator(*testP, *this, *trialP); }
const bool own_A = (rap!=this);
A.Reset(new ConstrainedOperator(rap, ess_tdof_list, own_A));
}
void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
@@ -124,251 +97,140 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
elem_restrict->Mult(x, localX);
localY = 0.0;
const int iSz = integrators.Size();
if (DeviceCanUseCeed() || !elem_restrict_lex)
for (int i = 0; i < iSz; ++i)
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(x, y);
}
}
else
{
elem_restrict_lex->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(localX, localY);
}
elem_restrict_lex->MultTranspose(localY, y);
integrators[i]->MultAssembled(localX, localY);
}
elem_restrict->MultTranspose(localY, y);
}
void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
elem_restrict->Mult(x, localX);
localY = 0.0;
const int iSz = integrators.Size();
if (elem_restrict_lex)
{
elem_restrict_lex->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultTransposePA(localX, localY);
}
elem_restrict_lex->MultTranspose(localY, y);
}
else
{
y.UseDevice(true);
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultTransposePA(x, y);
}
}
}
MixedBilinearFormExtension::MixedBilinearFormExtension(MixedBilinearForm *form)
: Operator(form->Height(), form->Width()), a(form)
{
// empty
}
const Operator *MixedBilinearFormExtension::GetProlongation() const
{
return a->GetProlongation();
}
const Operator *MixedBilinearFormExtension::GetRestriction() const
{
return a->GetRestriction();
}
const Operator *MixedBilinearFormExtension::GetOutputProlongation() const
{
return a->GetOutputProlongation();
}
const Operator *MixedBilinearFormExtension::GetOutputRestriction() const
{
return a->GetOutputRestriction();
}
// Data and methods for partially-assembled bilinear forms
PAMixedBilinearFormExtension::PAMixedBilinearFormExtension(
MixedBilinearForm *form)
: MixedBilinearFormExtension(form),
trialFes(form->TrialFESpace()),
testFes(form->TestFESpace()),
elem_restrict_trial(NULL),
elem_restrict_test(NULL)
{
Update();
}
void PAMixedBilinearFormExtension::Assemble()
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssemblePA(*trialFes, *testFes);
}
}
void PAMixedBilinearFormExtension::Update()
{
trialFes = a->TrialFESpace();
testFes = a->TestFESpace();
height = testFes->GetVSize();
width = trialFes->GetVSize();
elem_restrict_trial = trialFes->GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC);
elem_restrict_test = testFes->GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC);
if (elem_restrict_trial)
{
localTrial.UseDevice(true);
localTrial.SetSize(elem_restrict_trial->Height(), Device::GetMemoryType());
}
if (elem_restrict_test)
{
localTest.UseDevice(true); // ensure 'localY = 0.0' is done on device
localTest.SetSize(elem_restrict_test->Height(), Device::GetMemoryType());
}
}
void PAMixedBilinearFormExtension::FormRectangularSystemOperator(
const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A)
{
Operator * oper;
Operator::FormRectangularSystemOperator(trial_tdof_list, test_tdof_list, oper);
A.Reset(oper); // A will own oper
}
void PAMixedBilinearFormExtension::FormRectangularLinearSystem(
const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B)
{
Operator *oper;
Operator::FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b,
oper, X, B);
A.Reset(oper); // A will own oper
}
void PAMixedBilinearFormExtension::SetupMultInputs(const Operator
*elem_restrict_x,
const Vector &x,
Vector &localX,
const Operator *elem_restrict_y,
Vector &y,
Vector &localY,
const double c) const
{
// * G operation: localX = c*local(x)
if (elem_restrict_x)
{
elem_restrict_x->Mult(x, localX);
if (c != 1.0)
{
localX *= c;
}
}
else
{
if (c == 1.0)
{
localX.SyncAliasMemory(x);
}
else
{
localX.Set(c, x);
}
}
if (elem_restrict_y)
{
localY = 0.0;
}
else
{
y.UseDevice(true);
localY.SyncAliasMemory(y);
}
}
void PAMixedBilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
y = 0.0;
AddMult(x, y);
}
void PAMixedBilinearFormExtension::AddMult(const Vector &x, Vector &y,
const double c) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
// * G operation
SetupMultInputs(elem_restrict_trial, x, localTrial,
elem_restrict_test, y, localTest, c);
// * B^TDB operation
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(localTrial, localTest);
}
// * G^T operation
if (elem_restrict_test)
{
tempY.SetSize(y.Size());
elem_restrict_test->MultTranspose(localTest, tempY);
y += tempY;
integrators[i]->MultAssembledTranspose(localX, localY);
}
elem_restrict->MultTranspose(localY, y);
}
void PAMixedBilinearFormExtension::MultTranspose(const Vector &x,
Vector &y) const
ElemRestriction::ElemRestriction(const FiniteElementSpace &f)
: fes(f),
ne(fes.GetNE()),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
ndofs(fes.GetNDofs()),
dof(fes.GetFE(0)->GetDof()),
nedofs(ne*dof),
offsets(ndofs+1),
indices(ne*dof)
{
y = 0.0;
AddMultTranspose(x, y);
for (int e = 0; e < ne; ++e)
{
const FiniteElement *fe = fes.GetFE(e);
const TensorBasisElement* el =
dynamic_cast<const TensorBasisElement*>(fe);
if (el) { continue; }
mfem_error("Finite element not supported with partial assembly");
}
const FiniteElement *fe = fes.GetFE(0);
const TensorBasisElement* el = dynamic_cast<const TensorBasisElement*>(fe);
const Array<int> &dof_map = el->GetDofMap();
const bool dof_map_is_identity = (dof_map.Size()==0);
const Table& e2dTable = fes.GetElementToDofTable();
const int* elementMap = e2dTable.GetJ();
// We'll be keeping a count of how many local nodes point to its global dof
for (int i = 0; i <= ndofs; ++i)
{
offsets[i] = 0;
}
for (int e = 0; e < ne; ++e)
{
for (int d = 0; d < dof; ++d)
{
const int gid = elementMap[dof*e + d];
++offsets[gid + 1];
}
}
// Aggregate to find offsets for each global dof
for (int i = 1; i <= ndofs; ++i)
{
offsets[i] += offsets[i - 1];
}
// For each global dof, fill in all local nodes that point to it
for (int e = 0; e < ne; ++e)
{
for (int d = 0; d < dof; ++d)
{
const int did = dof_map_is_identity?d:dof_map[d];
const int gid = elementMap[dof*e + did];
const int lid = dof*e + d;
indices[offsets[gid]++] = lid;
}
}
// We shifted the offsets vector by 1 by using it as a counter
// Now we shift it back.
for (int i = ndofs; i > 0; --i)
{
offsets[i] = offsets[i - 1];
}
offsets[0] = 0;
}
void PAMixedBilinearFormExtension::AddMultTranspose(const Vector &x, Vector &y,
const double c) const
void ElemRestriction::Mult(const Vector& x, Vector& y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
// * G operation
SetupMultInputs(elem_restrict_test, x, localTest,
elem_restrict_trial, y, localTrial, c);
// * B^TD^TB operation
for (int i = 0; i < iSz; ++i)
const int vd = vdim;
const bool t = byvdim;
const DeviceArray d_offsets(offsets, ndofs+1);
const DeviceArray d_indices(indices, nedofs);
const DeviceMatrix d_x(x, t?vd:ndofs, t?ndofs:vd);
DeviceMatrix d_y(y, t?vd:nedofs, t?nedofs:vd);
MFEM_FORALL(i, ndofs,
{
integrators[i]->AddMultTransposePA(localTest, localTrial);
}
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i+1];
for (int c = 0; c < vd; ++c)
{
const double dofValue = d_x(t?c:i,t?i:c);
for (int j = offset; j < nextOffset; ++j)
{
const int idx_j = d_indices[j];
d_y(t?c:idx_j,t?idx_j:c) = dofValue;
}
}
});
}
// * G^T operation
if (elem_restrict_trial)
void ElemRestriction::MultTranspose(const Vector& x, Vector& y) const
{
const int vd = vdim;
const bool t = byvdim;
const DeviceArray d_offsets(offsets, ndofs+1);
const DeviceArray d_indices(indices, nedofs);
const DeviceMatrix d_x(x, t?vd:nedofs, t?nedofs:vd);
DeviceMatrix d_y(y, t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
tempY.SetSize(y.Size());
elem_restrict_trial->MultTranspose(localTrial, tempY);
y += tempY;
}
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i + 1];
for (int c = 0; c < vd; ++c)
{
double dofValue = 0;
for (int j = offset; j < nextOffset; ++j)
{
const int idx_j = d_indices[j];
dofValue += d_x(t?c:idx_j,t?idx_j:c);
}
d_y(t?c:i,t?i:c) = dofValue;
}
});
}
} // namespace mfem
+23 -110
View File
@@ -14,17 +14,32 @@
#include "../config/config.hpp"
#include "fespace.hpp"
#include "../general/device.hpp"
namespace mfem
{
class BilinearForm;
class MixedBilinearForm;
/// Element restriction operator
class ElemRestriction: public Operator
{
public:
const FiniteElementSpace &fes;
const int ne;
const int vdim;
const bool byvdim;
const int ndofs;
const int dof;
const int nedofs;
Array<int> offsets;
Array<int> indices;
public:
ElemRestriction(const FiniteElementSpace&);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
};
/** @brief Class extending the BilinearForm class to support the different
AssemblyLevel%s. */
class BilinearFormExtension : public Operator
{
protected:
@@ -33,9 +48,6 @@ protected:
public:
BilinearFormExtension(BilinearForm *form);
virtual MemoryClass GetMemoryClass() const
{ return Device::GetMemoryClass(); }
/// Get the finite element space prolongation matrix
virtual const Operator *GetProlongation() const;
@@ -43,10 +55,6 @@ public:
virtual const Operator *GetRestriction() const;
virtual void Assemble() = 0;
virtual void AssembleDiagonal(Vector &diag) const
{
MFEM_ABORT("AssembleDiagonal not implemented for this assembly level!");
}
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A) = 0;
virtual void FormLinearSystem(const Array<int> &ess_tdof_list,
@@ -72,7 +80,6 @@ public:
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
void Update() {}
~FABilinearFormExtension() {}
};
@@ -92,7 +99,6 @@ public:
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
void Update() {}
~EABilinearFormExtension() {}
};
@@ -100,15 +106,14 @@ public:
class PABilinearFormExtension : public BilinearFormExtension
{
protected:
const FiniteElementSpace *trialFes, *testFes; // Not owned
const FiniteElementSpace *trialFes, *testFes;
mutable Vector localX, localY;
const Operator *elem_restrict_lex; // Not owned
ElemRestriction *elem_restrict;
public:
PABilinearFormExtension(BilinearForm*);
void Assemble();
void AssembleDiagonal(Vector &diag) const;
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
@@ -118,8 +123,9 @@ public:
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
};
~PABilinearFormExtension();
};
/// Data and methods for matrix-free bilinear forms
class MFBilinearFormExtension : public BilinearFormExtension
@@ -137,102 +143,9 @@ public:
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
void Update() {}
~MFBilinearFormExtension() {}
};
/** @brief Class extending the MixedBilinearForm class to support the different
AssemblyLevel%s. */
class MixedBilinearFormExtension : public Operator
{
protected:
MixedBilinearForm *a; ///< Not owned
public:
MixedBilinearFormExtension(MixedBilinearForm *form);
virtual MemoryClass GetMemoryClass() const
{ return Device::GetMemoryClass(); }
/// Get the finite element space prolongation matrix
virtual const Operator *GetProlongation() const;
/// Get the finite element space restriction matrix
virtual const Operator *GetRestriction() const;
/// Get the output finite element space restriction matrix
virtual const Operator *GetOutputProlongation() const;
/// Get the output finite element space restriction matrix
virtual const Operator *GetOutputRestriction() const;
virtual void Assemble() = 0;
virtual void FormRectangularSystemOperator(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A) = 0;
virtual void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B) = 0;
virtual void AddMult(const Vector &x, Vector &y, const double c=1.0) const = 0;
virtual void AddMultTranspose(const Vector &x, Vector &y,
const double c=1.0) const = 0;
virtual void Update() = 0;
};
/// Data and methods for partially-assembled mixed bilinear forms
class PAMixedBilinearFormExtension : public MixedBilinearFormExtension
{
protected:
const FiniteElementSpace *trialFes, *testFes; // Not owned
mutable Vector localTrial, localTest, tempY;
const Operator *elem_restrict_trial; // Not owned
const Operator *elem_restrict_test; // Not owned
private:
/// Helper function to set up inputs/outputs for Mult or MultTranspose
void SetupMultInputs(const Operator *elem_restrict_x,
const Vector &x, Vector &localX,
const Operator *elem_restrict_y,
Vector &y, Vector &localY, const double c) const;
public:
PAMixedBilinearFormExtension(MixedBilinearForm *form);
/// Partial assembly of all internal integrators
void Assemble();
/**
@brief Setup OperatorHandle A to contain constrained linear operator
OperatorHandle A contains matrix-free constrained operator formed for RAP
system where ess_tdof_list are in trial space and eliminated from
"columns" of A.
*/
void FormRectangularSystemOperator(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A);
/**
Setup OperatorHandle A to contain constrained linear operator and
eliminate columns corresponding to essential dofs from system,
updating RHS B vector with the results.
*/
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B);
/// y = A*x
void Mult(const Vector &x, Vector &y) const;
/// y += c*A*x
void AddMult(const Vector &x, Vector &y, const double c=1.0) const;
/// y = A^T*x
void MultTranspose(const Vector &x, Vector &y) const;
/// y += c*A^T*x
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
/// Update internals for when a new MixedBilinearForm is given to this class
void Update();
};
}
#endif
+109 -146
View File
@@ -19,33 +19,19 @@ using namespace std;
namespace mfem
{
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
void BilinearFormIntegrator::Assemble(const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
mfem_error ("BilinearFormIntegrator::Assemble (...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleDiagonalPA(Vector &)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA (...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
void BilinearFormIntegrator::MultAssembled(Vector&, Vector&)
{
mfem_error ("BilinearFormIntegrator::MultAssembled (...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
void BilinearFormIntegrator::MultAssembledTranspose(Vector&, Vector&)
{
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose (...)\n"
" is not implemented for this class.");
@@ -392,73 +378,6 @@ void MixedScalarVectorIntegrator::AssembleElementMatrix2(
}
}
void GradientIntegrator::AssembleElementMatrix2(
const FiniteElement &trial_fe, const FiniteElement &test_fe,
ElementTransformation &Trans, DenseMatrix &elmat)
{
int dim = test_fe.GetDim();
int trial_dof = trial_fe.GetDof();
int test_dof = test_fe.GetDof();
double c;
Vector d_col;
dshape.SetSize(trial_dof, dim);
gshape.SetSize(trial_dof, dim);
Jadj.SetSize(dim);
shape.SetSize(test_dof);
elmat.SetSize(dim * test_dof, trial_dof);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(trial_fe, test_fe,
Trans);
elmat = 0.0;
elmat_comp.SetSize(test_dof, trial_dof);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trial_fe.CalcDShape(ip, dshape);
test_fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
CalcAdjugate(Trans.Jacobian(), Jadj);
Mult(dshape, Jadj, gshape);
c = ip.weight;
if (Q)
{
c *= Q->Eval(Trans, ip);
}
shape *= c;
for (int d = 0; d < dim; ++d)
{
gshape.GetColumnReference(d, d_col);
MultVWt(shape, d_col, elmat_comp);
for (int jj = 0; jj < trial_dof; ++jj)
{
for (int ii = 0; ii < test_dof; ++ii)
{
elmat(d * test_dof + ii, jj) += elmat_comp(ii, jj);
}
}
}
}
}
const IntegrationRule &GradientIntegrator::GetRule(const FiniteElement
&trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans)
{
int order = Trans.OrderGrad(&trial_fe) + test_fe.GetOrder() + Trans.OrderJ();
return IntRules.Get(trial_fe.GetGeomType(), order);
}
void DiffusionIntegrator::AssembleElementMatrix
( const FiniteElement &el, ElementTransformation &Trans,
DenseMatrix &elmat )
@@ -478,7 +397,29 @@ void DiffusionIntegrator::AssembleElementMatrix
#endif
elmat.SetSize(nd);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order;
if (el.Space() == FunctionSpace::Pk)
{
order = 2*el.GetOrder() - 2;
}
else
// order = 2*el.GetOrder() - 2; // <-- this seems to work fine too
{
order = 2*el.GetOrder() + dim - 1;
}
if (el.Space() == FunctionSpace::rQk)
{
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
}
else
{
ir = &IntRules.Get(el.GetGeomType(), order);
}
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -534,7 +475,28 @@ void DiffusionIntegrator::AssembleElementMatrix2(
#endif
elmat.SetSize(te_nd, tr_nd);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(trial_fe, test_fe);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order;
if (trial_fe.Space() == FunctionSpace::Pk)
{
order = trial_fe.GetOrder() + test_fe.GetOrder() - 2;
}
else
{
order = trial_fe.GetOrder() + test_fe.GetOrder() + dim - 1;
}
if (trial_fe.Space() == FunctionSpace::rQk)
{
ir = &RefinedIntRules.Get(trial_fe.GetGeomType(), order);
}
else
{
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -589,7 +551,29 @@ void DiffusionIntegrator::AssembleElementVector(
elvect.SetSize(nd);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order;
if (el.Space() == FunctionSpace::Pk)
{
order = 2*el.GetOrder() - 2;
}
else
// order = 2*el.GetOrder() - 2; // <-- this seems to work fine too
{
order = 2*el.GetOrder() + dim - 1;
}
if (el.Space() == FunctionSpace::rQk)
{
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
}
else
{
ir = &IntRules.Get(el.GetGeomType(), order);
}
}
elvect = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -749,27 +733,6 @@ double DiffusionIntegrator::ComputeFluxEnergy
return energy;
}
const IntegrationRule &DiffusionIntegrator::GetRule(
const FiniteElement &trial_fe, const FiniteElement &test_fe)
{
int order;
if (trial_fe.Space() == FunctionSpace::Pk)
{
order = trial_fe.GetOrder() + test_fe.GetOrder() - 2;
}
else
{
// order = 2*el.GetOrder() - 2; // <-- this seems to work fine too
order = trial_fe.GetOrder() + test_fe.GetOrder() + trial_fe.GetDim() - 1;
}
if (trial_fe.Space() == FunctionSpace::rQk)
{
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
}
return IntRules.Get(trial_fe.GetGeomType(), order);
}
void MassIntegrator::AssembleElementMatrix
( const FiniteElement &el, ElementTransformation &Trans,
@@ -785,7 +748,21 @@ void MassIntegrator::AssembleElementMatrix
elmat.SetSize(nd);
shape.SetSize(nd);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, Trans);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
// int order = 2 * el.GetOrder();
int order = 2 * el.GetOrder() + Trans.OrderW();
if (el.Space() == FunctionSpace::rQk)
{
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
}
else
{
ir = &IntRules.Get(el.GetGeomType(), order);
}
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -820,8 +797,13 @@ void MassIntegrator::AssembleElementMatrix2(
shape.SetSize(tr_nd);
te_shape.SetSize(te_nd);
const IntegrationRule *ir = IntRule ? IntRule :
&GetRule(trial_fe, test_fe, Trans);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -842,20 +824,6 @@ void MassIntegrator::AssembleElementMatrix2(
}
}
const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans)
{
// int order = trial_fe.GetOrder() + test_fe.GetOrder();
const int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
if (trial_fe.Space() == FunctionSpace::rQk)
{
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
}
return IntRules.Get(trial_fe.GetGeomType(), order);
}
void BoundaryMassIntegrator::AssembleFaceMatrix(
const FiniteElement &el1, const FiniteElement &el2,
@@ -927,7 +895,7 @@ void ConvectionIntegrator::AssembleElementMatrix(
ir = &IntRules.Get(el.GetGeomType(), order);
}
Q->Eval(Q_ir, Trans, *ir);
Q.Eval(Q_ir, Trans, *ir);
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -968,7 +936,7 @@ void GroupConvectionIntegrator::AssembleElementMatrix(
ir = &IntRules.Get(el.GetGeomType(), order);
}
Q->Eval(Q_nodal, Trans, el.GetNodes()); // sets the size of Q_nodal
Q.Eval(Q_nodal, Trans, el.GetNodes()); // sets the size of Q_nodal
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -1449,7 +1417,7 @@ void DerivativeIntegrator::AssembleElementMatrix2 (
dshapedxi(l) = dshapedxt(l,xi);
}
shape *= Q->Eval(Trans,ip) * det * ip.weight;
shape *= Q.Eval(Trans,ip) * det * ip.weight;
AddMultVWt (shape, dshapedxi, elmat);
}
}
@@ -2023,8 +1991,12 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
elmat.SetSize (test_dof, dim*trial_dof);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(trial_fe, test_fe,
Trans);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = Trans.OrderGrad(&trial_fe) + test_fe.GetOrder();
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
elmat = 0.0;
@@ -2054,15 +2026,6 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
}
}
const IntegrationRule &VectorDivergenceIntegrator::GetRule(
const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans)
{
int order = Trans.OrderGrad(&trial_fe) + test_fe.GetOrder() + Trans.OrderJ();
return IntRules.Get(trial_fe.GetGeomType(), order);
}
void DivDivIntegrator::AssembleElementMatrix(
const FiniteElement &el,
@@ -3300,7 +3263,7 @@ ScalarProductInterpolator::AssembleElementMatrix2(const FiniteElement &dom_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
internal::ShapeCoefficient dom_shape_coeff(*Q, dom_fe);
internal::ShapeCoefficient dom_shape_coeff(Q, dom_fe);
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
@@ -3335,7 +3298,7 @@ ScalarVectorProductInterpolator::AssembleElementMatrix2(
}
};
VShapeCoefficient dom_shape_coeff(*Q, dom_fe, Trans.GetSpaceDim());
VShapeCoefficient dom_shape_coeff(Q, dom_fe, Trans.GetSpaceDim());
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
@@ -3373,7 +3336,7 @@ VectorScalarProductInterpolator::AssembleElementMatrix2(
}
};
VecShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
VecShapeCoefficient dom_shape_coeff(VQ, dom_fe);
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
@@ -3420,11 +3383,11 @@ VectorCrossProductInterpolator::AssembleElementMatrix2(
}
};
VCrossVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
VCrossVShapeCoefficient dom_shape_coeff(VQ, dom_fe);
if (ran_fe.GetRangeType() == FiniteElement::SCALAR)
{
elmat.SetSize(ran_fe.GetDof()*VQ->GetVDim(),dom_fe.GetDof());
elmat.SetSize(ran_fe.GetDof()*VQ.GetVDim(),dom_fe.GetDof());
}
else
{
@@ -3473,7 +3436,7 @@ VectorInnerProductInterpolator::AssembleElementMatrix2(
ElementTransformation &Trans,
DenseMatrix &elmat)
{
internal::VDotVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
internal::VDotVShapeCoefficient dom_shape_coeff(VQ, dom_fe);
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
+69 -309
View File
@@ -15,7 +15,7 @@
#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "fespace.hpp"
#include "libceed/ceed.hpp"
#include "bilininteg_ext.hpp"
namespace mfem
{
@@ -23,53 +23,19 @@ namespace mfem
/// Abstract base class BilinearFormIntegrator
class BilinearFormIntegrator : public NonlinearFormIntegrator
{
protected:
BilinearFormIntegrator(const IntegrationRule *ir = NULL)
: NonlinearFormIntegrator(ir) { }
public:
BilinearFormIntegrator(const IntegrationRule *ir = NULL) :
NonlinearFormIntegrator(ir) { }
public:
// TODO: add support for other assembly levels (in addition to PA) and their
// actions.
// TODO: for mixed meshes the quadrature rules to be used by methods like
// AssemblePA() can be given as a QuadratureSpace, e.g. using a new method:
// SetQuadratureSpace().
// TODO: the methods for the various assembly levels make sense even in the
// base class NonlinearFormIntegrator, except that not all assembly levels
// make sense for the action of the nonlinear operator (but they all make
// sense for its Jacobian).
using NonlinearFormIntegrator::AssemblePA;
/// Method defining partial assembly.
/** The result of the partial assembly is stored internally so that it can be
used later in the methods AddMultPA() and AddMultTransposePA(). */
virtual void AssemblePA(const FiniteElementSpace &fes);
/** Used with BilinearFormIntegrators that have different spaces. */
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
/// Assemble diagonal and add it to Vector @a diag.
virtual void AssembleDiagonalPA(Vector &diag);
virtual void Assemble(const FiniteElementSpace&);
/// Method for partially assembled action.
/** Perform the action of integrator on the input @a x and add the result to
the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
the element-wise discontinuous version of the FE space.
This method can be called only after the method AssemblePA() has been
called. */
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void MultAssembled(Vector&, Vector&);
/// Method for partially assembled transposed action.
/** Perform the transpose action of integrator on the input @a x and add the
result to the output @a y. Both @a x and @a y are E-vectors, i.e. they
represent the element-wise discontinuous version of the FE space.
This method can be called only after the method AssemblePA() has been
called. */
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
virtual void MultAssembledTranspose(Vector&, Vector&);
/// Given a particular Finite Element computes the element matrix elmat.
virtual void AssembleElementMatrix(const FiniteElement &el,
@@ -318,10 +284,10 @@ protected:
Vector & shape)
{ trial_fe.CalcPhysShape(Trans, shape); }
Coefficient *Q;
private:
Coefficient *Q;
#ifndef MFEM_THREAD_SAFE
Vector test_shape;
Vector trial_shape;
@@ -392,13 +358,13 @@ protected:
DenseMatrix & shape)
{ trial_fe.CalcVShape(Trans, shape); }
private:
Coefficient *Q;
VectorCoefficient *VQ;
VectorCoefficient *DQ;
MatrixCoefficient *MQ;
private:
#ifndef MFEM_THREAD_SAFE
Vector V;
Vector D;
@@ -473,12 +439,12 @@ protected:
Vector & shape)
{ scalar_fe.CalcPhysShape(Trans, shape); }
private:
VectorCoefficient *VQ;
bool transpose;
bool cross_2d; // In 2D use a cross product rather than a dot product
private:
#ifndef MFEM_THREAD_SAFE
Vector V;
DenseMatrix vshape;
@@ -1667,125 +1633,31 @@ protected:
}
};
/** Class for integrating the bilinear form a(u,v) := (Q grad u, v) where Q is a
scalar coefficient, and v is a vector with components v_i in the same space
as u. */
class GradientIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *Q;
private:
Vector shape;
DenseMatrix dshape;
DenseMatrix gshape;
DenseMatrix Jadj;
DenseMatrix elmat_comp;
// PA extension
Vector pa_data;
const DofToQuad *trial_maps, *test_maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq;
int trial_dofs1D, test_dofs1D, quad1D;
public:
GradientIntegrator() :
Q{NULL}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
{ }
GradientIntegrator(Coefficient *_q) :
Q{_q}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
{ }
GradientIntegrator(Coefficient &q) :
Q{&q}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
{ }
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans);
};
/** Class for integrating the bilinear form a(u,v) := (Q grad u, grad v) where Q
can be a scalar or a matrix coefficient. */
class DiffusionIntegrator: public BilinearFormIntegrator
{
protected:
Coefficient *Q;
MatrixCoefficient *MQ;
private:
Vector vec, pointflux, shape;
#ifndef MFEM_THREAD_SAFE
DenseMatrix dshape, dshapedxt, invdfdx, mq;
DenseMatrix te_dshape, te_dshapedxt;
#endif
Coefficient *Q;
MatrixCoefficient *MQ;
// PA extension
const FiniteElementSpace *fespace;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
DofToQuad *maps;
GeometryExtension *geom;
int dim, ne, dofs1D, quad1D;
Vector pa_data;
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
#endif
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator()
{
Q = NULL;
MQ = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
DiffusionIntegrator() { Q = NULL; MQ = NULL; maps = NULL; geom = NULL; }
/// Construct a diffusion integrator with a scalar coefficient q
DiffusionIntegrator(Coefficient &q)
: Q(&q)
{
MQ = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
DiffusionIntegrator (Coefficient &q) : Q(&q) { MQ = NULL; maps = NULL; geom = NULL; }
/// Construct a diffusion integrator with a matrix coefficient q
DiffusionIntegrator(MatrixCoefficient &q)
: MQ(&q)
{
Q = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
virtual ~DiffusionIntegrator()
{
#ifdef MFEM_USE_CEED
delete ceedDataPtr;
#endif
}
DiffusionIntegrator (MatrixCoefficient &q) : MQ(&q) { Q = NULL; maps = NULL; geom = NULL; }
/** Given a particular Finite Element
computes the element stiffness matrix elmat. */
@@ -1813,18 +1685,11 @@ public:
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
using BilinearFormIntegrator::AssemblePA;
/// PA extension
virtual void Assemble(const FiniteElementSpace&);
virtual void MultAssembled(Vector&, Vector&);
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
void SetupPA(const FiniteElementSpace &fes, const bool force = false);
virtual ~DiffusionIntegrator();
};
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
@@ -1836,46 +1701,17 @@ protected:
#endif
Coefficient *Q;
// PA extension
const FiniteElementSpace *fespace;
Vector pa_data;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
Vector vec;
DofToQuad *maps;
GeometryExtension *geom;
int dim, ne, nq, dofs1D, quad1D;
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
#endif
public:
MassIntegrator(const IntegrationRule *ir = NULL)
: BilinearFormIntegrator(ir)
{
Q = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
: BilinearFormIntegrator(ir) { Q = NULL; maps = NULL; geom = NULL; }
/// Construct a mass integrator with coefficient q
MassIntegrator(Coefficient &q, const IntegrationRule *ir = NULL)
: BilinearFormIntegrator(ir), Q(&q)
{
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
: BilinearFormIntegrator(ir), Q(&q) { maps = NULL; geom = NULL; }
virtual ~MassIntegrator()
{
#ifdef MFEM_USE_CEED
delete ceedDataPtr;
#endif
}
/** Given a particular Finite Element
computes the element mass matrix elmat. */
virtual void AssembleElementMatrix(const FiniteElement &el,
@@ -1885,20 +1721,11 @@ public:
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
/// PA extension
virtual void Assemble(const FiniteElementSpace&);
virtual void MultAssembled(Vector&, Vector&);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans);
void SetupPA(const FiniteElementSpace &fes, const bool force = false);
virtual ~MassIntegrator();
};
class BoundaryMassIntegrator : public MassIntegrator
@@ -1917,19 +1744,17 @@ public:
/// alpha (q . grad u, v)
class ConvectionIntegrator : public BilinearFormIntegrator
{
protected:
VectorCoefficient *Q;
double alpha;
private:
#ifndef MFEM_THREAD_SAFE
DenseMatrix dshape, adjJ, Q_ir;
Vector shape, vec2, BdFidxT;
#endif
VectorCoefficient &Q;
double alpha;
public:
ConvectionIntegrator(VectorCoefficient &q, double a = 1.0)
: Q(&q) { alpha = a; }
: Q(q) { alpha = a; }
virtual void AssembleElementMatrix(const FiniteElement &,
ElementTransformation &,
DenseMatrix &);
@@ -1938,17 +1763,15 @@ public:
/// alpha (q . grad u, v) using the "group" FE discretization
class GroupConvectionIntegrator : public BilinearFormIntegrator
{
protected:
VectorCoefficient *Q;
double alpha;
private:
DenseMatrix dshape, adjJ, Q_nodal, grad;
Vector shape;
VectorCoefficient &Q;
double alpha;
public:
GroupConvectionIntegrator(VectorCoefficient &q, double a = 1.0)
: Q(&q) { alpha = a; }
: Q(q) { alpha = a; }
virtual void AssembleElementMatrix(const FiniteElement &,
ElementTransformation &,
DenseMatrix &);
@@ -1964,22 +1787,16 @@ private:
Vector shape, te_shape, vec;
DenseMatrix partelmat;
DenseMatrix mcoeff;
int Q_order;
protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
// PA extension
Vector pa_data;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
int Q_order;
public:
/// Construct an integrator with coefficient 1.0
VectorMassIntegrator()
: vdim(-1), Q_order(0), Q(NULL), VQ(NULL), MQ(NULL) { }
: vdim(-1), Q(NULL), VQ(NULL), MQ(NULL), Q_order(0) { }
/** Construct an integrator with scalar coefficient q.
If possible, save memory by using a scalar integrator since
the resulting matrix is block diagonal with the same diagonal
@@ -2006,9 +1823,6 @@ public:
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
};
@@ -2021,14 +1835,11 @@ public:
does NOT depend on the ElementTransformation Trans. */
class VectorFEDivergenceIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *Q;
private:
Coefficient *Q;
#ifndef MFEM_THREAD_SAFE
Vector divshape, shape;
#endif
public:
VectorFEDivergenceIntegrator() { Q = NULL; }
VectorFEDivergenceIntegrator(Coefficient &q) { Q = &q; }
@@ -2046,17 +1857,14 @@ public:
This is equivalent to a weak divergence of the Nedelec basis functions. */
class VectorFEWeakDivergenceIntegrator: public BilinearFormIntegrator
{
protected:
Coefficient *Q;
private:
Coefficient *Q;
#ifndef MFEM_THREAD_SAFE
DenseMatrix dshape;
DenseMatrix dshapedxt;
DenseMatrix vshape;
DenseMatrix invdfdx;
#endif
public:
VectorFEWeakDivergenceIntegrator() { Q = NULL; }
VectorFEWeakDivergenceIntegrator(Coefficient &q) { Q = &q; }
@@ -2073,16 +1881,13 @@ public:
test spaces are switched, assembles the form (u, curl v). */
class VectorFECurlIntegrator: public BilinearFormIntegrator
{
protected:
Coefficient *Q;
private:
Coefficient *Q;
#ifndef MFEM_THREAD_SAFE
DenseMatrix curlshapeTrial;
DenseMatrix vshapeTest;
DenseMatrix curlshapeTrial_dFT;
#endif
public:
VectorFECurlIntegrator() { Q = NULL; }
VectorFECurlIntegrator(Coefficient &q) { Q = &q; }
@@ -2095,19 +1900,17 @@ public:
DenseMatrix &elmat);
};
/// Class for integrating (Q D_i(u), v); u and v are scalars
class DerivativeIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient* Q;
private:
Coefficient & Q;
int xi;
DenseMatrix dshape, dshapedxt, invdfdx;
Vector shape, dshapedxi;
public:
DerivativeIntegrator(Coefficient &q, int i) : Q(&q), xi(i) { }
DerivativeIntegrator(Coefficient &q, int i) : Q(q), xi(i) { }
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat)
@@ -2127,8 +1930,6 @@ private:
DenseMatrix curlshape, curlshape_dFt, M;
DenseMatrix vshape, projcurl;
#endif
protected:
Coefficient *Q;
MatrixCoefficient *MQ;
@@ -2162,8 +1963,6 @@ private:
#ifndef MFEM_THREAD_SAFE
DenseMatrix dshape_hat, dshape, curlshape, Jadj, grad_hat, grad;
#endif
protected:
Coefficient *Q;
public:
@@ -2185,6 +1984,9 @@ public:
class VectorFEMassIntegrator: public BilinearFormIntegrator
{
private:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
void Init(Coefficient *q, VectorCoefficient *vq, MatrixCoefficient *mq)
{ Q = q; VQ = vq; MQ = mq; }
@@ -2196,11 +1998,6 @@ private:
DenseMatrix trial_vshape;
#endif
protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
public:
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL); }
@@ -2223,57 +2020,32 @@ public:
scalar FE space; p is also in a (different) scalar FE space. */
class VectorDivergenceIntegrator : public BilinearFormIntegrator
{
protected:
private:
Coefficient *Q;
private:
Vector shape;
Vector divshape;
DenseMatrix dshape;
DenseMatrix gshape;
DenseMatrix Jadj;
// PA extension
Vector pa_data;
const DofToQuad *trial_maps, *test_maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq;
int trial_dofs1D, test_dofs1D, quad1D;
public:
VectorDivergenceIntegrator() :
Q(NULL), trial_maps(NULL), test_maps(NULL), geom(NULL)
{ }
VectorDivergenceIntegrator(Coefficient *_q) :
Q(_q), trial_maps(NULL), test_maps(NULL), geom(NULL)
{ }
VectorDivergenceIntegrator(Coefficient &q) :
Q(&q), trial_maps(NULL), test_maps(NULL), geom(NULL)
{ }
VectorDivergenceIntegrator() { Q = NULL; }
VectorDivergenceIntegrator(Coefficient *_q) { Q = _q; }
VectorDivergenceIntegrator(Coefficient &q) { Q = &q; }
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans);
};
/// (Q div u, div v) for RT elements
class DivDivIntegrator: public BilinearFormIntegrator
{
protected:
private:
Coefficient *Q;
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape;
#endif
@@ -2295,16 +2067,9 @@ public:
diffusion matrix in each diagonal block. */
class VectorDiffusionIntegrator : public BilinearFormIntegrator
{
protected:
private:
Coefficient *Q;
// PA extension
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, dofs1D, quad1D;
Vector pa_data;
private:
DenseMatrix Jinv;
DenseMatrix dshape;
DenseMatrix gshape;
@@ -2320,9 +2085,6 @@ public:
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
};
/** Integrator for the linear elasticity form:
@@ -2332,11 +2094,10 @@ public:
using multiple copies of a scalar FE space. */
class ElasticityIntegrator : public BilinearFormIntegrator
{
protected:
private:
double q_lambda, q_mu;
Coefficient *lambda, *mu;
private:
#ifndef MFEM_THREAD_SAFE
Vector shape;
DenseMatrix dshape, gshape, pelmat;
@@ -2393,12 +2154,11 @@ public:
points. */
class DGTraceIntegrator : public BilinearFormIntegrator
{
protected:
private:
Coefficient *rho;
VectorCoefficient *u;
double alpha, beta;
private:
Vector shape1, shape2;
public:
@@ -2685,7 +2445,7 @@ public:
class ScalarProductInterpolator : public DiscreteInterpolator
{
public:
ScalarProductInterpolator(Coefficient & sc) : Q(&sc) { }
ScalarProductInterpolator(Coefficient & sc) : Q(sc) { }
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
const FiniteElement &ran_fe,
@@ -2693,7 +2453,7 @@ public:
DenseMatrix &elmat);
protected:
Coefficient *Q;
Coefficient &Q;
};
/** Interpolator of a scalar coefficient multiplied by a vector field onto
@@ -2703,14 +2463,14 @@ class ScalarVectorProductInterpolator : public DiscreteInterpolator
{
public:
ScalarVectorProductInterpolator(Coefficient & sc)
: Q(&sc) { }
: Q(sc) { }
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
const FiniteElement &ran_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
protected:
Coefficient *Q;
Coefficient &Q;
};
/** Interpolator of a vector coefficient multiplied by a scalar field onto
@@ -2720,14 +2480,14 @@ class VectorScalarProductInterpolator : public DiscreteInterpolator
{
public:
VectorScalarProductInterpolator(VectorCoefficient & vc)
: VQ(&vc) { }
: VQ(vc) { }
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
const FiniteElement &ran_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
protected:
VectorCoefficient *VQ;
VectorCoefficient &VQ;
};
/** Interpolator of the cross product between a vector coefficient and an
@@ -2737,14 +2497,14 @@ class VectorCrossProductInterpolator : public DiscreteInterpolator
{
public:
VectorCrossProductInterpolator(VectorCoefficient & vc)
: VQ(&vc) { }
: VQ(vc) { }
virtual void AssembleElementMatrix2(const FiniteElement &nd_fe,
const FiniteElement &rt_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
protected:
VectorCoefficient *VQ;
VectorCoefficient &VQ;
};
/** Interpolator of the inner product between a vector coefficient and an
@@ -2753,14 +2513,14 @@ protected:
class VectorInnerProductInterpolator : public DiscreteInterpolator
{
public:
VectorInnerProductInterpolator(VectorCoefficient & vc) : VQ(&vc) { }
VectorInnerProductInterpolator(VectorCoefficient & vc) : VQ(vc) { }
virtual void AssembleElementMatrix2(const FiniteElement &rt_fe,
const FiniteElement &l2_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
protected:
VectorCoefficient *VQ;
VectorCoefficient &VQ;
};
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_BILININTEG_EXT
#define MFEM_BILININTEG_EXT
#include "fespace.hpp"
namespace mfem
{
/// GeometryExtension
class GeometryExtension
{
public:
Array<int> eMap;
Array<double> nodes;
Array<double> X, J, invJ, detJ;
static GeometryExtension* Get(const FiniteElementSpace&,
const IntegrationRule&);
static GeometryExtension* Get(const FiniteElementSpace&,
const IntegrationRule&,
const Vector&);
static void ReorderByVDim(const GridFunction*);
static void ReorderByNodes(const GridFunction*);
};
/// DofToQuad
class DofToQuad
{
private:
std::string hash;
public:
~DofToQuad();
void operator=(DofToQuad&);
void operator=(DofToQuad const&);
public:
Array<double> W, B, G, Bt, Gt;
public:
static DofToQuad* Get(const FiniteElementSpace&,
const IntegrationRule&,
const bool = false);
static DofToQuad* Get(const FiniteElementSpace&,
const FiniteElementSpace&,
const IntegrationRule&,
const bool = false);
static DofToQuad* Get(const FiniteElement&,
const FiniteElement&,
const IntegrationRule&,
const bool = false);
static DofToQuad* GetTensorMaps(const FiniteElement&,
const FiniteElement&,
const IntegrationRule&,
const bool = false);
static DofToQuad* GetD2QTensorMaps(const FiniteElement&,
const IntegrationRule&,
const bool = false);
static DofToQuad* GetSimplexMaps(const FiniteElement&,
const IntegrationRule&,
const bool = false);
static DofToQuad* GetSimplexMaps(const FiniteElement&,
const FiniteElement&,
const IntegrationRule&,
const bool = false);
static DofToQuad* GetD2QSimplexMaps(const FiniteElement&,
const IntegrationRule&,
const bool = false);
};
}
#endif
-823
View File
@@ -1,823 +0,0 @@
// Copyright (c) 2019, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
using namespace std;
namespace mfem
{
// PA Gradient Integrator
/* Description of the *SetupND functions
Inputs are as follows
\b Q1D number of quadrature points in one dimension.
\b w quadrature weights.
\b j element Jacobians.
\b COEFF coefficient at quadrature points.
The function is used precompute data needed at quadrature points during
the action. */
/* Description of the *ApplyND functions
The template parameters are
\b T_D1D number of degrees of freedom in one dimension,
\b T_Q1D number of quadrature points in one dimension,
and are necessary to allow for compiler optimizations inside the kernel.
Inputs are as follows
\b NE number of elements.
\b B matrix of basis functions.
\b G matrix of derivatives of the basis functions.
\b Bt transpose of matrix of basis functions.
\b Gt transpose matrix of derivatives of the basis functions.
\b op data used during action of the element matrix in the tensor
product application.
\b x input vector of degrees of freedom on the element.
\b y output vector of degrees of freedom on the element.
The function computes the kernel for one dimension that is suitable for
tensor product action to form ND operators.
Most of the ND inputs are reshaped as NQ*(ND*ND)*NE data structure, i.e
to allow indexing such as op(qpt,i,j,el).
The output data structure is dependent on the kernel and layout of the
dimension ND and element number, but in general resembles the action of the
element matrix in the tensor product application. */
/* Description of the Smem*ApplyND functions
The shared memory (Smem) versions of the kernels differ from the regular
versions in the following properties.
\b MFEM_FORALL is using only one level of parallelism.
\b MFEM_FORALL_ND uses an additional level of parallelism
\b MFEM_FOREACH_THREAD
These macros allow automatic mapping of manually defined blocks to
underlying hardware threads. These threads can share memory by using
the \b MFEM_SHARED keyword for local arrays. */
// PA Gradient Assemble 2D kernel
static void PAGradientSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const double COEFF,
Vector &op)
{
const int NQ = Q1D*Q1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto y = Reshape(op.Write(), NQ, 2, 2, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double J11 = J(q,0,0,e);
const double J12 = J(q,0,1,e);
const double J21 = J(q,1,0,e);
const double J22 = J(q,1,1,e);
// Store wq * Q * adj(J)
y(q,0,0,e) = W[q] * COEFF * J22; // 1,1
y(q,0,1,e) = W[q] * COEFF * -J12; // 1,2
y(q,1,0,e) = W[q] * COEFF * -J21; // 2,1
y(q,1,1,e) = W[q] * COEFF * J11; // 2,2
}
});
}
// PA Gradient Assemble 3D kernel
static void PAGradientSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const double COEFF,
Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto y = Reshape(op.Write(), NQ, 3, 3, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J31 = J(q,2,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double J32 = J(q,2,1,e);
const double J13 = J(q,0,2,e);
const double J23 = J(q,1,2,e);
const double J33 = J(q,2,2,e);
const double cw = W[q] * COEFF;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
const double A13 = (J12 * J23) - (J22 * J13);
const double A21 = (J31 * J23) - (J21 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J21 * J13) - (J11 * J23);
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
// Store wq * Q * adj(J)
y(q,0,0,e) = cw * A11; // 1,1
y(q,0,1,e) = cw * A12; // 1,2
y(q,0,2,e) = cw * A13; // 1,3
y(q,1,0,e) = cw * A21; // 2,1
y(q,1,1,e) = cw * A22; // 2,2
y(q,1,2,e) = cw * A23; // 2,3
y(q,2,0,e) = cw * A31; // 3,1
y(q,2,1,e) = cw * A32; // 3,2
y(q,2,2,e) = cw * A33; // 3,3
}
});
}
static void PAGradientSetup(const int dim,
const int TR_D1D,
const int TE_D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const double COEFF,
Vector &op)
{
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PAGradientSetup"); }
if (dim == 2)
{
PAGradientSetup2D(Q1D, NE, W, J, COEFF, op);
}
if (dim == 3)
{
PAGradientSetup3D(Q1D, NE, W, J, COEFF, op);
}
}
void GradientIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes)
{
// Assumes tensor-product elements ordered by nodes
MFEM_ASSERT(trial_fes.GetOrdering() == Ordering::byNODES,
"PA Only supports Ordering::byNODES!");
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement &trial_fe = *trial_fes.GetFE(0);
const FiniteElement &test_fe = *test_fes.GetFE(0);
ElementTransformation *trans = mesh->GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(trial_fe, test_fe,
*trans);
const int dims = trial_fe.GetDim();
const int dimsToStore = dims * dims;
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = trial_fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
trial_maps = &trial_fe.GetDofToQuad(*ir, DofToQuad::TENSOR);
trial_dofs1D = trial_maps->ndof;
quad1D = trial_maps->nqpt;
test_maps = &test_fe.GetDofToQuad(*ir, DofToQuad::TENSOR);
test_dofs1D = test_maps->ndof;
MFEM_ASSERT(quad1D == test_maps->nqpt,
"PA requires test and trial space to have same number of quadrature points!");
pa_data.SetSize(nq * dimsToStore * ne, Device::GetMemoryType());
double coeff = 1.0;
if (Q)
{
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient*>(Q);
MFEM_VERIFY(cQ != NULL, "only ConstantCoefficient is supported!");
coeff = cQ->constant;
}
PAGradientSetup(dim, trial_dofs1D, test_dofs1D, quad1D,
ne, ir->GetWeights(), geom->J, coeff, pa_data);
}
// PA Gradient Apply 2D kernel
template<int T_TR_D1D = 0, int T_TE_D1D = 0, int T_Q1D = 0>
static void PAGradientApply2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Array<double> &bt,
const Vector &_op,
const Vector &_x,
Vector &_y,
const int tr_d1d = 0,
const int te_d1d = 0,
const int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, TR_D1D);
auto G = Reshape(g.Read(), Q1D, TR_D1D);
auto Bt = Reshape(bt.Read(), TE_D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D*Q1D, 2,2, NE);
auto x = Reshape(_x.Read(), TR_D1D, TR_D1D, NE);
auto y = Reshape(_y.ReadWrite(), TE_D1D, TE_D1D, 2, NE);
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = 2;
// the following variables are evaluated at compile time
constexpr int max_TE_D1D = T_TE_D1D ? T_TE_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double grad[max_Q1D][max_Q1D][VDIM];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qy][qx][0] = 0.0;
grad[qy][qx][1] = 0.0;
}
}
for (int dy = 0; dy < TR_D1D; ++dy)
{
double gradX[max_Q1D][VDIM];
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] = 0.0;
gradX[qx][1] = 0.0;
}
for (int dx = 0; dx < TR_D1D; ++dx)
{
const double s = x(dx,dy,e);
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] += s * G(qx,dx);
gradX[qx][1] += s * B(qx,dx);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B(qy,dy);
const double wDy = G(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qy][qx][0] += gradX[qx][0] * wy;
grad[qy][qx][1] += gradX[qx][1] * wDy;
}
}
}
// We've now calculated grad(p) = [Dxy, xDy] in plane
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + qy * Q1D;
const double gradX = grad[qy][qx][0];
const double gradY = grad[qy][qx][1];
grad[qy][qx][0] = gradX*op(q,0,0,e) + gradY*op(q,1,0,e);
grad[qy][qx][1] = gradX*op(q,0,1,e) + gradY*op(q,1,1,e);
}
}
// We've now calculated grad = grad p * op
for (int qy = 0; qy < Q1D; ++qy)
{
double opX[max_TE_D1D][VDIM];
for (int dx = 0; dx < TE_D1D; ++dx)
{
opX[dx][0] = 0.0;
opX[dx][1] = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
opX[dx][0] += Bt(dx,qx)*grad[qy][qx][0];
opX[dx][1] += Bt(dx,qx)*grad[qy][qx][1];
}
}
for (int dy = 0; dy < TE_D1D; ++dy)
{
for (int dx = 0; dx < TE_D1D; ++dx)
{
y(dx,dy,0,e) += Bt(dy,qy)*opX[dx][0];
y(dx,dy,1,e) += Bt(dy,qy)*opX[dx][1];
}
}
}
// We've now calculated y = u * grad
});
}
// PA Gradient Apply 2D kernel transpose
template<int T_TR_D1D = 0, int T_TE_D1D = 0, int T_Q1D = 0>
static void PAGradientApplyTranspose2D(const int NE,
const Array<double> &bt,
const Array<double> &gt,
const Array<double> &b,
const Vector &_op,
const Vector &_x,
Vector &_y,
const int tr_d1d = 0,
const int te_d1d = 0,
const int q1d = 0)
{
// TODO
MFEM_ASSERT(false, "GradientPAApplyTranspose 3D not implemented.");
}
// PA Gradient Apply 3D kernel
template<const int T_TR_D1D = 0, const int T_TE_D1D = 0, const int T_Q1D = 0>
static void PAGradientApply3D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Array<double> &bt,
const Vector &_op,
const Vector &_x,
Vector &_y,
int tr_d1d = 0,
int te_d1d = 0,
int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, TR_D1D);
auto G = Reshape(g.Read(), Q1D, TR_D1D);
auto Bt = Reshape(bt.Read(), TE_D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D*Q1D*Q1D, 3,3, NE);
auto x = Reshape(_x.Read(), TR_D1D, TR_D1D, TR_D1D, NE);
auto y = Reshape(_y.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, 3, NE);
MFEM_FORALL(e, NE,
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = 3;
// the following variables are evaluated at compile time
constexpr int max_TE_D1D = T_TE_D1D ? T_TE_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double grad[max_Q1D][max_Q1D][max_Q1D][VDIM];
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qz][qy][qx][0] = 0.0;
grad[qz][qy][qx][1] = 0.0;
grad[qz][qy][qx][2] = 0.0;
}
}
}
for (int dz = 0; dz < TR_D1D; ++dz)
{
double gradXY[max_Q1D][max_Q1D][3];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
gradXY[qy][qx][0] = 0.0;
gradXY[qy][qx][1] = 0.0;
gradXY[qy][qx][2] = 0.0;
}
}
for (int dy = 0; dy < TR_D1D; ++dy)
{
double gradX[max_Q1D][2];
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] = 0.0;
gradX[qx][1] = 0.0;
}
for (int dx = 0; dx < TR_D1D; ++dx)
{
const double s = x(dx,dy,dz,e);
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] += s * B(qx,dx);
gradX[qx][1] += s * G(qx,dx);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B(qy,dy);
const double wDy = G(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
const double wx = gradX[qx][0];
const double wDx = gradX[qx][1];
gradXY[qy][qx][0] += wDx * wy;
gradXY[qy][qx][1] += wx * wDy;
gradXY[qy][qx][2] += wx * wy;
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const double wz = B(qz,dz);
const double wDz = G(qz,dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qz][qy][qx][0] += gradXY[qy][qx][0] * wz;
grad[qz][qy][qx][1] += gradXY[qy][qx][1] * wz;
grad[qz][qy][qx][2] += gradXY[qy][qx][2] * wDz;
}
}
}
}
// We've now calculated grad(p) = [Dxyz, xDyz, xyDz] in plane
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const double gradX = grad[qz][qy][qx][0];
const double gradY = grad[qz][qy][qx][1];
const double gradZ = grad[qz][qy][qx][2];
grad[qz][qy][qx][0] = gradX*op(q,0,0,e) + gradY*op(q,1,0,e) + gradZ*op(q,2,0,e);
grad[qz][qy][qx][1] = gradX*op(q,0,1,e) + gradY*op(q,1,1,e) + gradZ*op(q,2,1,e);
grad[qz][qy][qx][2] = gradX*op(q,0,2,e) + gradY*op(q,1,2,e) + gradZ*op(q,2,2,e);
}
}
}
// We've now calculated grad = grad p * op
for (int qz = 0; qz < Q1D; ++qz)
{
double opXY[max_TE_D1D][max_TE_D1D][VDIM];
for (int dy = 0; dy < TE_D1D; ++dy)
{
for (int dx = 0; dx < TE_D1D; ++dx)
{
opXY[dy][dx][0] = 0.0;
opXY[dy][dx][1] = 0.0;
opXY[dy][dx][2] = 0.0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double opX[max_TE_D1D][VDIM];
for (int dx = 0; dx < TE_D1D; ++dx)
{
opX[dx][0] = 0.0;
opX[dx][1] = 0.0;
opX[dx][2] = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
opX[dx][0] += Bt(dx,qx)*grad[qz][qy][qx][0];
opX[dx][1] += Bt(dx,qx)*grad[qz][qy][qx][1];
opX[dx][2] += Bt(dx,qx)*grad[qz][qy][qx][2];
}
}
for (int dy = 0; dy < TE_D1D; ++dy)
{
for (int dx = 0; dx < TE_D1D; ++dx)
{
opXY[dy][dx][0] += Bt(dy,qy)*opX[dx][0];
opXY[dy][dx][1] += Bt(dy,qy)*opX[dx][1];
opXY[dy][dx][2] += Bt(dy,qy)*opX[dx][2];
}
}
}
for (int dz = 0; dz < TE_D1D; ++dz)
{
for (int dy = 0; dy < TE_D1D; ++dy)
{
for (int dx = 0; dx < TE_D1D; ++dx)
{
y(dx,dy,dz,0,e) += Bt(dz,qz)*opXY[dy][dx][0];
y(dx,dy,dz,1,e) += Bt(dz,qz)*opXY[dy][dx][1];
y(dx,dy,dz,2,e) += Bt(dz,qz)*opXY[dy][dx][2];
}
}
}
}
// We've now calculated y = u * grad
});
}
// PA Gradient Apply 3D kernel
template<const int T_TR_D1D = 0, const int T_TE_D1D = 0, const int T_Q1D = 0>
static void PAGradientApplyTranspose3D(const int NE,
const Array<double> &bt,
const Array<double> &gt,
const Array<double> &b,
const Vector &_op,
const Vector &_x,
Vector &_y,
int tr_d1d = 0,
int te_d1d = 0,
int q1d = 0)
{
MFEM_ASSERT(false, "Gradient PA Apply Transpose 3D not implemented.");
}
// Shared memory PA Gradient Apply 3D kernel
template<const int T_TR_D1D = 0, const int T_TE_D1D = 0, const int T_Q1D = 0>
static void SmemPAGradientApply3D(const int NE,
const Array<double> &b_,
const Array<double> &g_,
const Array<double> &bt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int tr_d1d = 0,
const int te_d1d = 0,
const int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= MAX_D1D, "");
MFEM_VERIFY(TR_D1D <= Q1D, "");
MFEM_VERIFY(TE_D1D <= Q1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto b = Reshape(b_.Read(), Q1D, TR_D1D);
auto g = Reshape(g_.Read(), Q1D, TR_D1D);
auto bt = Reshape(bt_.Read(), TE_D1D, Q1D);
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, 3, 3, NE);
auto x = Reshape(x_.Read(), TR_D1D, TR_D1D, TR_D1D, NE);
auto y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, 3, NE);
MFEM_FORALL_3D(e, NE, (Q1D>8)?8:Q1D, (Q1D>8)?8:Q1D, (Q1D>8)?8:Q1D,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1DR = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int D1DE = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1R = T_TR_D1D ? T_TR_D1D : MAX_D1D;
constexpr int MD1E = T_TE_D1D ? T_TE_D1D : MAX_D1D;
constexpr int MD1 = MD1E > MD1R ? MD1E : MD1R;
constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
MFEM_SHARED double sBG[2][MQ1*MD1];
double (*B)[MD1] = (double (*)[MD1]) (sBG+0);
double (*G)[MD1] = (double (*)[MD1]) (sBG+1);
double (*Bt)[MQ1] = (double (*)[MQ1]) (sBG+0);
MFEM_SHARED double sm0[3][MDQ*MDQ*MDQ];
MFEM_SHARED double sm1[3][MDQ*MDQ*MDQ];
double (*X)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+2);
double (*DDQ0)[MD1][MQ1] = (double (*)[MD1][MQ1]) (sm0+0);
double (*DDQ1)[MD1][MQ1] = (double (*)[MD1][MQ1]) (sm0+1);
double (*DQQ0)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) (sm1+0);
double (*DQQ1)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) (sm1+1);
double (*DQQ2)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) (sm1+2);
double (*QQQ0)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) (sm0+0);
double (*QQQ1)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) (sm0+1);
double (*QQQ2)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) (sm0+2);
double (*QQD0)[MQ1][MD1] = (double (*)[MQ1][MD1]) (sm1+0);
double (*QQD1)[MQ1][MD1] = (double (*)[MQ1][MD1]) (sm1+1);
double (*QQD2)[MQ1][MD1] = (double (*)[MQ1][MD1]) (sm1+2);
double (*QDD0)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+0);
double (*QDD1)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+1);
double (*QDD2)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+2);
MFEM_FOREACH_THREAD(dz,z,D1DR)
{
MFEM_FOREACH_THREAD(dy,y,D1DR)
{
MFEM_FOREACH_THREAD(dx,x,D1DR)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
}
}
}
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1DR)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
G[q][d] = g(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1DR)
{
MFEM_FOREACH_THREAD(dy,y,D1DR)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0;
double v = 0.0;
for (int dx = 0; dx < D1DR; ++dx)
{
const double coord = X[dz][dy][dx];
u += coord * B[qx][dx];
v += coord * G[qx][dx];
}
DDQ0[dz][dy][qx] = u;
DDQ1[dz][dy][qx] = v;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1DR)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0;
double v = 0.0;
double w = 0.0;
for (int dy = 0; dy < D1DR; ++dy)
{
u += DDQ1[dz][dy][qx] * B[qy][dy];
v += DDQ0[dz][dy][qx] * G[qy][dy];
w += DDQ0[dz][dy][qx] * B[qy][dy];
}
DQQ0[dz][qy][qx] = u;
DQQ1[dz][qy][qx] = v;
DQQ2[dz][qy][qx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0;
double v = 0.0;
double w = 0.0;
for (int dz = 0; dz < D1DR; ++dz)
{
u += DQQ0[dz][qy][qx] * B[qz][dz];
v += DQQ1[dz][qy][qx] * B[qz][dz];
w += DQQ2[dz][qy][qx] * G[qz][dz];
}
QQQ0[qz][qy][qx] = u;
QQQ1[qz][qy][qx] = v;
QQQ2[qz][qy][qx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const double gX = QQQ0[qz][qy][qx];
const double gY = QQQ1[qz][qy][qx];
const double gZ = QQQ2[qz][qy][qx];
QQQ0[qz][qy][qx] = (D(q,0,0,e)*gX) + (D(q,1,0,e)*gY) + (D(q,2,0,e)*gZ);
QQQ1[qz][qy][qx] = (D(q,0,1,e)*gX) + (D(q,1,1,e)*gY) + (D(q,2,1,e)*gZ);
QQQ2[qz][qy][qx] = (D(q,0,2,e)*gX) + (D(q,1,2,e)*gY) + (D(q,2,2,e)*gZ);
}
}
}
MFEM_SYNC_THREAD;
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1DE)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
Bt[d][q] = bt(d,q);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1DE)
{
double u = 0.0;
double v = 0.0;
double w = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += QQQ0[qz][qy][qx] * Bt[dx][qx];
v += QQQ1[qz][qy][qx] * Bt[dx][qx];
w += QQQ2[qz][qy][qx] * Bt[dx][qx];
}
QQD0[qz][qy][dx] = u;
QQD1[qz][qy][dx] = v;
QQD2[qz][qy][dx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1DE)
{
MFEM_FOREACH_THREAD(dx,x,D1DE)
{
double u = 0.0;
double v = 0.0;
double w = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += QQD0[qz][qy][dx] * Bt[dy][qy];
v += QQD1[qz][qy][dx] * Bt[dy][qy];
w += QQD2[qz][qy][dx] * Bt[dy][qy];
}
QDD0[qz][dy][dx] = u;
QDD1[qz][dy][dx] = v;
QDD2[qz][dy][dx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1DE)
{
MFEM_FOREACH_THREAD(dy,y,D1DE)
{
MFEM_FOREACH_THREAD(dx,x,D1DE)
{
double u = 0.0;
double v = 0.0;
double w = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += QDD0[qz][dy][dx] * Bt[dz][qz];
v += QDD1[qz][dy][dx] * Bt[dz][qz];
w += QDD2[qz][dy][dx] * Bt[dz][qz];
}
y(dx,dy,dz,0,e) += u;
y(dx,dy,dz,1,e) += v;
y(dx,dy,dz,2,e) += w;
}
}
}
});
}
static void PAGradientApply(const int dim,
const int TR_D1D,
const int TE_D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Vector &op,
const Vector &x,
Vector &y,
bool transpose=false)
{
if (dim == 2)
{
return PAGradientApply2D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
if (dim == 3)
{
return PAGradientApply3D(NE,B,G,Bt,op,x,y,TR_D1D,TE_D1D,Q1D);
}
MFEM_ABORT("Unknown kernel.");
}
// PA Gradient Apply kernel
void GradientIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PAGradientApply(dim, trial_dofs1D, test_dofs1D, quad1D, ne,
trial_maps->B, trial_maps->G, test_maps->Bt, pa_data, x, y,
false);
}
// PA Gradient Apply kernel
void GradientIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
MFEM_ABORT("PA Gradient AddMultTransposePA not implemented.");
}
} // namespace mfem
File diff suppressed because it is too large Load Diff
-503
View File
@@ -1,503 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
using namespace std;
namespace mfem
{
// PA Vector Diffusion Integrator
// PA Diffusion Assemble 2D kernel
static void PAVectorDiffusionSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const double COEFF,
Vector &op)
{
const int NQ = Q1D*Q1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto y = Reshape(op.Write(), NQ, 3, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double c_detJ = W[q] * COEFF / ((J11*J22)-(J21*J12));
y(q,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
y(q,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
y(q,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
}
});
}
// PA Diffusion Assemble 3D kernel
static void PAVectorDiffusionSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const double COEFF,
Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto y = Reshape(op.Write(), NQ, 6, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J31 = J(q,2,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double J32 = J(q,2,1,e);
const double J13 = J(q,0,2,e);
const double J23 = J(q,1,2,e);
const double J33 = J(q,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double c_detJ = W[q] * COEFF / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
const double A13 = (J12 * J23) - (J22 * J13);
const double A21 = (J31 * J23) - (J21 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J21 * J13) - (J11 * J23);
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
y(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
y(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
y(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
y(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
y(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
y(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
}
});
}
static void PAVectorDiffusionSetup(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
const Vector &J,
const double COEFF,
Vector &op)
{
if (!(dim == 2 || dim == 3))
{
MFEM_ABORT("Dimension not supported.");
}
if (dim == 2)
{
PAVectorDiffusionSetup2D(Q1D, NE, W, J, COEFF, op);
}
if (dim == 3)
{
PAVectorDiffusionSetup3D(Q1D, NE, W, J, COEFF, op);
}
}
void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir
= IntRule ? IntRule : &DiffusionIntegrator::GetRule(el, el);
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
double coeff = 1.0;
if (Q)
{
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient*>(Q);
MFEM_VERIFY(cQ != NULL, "only ConstantCoefficient is supported!");
coeff = cQ->constant;
}
PAVectorDiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
// PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0> static
void PAVectorDiffusionApply2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Array<double> &bt,
const Array<double> &gt,
const Vector &_op,
const Vector &_x,
Vector &_y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Bt = Reshape(bt.Read(), D1D, Q1D);
auto Gt = Reshape(gt.Read(), D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D*Q1D, 3, NE);
auto x = Reshape(_x.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(_y.ReadWrite(), D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
for (int c = 0; c < VDIM; ++ c)
{
double grad[max_Q1D][max_Q1D][2];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qy][qx][0] = 0.0;
grad[qy][qx][1] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
double gradX[max_Q1D][2];
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] = 0.0;
gradX[qx][1] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x(dx,dy,c,e);
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] += s * B(qx,dx);
gradX[qx][1] += s * G(qx,dx);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B(qy,dy);
const double wDy = G(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qy][qx][0] += gradX[qx][1] * wy;
grad[qy][qx][1] += gradX[qx][0] * wDy;
}
}
}
// Calculate Dxy, xDy in plane
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + qy * Q1D;
const double O11 = op(q,0,e);
const double O12 = op(q,1,e);
const double O22 = op(q,2,e);
const double gradX = grad[qy][qx][0];
const double gradY = grad[qy][qx][1];
grad[qy][qx][0] = (O11 * gradX) + (O12 * gradY);
grad[qy][qx][1] = (O12 * gradX) + (O22 * gradY);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double gradX[max_D1D][2];
for (int dx = 0; dx < D1D; ++dx)
{
gradX[dx][0] = 0;
gradX[dx][1] = 0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double gX = grad[qy][qx][0];
const double gY = grad[qy][qx][1];
for (int dx = 0; dx < D1D; ++dx)
{
const double wx = Bt(dx,qx);
const double wDx = Gt(dx,qx);
gradX[dx][0] += gX * wDx;
gradX[dx][1] += gY * wx;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double wy = Bt(dy,qy);
const double wDy = Gt(dy,qy);
for (int dx = 0; dx < D1D; ++dx)
{
y(dx,dy,c,e) += ((gradX[dx][0] * wy) + (gradX[dx][1] * wDy));
}
}
}
}
});
}
// PA Diffusion Apply 3D kernel
template<const int T_D1D = 0,
const int T_Q1D = 0> static
void PAVectorDiffusionApply3D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Array<double> &bt,
const Array<double> &gt,
const Vector &_op,
const Vector &_x,
Vector &_y,
int d1d = 0, int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 3;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Bt = Reshape(bt.Read(), D1D, Q1D);
auto Gt = Reshape(gt.Read(), D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D*Q1D*Q1D, 6, NE);
auto x = Reshape(_x.Read(), D1D, D1D, D1D, VDIM, NE);
auto y = Reshape(_y.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
for (int c = 0; c < VDIM; ++ c)
{
double grad[max_Q1D][max_Q1D][max_Q1D][3];
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qz][qy][qx][0] = 0.0;
grad[qz][qy][qx][1] = 0.0;
grad[qz][qy][qx][2] = 0.0;
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
double gradXY[max_Q1D][max_Q1D][3];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
gradXY[qy][qx][0] = 0.0;
gradXY[qy][qx][1] = 0.0;
gradXY[qy][qx][2] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
double gradX[max_Q1D][2];
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] = 0.0;
gradX[qx][1] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x(dx,dy,dz,c,e);
for (int qx = 0; qx < Q1D; ++qx)
{
gradX[qx][0] += s * B(qx,dx);
gradX[qx][1] += s * G(qx,dx);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B(qy,dy);
const double wDy = G(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
const double wx = gradX[qx][0];
const double wDx = gradX[qx][1];
gradXY[qy][qx][0] += wDx * wy;
gradXY[qy][qx][1] += wx * wDy;
gradXY[qy][qx][2] += wx * wy;
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const double wz = B(qz,dz);
const double wDz = G(qz,dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
grad[qz][qy][qx][0] += gradXY[qy][qx][0] * wz;
grad[qz][qy][qx][1] += gradXY[qy][qx][1] * wz;
grad[qz][qy][qx][2] += gradXY[qy][qx][2] * wDz;
}
}
}
}
// Calculate Dxyz, xDyz, xyDz in plane
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const double O11 = op(q,0,e);
const double O12 = op(q,1,e);
const double O13 = op(q,2,e);
const double O22 = op(q,3,e);
const double O23 = op(q,4,e);
const double O33 = op(q,5,e);
const double gradX = grad[qz][qy][qx][0];
const double gradY = grad[qz][qy][qx][1];
const double gradZ = grad[qz][qy][qx][2];
grad[qz][qy][qx][0] = (O11*gradX)+(O12*gradY)+(O13*gradZ);
grad[qz][qy][qx][1] = (O12*gradX)+(O22*gradY)+(O23*gradZ);
grad[qz][qy][qx][2] = (O13*gradX)+(O23*gradY)+(O33*gradZ);
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
double gradXY[max_D1D][max_D1D][3];
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
gradXY[dy][dx][0] = 0;
gradXY[dy][dx][1] = 0;
gradXY[dy][dx][2] = 0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double gradX[max_D1D][3];
for (int dx = 0; dx < D1D; ++dx)
{
gradX[dx][0] = 0;
gradX[dx][1] = 0;
gradX[dx][2] = 0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double gX = grad[qz][qy][qx][0];
const double gY = grad[qz][qy][qx][1];
const double gZ = grad[qz][qy][qx][2];
for (int dx = 0; dx < D1D; ++dx)
{
const double wx = Bt(dx,qx);
const double wDx = Gt(dx,qx);
gradX[dx][0] += gX * wDx;
gradX[dx][1] += gY * wx;
gradX[dx][2] += gZ * wx;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double wy = Bt(dy,qy);
const double wDy = Gt(dy,qy);
for (int dx = 0; dx < D1D; ++dx)
{
gradXY[dy][dx][0] += gradX[dx][0] * wy;
gradXY[dy][dx][1] += gradX[dx][1] * wDy;
gradXY[dy][dx][2] += gradX[dx][2] * wy;
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
const double wz = Bt(dz,qz);
const double wDz = Gt(dz,qz);
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
y(dx,dy,dz,c,e) +=
((gradXY[dy][dx][0] * wz) +
(gradXY[dy][dx][1] * wz) +
(gradXY[dy][dx][2] * wDz));
}
}
}
}
}
});
}
static void PAVectorDiffusionApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &op,
const Vector &x,
Vector &y)
{
if (dim == 2)
{
return PAVectorDiffusionApply2D(NE,B,G,Bt,Gt,op,x,y,D1D,Q1D);
}
if (dim == 3)
{
return PAVectorDiffusionApply3D(NE,B,G,Bt,Gt,op,x,y,D1D,Q1D);
}
MFEM_ABORT("Unknown kernel.");
}
// PA Diffusion Apply kernel
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PAVectorDiffusionApply(dim, dofs1D, quad1D, ne,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
}
} // namespace mfem
-367
View File
@@ -1,367 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
using namespace std;
namespace mfem
{
// PA Mass Integrator
// PA Mass Assemble kernel
void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assuming the same element type
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation *T = mesh->GetElementTransformation(0);
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(el, el, *T);
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::COORDINATES |
GeometricFactors::JACOBIANS);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, Device::GetMemoryType());
double coeff = 1.0;
if (Q)
{
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient*>(Q);
MFEM_VERIFY(cQ != NULL, "Only ConstantCoefficient is supported.");
coeff = cQ->constant;
}
if (!(dim == 2 || dim == 3))
{
MFEM_ABORT("Dimension not supported.");
}
if (dim == 2)
{
const double constant = coeff;
const int NE = ne;
const int NQ = nq;
auto w = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double J11 = J(q,0,0,e);
const double J12 = J(q,1,0,e);
const double J21 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double detJ = (J11*J22)-(J21*J12);
v(q,e) = w[q] * constant * detJ;
}
});
}
if (dim == 3)
{
const double constant = coeff;
const int NE = ne;
const int NQ = nq;
auto W = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
auto v = Reshape(pa_data.Write(), NQ,NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double J11 = J(q,0,0,e), J12 = J(q,0,1,e), J13 = J(q,0,2,e);
const double J21 = J(q,1,0,e), J22 = J(q,1,1,e), J23 = J(q,1,2,e);
const double J31 = J(q,2,0,e), J32 = J(q,2,1,e), J33 = J(q,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
v(q,e) = W[q] * constant * detJ;
}
});
}
}
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void PAVectorMassApply2D(const int NE,
const Array<double> &_B,
const Array<double> &_Bt,
const Vector &_op,
const Vector &_x,
Vector &_y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(_B.Read(), Q1D, D1D);
auto Bt = Reshape(_Bt.Read(), D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
auto x = Reshape(_x.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(_y.ReadWrite(), D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double sol_xy[max_Q1D][max_Q1D];
for (int c = 0; c < VDIM; ++c)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
double sol_x[max_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
sol_x[qy] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x(dx,dy,c,e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B(qx,dx)* s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double d2q = B(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] += d2q * sol_x[qx];
}
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] *= op(qx,qy,e);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double sol_x[max_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0.0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double s = sol_xy[qy][qx];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt(dx,qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double q2d = Bt(dy,qy);
for (int dx = 0; dx < D1D; ++dx)
{
y(dx,dy,c,e) += q2d * sol_x[dx];
}
}
}
}
});
}
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void PAVectorMassApply3D(const int NE,
const Array<double> &_B,
const Array<double> &_Bt,
const Vector &_op,
const Vector &_x,
Vector &_y,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 3;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(_B.Read(), Q1D, D1D);
auto Bt = Reshape(_Bt.Read(), D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
auto x = Reshape(_x.Read(), D1D, D1D, D1D, VDIM, NE);
auto y = Reshape(_y.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
double sol_xyz[max_Q1D][max_Q1D][max_Q1D];
for (int c = 0; c < VDIM; ++ c)
{
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xyz[qz][qy][qx] = 0.0;
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
double sol_xy[max_Q1D][max_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
double sol_x[max_Q1D];
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] = 0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x(dx,dy,dz,c,e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B(qx,dx) * s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] += wy * sol_x[qx];
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const double wz = B(qz,dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xyz[qz][qy][qx] += wz * sol_xy[qy][qx];
}
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xyz[qz][qy][qx] *= op(qx,qy,qz,e);
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
double sol_xy[max_D1D][max_D1D];
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] = 0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double sol_x[max_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double s = sol_xyz[qz][qy][qx];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt(dx,qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double wy = Bt(dy,qy);
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] += wy * sol_x[dx];
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
const double wz = Bt(dz,qz);
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
y(dx,dy,dz,c,e) += wz * sol_xy[dy][dx];
}
}
}
}
}
});
}
static void PAVectorMassApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &Bt,
const Vector &op,
const Vector &x,
Vector &y)
{
if (dim == 2)
{
return PAVectorMassApply2D(NE, B, Bt, op, x, y, D1D, Q1D);
}
if (dim == 3)
{
return PAVectorMassApply3D(NE, B, Bt, op, x, y, D1D, Q1D);
}
MFEM_ABORT("Unknown kernel.");
}
void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PAVectorMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
}
} // namespace mfem
+12 -20
View File
@@ -28,6 +28,11 @@ double PWConstCoefficient::Eval(ElementTransformation & T,
return (constants(att-1));
}
DeviceFunctionCoefficientPtr FunctionCoefficient::GetDeviceFunction()
{
return DeviceFunction;
}
double FunctionCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
@@ -40,6 +45,10 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
{
return ((*Function)(transip));
}
else if (DeviceFunction)
{
return ((*DeviceFunction)(Vector3(x)));
}
else
{
return (*TDFunction)(transip, GetTime());
@@ -125,27 +134,19 @@ void VectorFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
}
VectorArrayCoefficient::VectorArrayCoefficient (int dim)
: VectorCoefficient(dim), Coeff(dim), ownCoeff(dim)
: VectorCoefficient(dim), Coeff(dim)
{
for (int i = 0; i < dim; i++)
{
Coeff[i] = NULL;
ownCoeff[i] = true;
}
}
void VectorArrayCoefficient::Set(int i, Coefficient *c, bool own)
{
if (ownCoeff[i]) { delete Coeff[i]; }
Coeff[i] = c;
ownCoeff[i] = own;
}
VectorArrayCoefficient::~VectorArrayCoefficient()
{
for (int i = 0; i < vdim; i++)
{
if (ownCoeff[i]) { delete Coeff[i]; }
delete Coeff[i];
}
}
@@ -317,26 +318,17 @@ MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
: MatrixCoefficient (dim)
{
Coeff.SetSize(height*width);
ownCoeff.SetSize(height*width);
for (int i = 0; i < (height*width); i++)
{
Coeff[i] = NULL;
ownCoeff[i] = true;
}
}
void MatrixArrayCoefficient::Set(int i, int j, Coefficient * c, bool own)
{
if (ownCoeff[i*width+j]) { delete Coeff[i*width+j]; }
Coeff[i*width+j] = c;
ownCoeff[i*width+j] = own;
}
MatrixArrayCoefficient::~MatrixArrayCoefficient ()
{
for (int i=0; i < height*width; i++)
{
if (ownCoeff[i]) { delete Coeff[i]; }
delete Coeff[i];
}
}
+22 -8
View File
@@ -112,6 +112,7 @@ public:
const IntegrationPoint &ip);
};
typedef double (*DeviceFunctionCoefficientPtr)(const Vector3&);
/// class for C-function coefficient
class FunctionCoefficient : public Coefficient
@@ -119,6 +120,7 @@ class FunctionCoefficient : public Coefficient
protected:
double (*Function)(const Vector &);
double (*TDFunction)(const Vector &, double);
double (*DeviceFunction)(const Vector3&);
public:
/// Define a time-independent coefficient from a C-function
@@ -126,6 +128,7 @@ public:
{
Function = f;
TDFunction = NULL;
DeviceFunction = NULL;
}
/// Define a time-dependent coefficient from a C-function
@@ -133,6 +136,16 @@ public:
{
Function = NULL;
TDFunction = tdf;
DeviceFunction = NULL;
}
/// Define a time-independent coefficient from a C-function using
/// Vector3 instead of a Vector.
FunctionCoefficient(double (*df)(const Vector3 &))
{
Function = NULL;
TDFunction = NULL;
DeviceFunction = df;
}
/// (DEPRECATED) Define a time-independent coefficient from a C-function
@@ -142,6 +155,7 @@ public:
{
Function = reinterpret_cast<double(*)(const Vector&)>(f);
TDFunction = NULL;
DeviceFunction = NULL;
}
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
@@ -151,11 +165,17 @@ public:
{
Function = NULL;
TDFunction = reinterpret_cast<double(*)(const Vector&,double)>(tdf);
DeviceFunction = NULL;
}
/// Evaluate coefficient
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
/// Return the coefficient's C-function that uses Vector3.
/// Warning: for now, the returned function can only be used on the
/// host inside a MFEM_FORALL.
DeviceFunctionCoefficientPtr GetDeviceFunction();
};
class GridFunction;
@@ -369,7 +389,6 @@ class VectorArrayCoefficient : public VectorCoefficient
{
private:
Array<Coefficient*> Coeff;
Array<bool> ownCoeff;
public:
/// Construct vector of dim coefficients.
@@ -381,7 +400,7 @@ public:
Coefficient **GetCoeffs() { return Coeff; }
/// Sets coefficient in the vector.
void Set(int i, Coefficient *c, bool own=true);
void Set(int i, Coefficient *c) { delete Coeff[i]; Coeff[i] = c; }
/// Evaluates i'th component of the vector.
double Eval(int i, ElementTransformation &T, const IntegrationPoint &ip)
@@ -501,13 +520,9 @@ public:
void SetDeltaCoefficient(const DeltaCoefficient& _d) { d = _d; }
/// Return the associated scalar DeltaCoefficient.
DeltaCoefficient& GetDeltaCoefficient() { return d; }
void SetScale(double s) { d.SetScale(s); }
void SetDirection(const Vector& _d);
void SetDeltaCenter(const Vector& center) { d.SetDeltaCenter(center); }
void GetDeltaCenter(Vector& center) { d.GetDeltaCenter(center); }
/** @brief Return the specified direction vector multiplied by the value
returned by DeltaCoefficient::EvalDelta() of the associated scalar
DeltaCoefficient. */
@@ -633,7 +648,6 @@ class MatrixArrayCoefficient : public MatrixCoefficient
{
private:
Array<Coefficient *> Coeff;
Array<bool> ownCoeff;
public:
@@ -641,7 +655,7 @@ public:
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
void Set(int i, int j, Coefficient * c, bool own=true);
void Set(int i, int j, Coefficient * c) { delete Coeff[i*width+j]; Coeff[i*width+j] = c; }
double Eval(int i, int j, ElementTransformation &T, const IntegrationPoint &ip)
{ return Coeff[i*width+j] ? Coeff[i*width+j] -> Eval(T, ip, GetTime()) : 0.0; }
+208 -315
View File
@@ -19,27 +19,27 @@ namespace mfem
ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *fes)
: Vector(2*(fes->GetVSize()))
{
gfr = new GridFunction(fes, &data[0]);
gfi = new GridFunction(fes, &data[fes->GetVSize()]);
gfr_ = new GridFunction(fes, &data[0]);
gfi_ = new GridFunction(fes, &data[fes->GetVSize()]);
}
void
ComplexGridFunction::Update()
{
FiniteElementSpace * fes = gfr->FESpace();
FiniteElementSpace * fes = gfr_->FESpace();
int vsize = fes->GetVSize();
const Operator *T = fes->GetUpdateOperator();
if (T)
{
// Update the individual GridFunction objects. This will allocate new data
// arrays for each GridFunction.
gfr->Update();
gfi->Update();
// Update the individual GridFunction objects. This will allocate
// new data arrays for each GridFunction.
gfr_->Update();
gfi_->Update();
// Our data array now contains old data as well as being the wrong size so
// reallocate it.
// Our data array now contains old data as well as being the wrong size
// so reallocate it.
this->SetSize(2 * vsize);
// Create temporary vectors which point to the new data array
@@ -47,29 +47,28 @@ ComplexGridFunction::Update()
Vector gf_i(&data[vsize], vsize);
// Copy the updated GridFunctions into the new data array
gf_r = *gfr;
gf_i = *gfi;
gf_r = *gfr_;
gf_i = *gfi_;
// Replace the individual data arrays with pointers into the new data
// array
gfr->NewDataAndSize(&data[0], vsize);
gfi->NewDataAndSize(&data[vsize], vsize);
// Replace the individual data arrays with pointers into the new data array
gfr_->NewDataAndSize(&data[0], vsize);
gfi_->NewDataAndSize(&data[vsize], vsize);
}
else
{
// The existing data will not be transferred to the new GridFunctions so
// delete it a allocate a new array
// The existing data will not be transferred to the new GridFunctions
// so delete it a allocate a new array
this->SetSize(2 * vsize);
// Point the individual GridFunctions to the new data array
gfr->NewDataAndSize(&data[0], vsize);
gfi->NewDataAndSize(&data[vsize], vsize);
gfr_->NewDataAndSize(&data[0], vsize);
gfi_->NewDataAndSize(&data[vsize], vsize);
// These updates will only set the proper 'sequence' value within
// the individual GridFunction objects because their sizes are
// already correct
gfr->Update();
gfi->Update();
gfr_->Update();
gfi_->Update();
}
}
@@ -77,16 +76,16 @@ void
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff)
{
gfr->ProjectCoefficient(real_coeff);
gfi->ProjectCoefficient(imag_coeff);
gfr_->ProjectCoefficient(real_coeff);
gfi_->ProjectCoefficient(imag_coeff);
}
void
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff)
{
gfr->ProjectCoefficient(real_vcoeff);
gfi->ProjectCoefficient(imag_vcoeff);
gfr_->ProjectCoefficient(real_vcoeff);
gfi_->ProjectCoefficient(imag_vcoeff);
}
void
@@ -94,8 +93,8 @@ ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
Array<int> &attr)
{
gfr->ProjectBdrCoefficient(real_coeff, attr);
gfi->ProjectBdrCoefficient(imag_coeff, attr);
gfr_->ProjectBdrCoefficient(real_coeff, attr);
gfi_->ProjectBdrCoefficient(imag_coeff, attr);
}
void
@@ -103,8 +102,8 @@ ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff,
Array<int> &attr)
{
gfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
gfi->ProjectBdrCoefficientNormal(imag_vcoeff, attr);
gfr_->ProjectBdrCoefficientNormal(real_vcoeff, attr);
gfi_->ProjectBdrCoefficientNormal(imag_vcoeff, attr);
}
void
@@ -114,72 +113,38 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&imag_vcoeff,
Array<int> &attr)
{
gfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
gfi->ProjectBdrCoefficientTangent(imag_vcoeff, attr);
gfr_->ProjectBdrCoefficientTangent(real_vcoeff, attr);
gfi_->ProjectBdrCoefficientTangent(imag_vcoeff, attr);
}
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *f,
ComplexOperator::Convention convention)
: Vector(2*(f->GetVSize())),
conv(convention)
conv_(convention)
{
lfr = new LinearForm(f, &data[0]);
lfi = new LinearForm(f, &data[f->GetVSize()]);
lfr_ = new LinearForm(f, &data[0]);
lfi_ = new LinearForm(f, &data[f->GetVSize()]);
}
ComplexLinearForm::~ComplexLinearForm()
{
delete lfr;
delete lfi;
delete lfr_;
delete lfi_;
}
void
ComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { lfr->AddDomainIntegrator(lfi_real); }
if ( lfi_imag ) { lfi->AddDomainIntegrator(lfi_imag); }
}
void
ComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { lfr->AddBoundaryIntegrator(lfi_real); }
if ( lfi_imag ) { lfi->AddBoundaryIntegrator(lfi_imag); }
}
void
ComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker)
{
if ( lfi_real ) { lfr->AddBoundaryIntegrator(lfi_real, bdr_attr_marker); }
if ( lfi_imag ) { lfi->AddBoundaryIntegrator(lfi_imag, bdr_attr_marker); }
}
void
ComplexLinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { lfr->AddBdrFaceIntegrator(lfi_real); }
if ( lfi_imag ) { lfi->AddBdrFaceIntegrator(lfi_imag); }
}
void
ComplexLinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker)
{
if ( lfi_real ) { lfr->AddBdrFaceIntegrator(lfi_real, bdr_attr_marker); }
if ( lfi_imag ) { lfi->AddBdrFaceIntegrator(lfi_imag, bdr_attr_marker); }
if ( lfi_real ) { lfr_->AddDomainIntegrator(lfi_real); }
if ( lfi_imag ) { lfi_->AddDomainIntegrator(lfi_imag); }
}
void
ComplexLinearForm::Update()
{
FiniteElementSpace *fes = lfr->FESpace();
FiniteElementSpace *fes = lfr_->FESpace();
this->Update(fes);
}
@@ -190,59 +155,59 @@ ComplexLinearForm::Update(FiniteElementSpace *fes)
int vsize = fes->GetVSize();
SetSize(2 * vsize);
Vector vlfr(&data[0], vsize);
Vector vlfi(&data[vsize], vsize);
Vector lfr(&data[0], vsize);
Vector lfi(&data[vsize], vsize);
lfr->Update(fes, vlfr, 0);
lfi->Update(fes, vlfi, 0);
lfr_->Update(fes, lfr, 0);
lfi_->Update(fes, lfi, 0);
}
void
ComplexLinearForm::Assemble()
{
lfr->Assemble();
lfi->Assemble();
if (conv == ComplexOperator::BLOCK_SYMMETRIC)
lfr_->Assemble();
lfi_->Assemble();
if (conv_ == ComplexOperator::BLOCK_SYMMETRIC)
{
*lfi *= -1.0;
*lfi_ *= -1.0;
}
}
complex<double>
ComplexLinearForm::operator()(const ComplexGridFunction &gf) const
{
double s = (conv == ComplexOperator::HERMITIAN)?1.0:-1.0;
return complex<double>((*lfr)(gf.real()) - s * (*lfi)(gf.imag()),
(*lfr)(gf.imag()) + s * (*lfi)(gf.real()));
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
return complex<double>((*lfr_)(gf.real()) - s * (*lfi_)(gf.imag()),
(*lfr_)(gf.imag()) + s * (*lfi_)(gf.real()));
}
SesquilinearForm::SesquilinearForm(FiniteElementSpace *f,
ComplexOperator::Convention convention)
: conv(convention),
blfr(new BilinearForm(f)),
blfi(new BilinearForm(f))
: conv_(convention),
blfr_(new BilinearForm(f)),
blfi_(new BilinearForm(f))
{}
SesquilinearForm::~SesquilinearForm()
{
delete blfr;
delete blfi;
delete blfr_;
delete blfi_;
}
void SesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { blfr->AddDomainIntegrator(bfi_real); }
if (bfi_imag) { blfi->AddDomainIntegrator(bfi_imag); }
if (bfi_real) { blfr_->AddDomainIntegrator(bfi_real); }
if (bfi_imag) { blfi_->AddDomainIntegrator(bfi_imag); }
}
void
SesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { blfr->AddBoundaryIntegrator(bfi_real); }
if (bfi_imag) { blfi->AddBoundaryIntegrator(bfi_imag); }
if (bfi_real) { blfr_->AddBoundaryIntegrator(bfi_real); }
if (bfi_imag) { blfi_->AddBoundaryIntegrator(bfi_imag); }
}
void
@@ -250,53 +215,30 @@ SesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> & bdr_marker)
{
if (bfi_real) { blfr->AddBoundaryIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { blfi->AddBoundaryIntegrator(bfi_imag, bdr_marker); }
}
void
SesquilinearForm::AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { blfr->AddInteriorFaceIntegrator(bfi_real); }
if (bfi_imag) { blfi->AddInteriorFaceIntegrator(bfi_imag); }
}
void SesquilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { blfr->AddBdrFaceIntegrator(bfi_real); }
if (bfi_imag) { blfi->AddBdrFaceIntegrator(bfi_imag); }
}
void SesquilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker)
{
if (bfi_real) { blfr->AddBdrFaceIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { blfi->AddBdrFaceIntegrator(bfi_imag, bdr_marker); }
if (bfi_real) { blfr_->AddBoundaryIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { blfi_->AddBoundaryIntegrator(bfi_imag, bdr_marker); }
}
void
SesquilinearForm::Assemble(int skip_zeros)
{
blfr->Assemble(skip_zeros);
blfi->Assemble(skip_zeros);
blfr_->Assemble(skip_zeros);
blfi_->Assemble(skip_zeros);
}
void
SesquilinearForm::Finalize(int skip_zeros)
{
blfr->Finalize(skip_zeros);
blfi->Finalize(skip_zeros);
blfr_->Finalize(skip_zeros);
blfi_->Finalize(skip_zeros);
}
ComplexSparseMatrix *
SesquilinearForm::AssembleComplexSparseMatrix()
SesquilinearForm::AssembleCompSpMat()
{
return new ComplexSparseMatrix(&blfr->SpMat(),
&blfi->SpMat(),
false, false, conv);
return new ComplexSparseMatrix(&blfr_->SpMat(),
&blfi_->SpMat(),
false, false, conv_);
}
@@ -307,14 +249,16 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &X, Vector &B,
int ci)
{
FiniteElementSpace * fes = blfr->FESpace();
FiniteElementSpace * fes = blfr_->FESpace();
int vsize = fes->GetVSize();
// int tvsize = pfes->GetTrueVSize();
double s = (conv == ComplexOperator::HERMITIAN)?1.0:-1.0;
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
// Allocate temporary vectors
Vector b_0(vsize); b_0 = 0.0;
// Vector B_0(tvsize); B_0 = 0.0;
// Extract the real and imaginary parts of the input vectors
MFEM_ASSERT(x.Size() == 2 * vsize, "Input GridFunction of incorrect size!");
@@ -325,13 +269,21 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector b_r(b.GetData(), vsize);
Vector b_i(&(b.GetData())[vsize], vsize);
b_i *= s;
/*
X.SetSize(2 * tvsize);
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
B.SetSize(2 * tvsize);
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
*/
SparseMatrix * A_r = new SparseMatrix;
SparseMatrix * A_i = new SparseMatrix;
Vector X_0, B_0;
b_0 = b_r;
blfr->FormLinearSystem(ess_tdof_list, x_r, b_r, *A_r, X_0, B_0, ci);
blfr_->FormLinearSystem(ess_tdof_list, x_r, b_r, *A_r, X_0, B_0, ci);
int tvsize = B_0.Size();
X.SetSize(2 * tvsize);
@@ -343,15 +295,15 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
X_r = X_0; B_r = B_0;
b_0 = 0.0;
blfi->FormLinearSystem(ess_tdof_list, x_i, b_0, *A_i, X_0, B_0, false);
blfi_->FormLinearSystem(ess_tdof_list, x_i, b_0, *A_i, X_0, B_0, false);
B_r -= B_0;
b_0 = b_i;
blfr->FormLinearSystem(ess_tdof_list, x_i, b_0, *A_r, X_0, B_0, ci);
blfr_->FormLinearSystem(ess_tdof_list, x_i, b_0, *A_r, X_0, B_0, ci);
X_i = X_0; B_i = B_0;
b_0 = 0.0;
blfi->FormLinearSystem(ess_tdof_list, x_r, b_0, *A_i, X_0, B_0, false);
blfi_->FormLinearSystem(ess_tdof_list, x_r, b_0, *A_i, X_0, B_0, false);
B_i += B_0;
B_i *= s;
@@ -360,7 +312,7 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
// A = A_r + i A_i
A.Clear();
ComplexSparseMatrix * A_sp =
new ComplexSparseMatrix(A_r, A_i, true, true, conv);
new ComplexSparseMatrix(A_r, A_i, true, true, conv_);
A.Reset<ComplexSparseMatrix>(A_sp, true);
}
@@ -368,7 +320,7 @@ void
SesquilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x)
{
FiniteElementSpace * fes = blfr->FESpace();
FiniteElementSpace * fes = blfr_->FESpace();
const SparseMatrix *P = fes->GetConformingProlongation();
@@ -396,8 +348,8 @@ SesquilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
void
SesquilinearForm::Update(FiniteElementSpace *nfes)
{
if ( blfr ) { blfr->Update(nfes); }
if ( blfi ) { blfi->Update(nfes); }
if ( blfr_ ) { blfr_->Update(nfes); }
if ( blfi_ ) { blfi_->Update(nfes); }
}
@@ -406,24 +358,24 @@ SesquilinearForm::Update(FiniteElementSpace *nfes)
ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pfes)
: Vector(2*(pfes->GetVSize()))
{
pgfr = new ParGridFunction(pfes, &data[0]);
pgfi = new ParGridFunction(pfes, &data[pfes->GetVSize()]);
pgfr_ = new ParGridFunction(pfes, &data[0]);
pgfi_ = new ParGridFunction(pfes, &data[pfes->GetVSize()]);
}
void
ParComplexGridFunction::Update()
{
ParFiniteElementSpace * pfes = pgfr->ParFESpace();
ParFiniteElementSpace * pfes = pgfr_->ParFESpace();
int vsize = pfes->GetVSize();
const Operator *T = pfes->GetUpdateOperator();
if (T)
{
// Update the individual GridFunction objects. This will allocate new data
// arrays for each GridFunction.
pgfr->Update();
pgfi->Update();
// Update the individual GridFunction objects. This will allocate
// new data arrays for each GridFunction.
pgfr_->Update();
pgfi_->Update();
// Our data array now contains old data as well as being the wrong size
// so reallocate it.
@@ -434,28 +386,28 @@ ParComplexGridFunction::Update()
Vector gf_i(&data[vsize], vsize);
// Copy the updated GridFunctions into the new data array
gf_r = *pgfr;
gf_i = *pgfi;
gf_r = *pgfr_;
gf_i = *pgfi_;
// Replace the individual data arrays with pointers into the new data
// array
pgfr->NewDataAndSize(&data[0], vsize);
pgfi->NewDataAndSize(&data[vsize], vsize);
// Replace the individual data arrays with pointers into the new data array
pgfr_->NewDataAndSize(&data[0], vsize);
pgfi_->NewDataAndSize(&data[vsize], vsize);
}
else
{
// The existing data will not be transferred to the new GridFunctions so
// delete it a allocate a new array
// The existing data will not be transferred to the new GridFunctions
// so delete it a allocate a new array
this->SetSize(2 * vsize);
// Point the individual GridFunctions to the new data array
pgfr->NewDataAndSize(&data[0], vsize);
pgfi->NewDataAndSize(&data[vsize], vsize);
pgfr_->NewDataAndSize(&data[0], vsize);
pgfi_->NewDataAndSize(&data[vsize], vsize);
// These updates will only set the proper 'sequence' value within the
// individual GridFunction objects because their sizes are already correct
pgfr->Update();
pgfi->Update();
// These updates will only set the proper 'sequence' value within
// the individual GridFunction objects because their sizes are
// already correct
pgfr_->Update();
pgfi_->Update();
}
}
@@ -463,16 +415,16 @@ void
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff)
{
pgfr->ProjectCoefficient(real_coeff);
pgfi->ProjectCoefficient(imag_coeff);
pgfr_->ProjectCoefficient(real_coeff);
pgfi_->ProjectCoefficient(imag_coeff);
}
void
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff)
{
pgfr->ProjectCoefficient(real_vcoeff);
pgfi->ProjectCoefficient(imag_vcoeff);
pgfr_->ProjectCoefficient(real_vcoeff);
pgfi_->ProjectCoefficient(imag_vcoeff);
}
void
@@ -480,8 +432,8 @@ ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
Array<int> &attr)
{
pgfr->ProjectBdrCoefficient(real_coeff, attr);
pgfi->ProjectBdrCoefficient(imag_coeff, attr);
pgfr_->ProjectBdrCoefficient(real_coeff, attr);
pgfi_->ProjectBdrCoefficient(imag_coeff, attr);
}
void
@@ -491,8 +443,8 @@ ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
&imag_vcoeff,
Array<int> &attr)
{
pgfr->ProjectBdrCoefficientNormal(real_vcoeff, attr);
pgfi->ProjectBdrCoefficientNormal(imag_vcoeff, attr);
pgfr_->ProjectBdrCoefficientNormal(real_vcoeff, attr);
pgfi_->ProjectBdrCoefficientNormal(imag_vcoeff, attr);
}
void
@@ -502,36 +454,36 @@ ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&imag_vcoeff,
Array<int> &attr)
{
pgfr->ProjectBdrCoefficientTangent(real_vcoeff, attr);
pgfi->ProjectBdrCoefficientTangent(imag_vcoeff, attr);
pgfr_->ProjectBdrCoefficientTangent(real_vcoeff, attr);
pgfi_->ProjectBdrCoefficientTangent(imag_vcoeff, attr);
}
void
ParComplexGridFunction::Distribute(const Vector *tv)
{
ParFiniteElementSpace * pfes = pgfr->ParFESpace();
ParFiniteElementSpace * pfes = pgfr_->ParFESpace();
HYPRE_Int size = pfes->GetTrueVSize();
double * tvd = tv->GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
pgfr->Distribute(tvr);
pgfi->Distribute(tvi);
pgfr_->Distribute(tvr);
pgfi_->Distribute(tvi);
}
void
ParComplexGridFunction::ParallelProject(Vector &tv) const
{
ParFiniteElementSpace * pfes = pgfr->ParFESpace();
ParFiniteElementSpace * pfes = pgfr_->ParFESpace();
HYPRE_Int size = pfes->GetTrueVSize();
double * tvd = tv.GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
pgfr->ParallelProject(tvr);
pgfi->ParallelProject(tvi);
pgfr_->ParallelProject(tvr);
pgfi_->ParallelProject(tvi);
}
@@ -539,117 +491,83 @@ ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
ComplexOperator::Convention
convention)
: Vector(2*(pfes->GetVSize())),
conv(convention)
conv_(convention)
{
plfr = new ParLinearForm(pfes, &data[0]);
plfi = new ParLinearForm(pfes, &data[pfes->GetVSize()]);
plfr_ = new ParLinearForm(pfes, &data[0]);
plfi_ = new ParLinearForm(pfes, &data[pfes->GetVSize()]);
HYPRE_Int * tdof_offsets_fes = pfes->GetTrueDofOffsets();
HYPRE_Int * tdof_offsets = pfes->GetTrueDofOffsets();
int n = (HYPRE_AssumedPartitionCheck()) ? 2 : pfes->GetNRanks();
tdof_offsets = new HYPRE_Int[n+1];
tdof_offsets_ = new HYPRE_Int[n+1];
for (int i=0; i<=n; i++)
{
tdof_offsets[i] = 2 * tdof_offsets_fes[i];
tdof_offsets_[i] = 2 * tdof_offsets[i];
}
}
ParComplexLinearForm::~ParComplexLinearForm()
{
delete plfr;
delete plfi;
delete [] tdof_offsets;
delete plfr_;
delete plfi_;
delete [] tdof_offsets_;
}
void
ParComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { plfr->AddDomainIntegrator(lfi_real); }
if ( lfi_imag ) { plfi->AddDomainIntegrator(lfi_imag); }
}
void
ParComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { plfr->AddBoundaryIntegrator(lfi_real); }
if ( lfi_imag ) { plfi->AddBoundaryIntegrator(lfi_imag); }
}
void
ParComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker)
{
if ( lfi_real ) { plfr->AddBoundaryIntegrator(lfi_real, bdr_attr_marker); }
if ( lfi_imag ) { plfi->AddBoundaryIntegrator(lfi_imag, bdr_attr_marker); }
}
void
ParComplexLinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { plfr->AddBdrFaceIntegrator(lfi_real); }
if ( lfi_imag ) { plfi->AddBdrFaceIntegrator(lfi_imag); }
}
void
ParComplexLinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker)
{
if ( lfi_real ) { plfr->AddBdrFaceIntegrator(lfi_real, bdr_attr_marker); }
if ( lfi_imag ) { plfi->AddBdrFaceIntegrator(lfi_imag, bdr_attr_marker); }
if ( lfi_real ) { plfr_->AddDomainIntegrator(lfi_real); }
if ( lfi_imag ) { plfi_->AddDomainIntegrator(lfi_imag); }
}
void
ParComplexLinearForm::Update(ParFiniteElementSpace *pf)
{
ParFiniteElementSpace *pfes = (pf!=NULL)?pf:plfr->ParFESpace();
ParFiniteElementSpace *pfes = (pf!=NULL)?pf:plfr_->ParFESpace();
int vsize = pfes->GetVSize();
SetSize(2 * vsize);
Vector vplfr(&data[0], vsize);
Vector vplfi(&data[vsize], vsize);
Vector plfr(&data[0], vsize);
Vector plfi(&data[vsize], vsize);
plfr->Update(pfes, vplfr, 0);
plfi->Update(pfes, vplfi, 0);
plfr_->Update(pfes, plfr, 0);
plfi_->Update(pfes, plfi, 0);
}
void
ParComplexLinearForm::Assemble()
{
plfr->Assemble();
plfi->Assemble();
if (conv == ComplexOperator::BLOCK_SYMMETRIC)
plfr_->Assemble();
plfi_->Assemble();
if (conv_ == ComplexOperator::BLOCK_SYMMETRIC)
{
*plfi *= -1.0;
*plfi_ *= -1.0;
}
}
void
ParComplexLinearForm::ParallelAssemble(Vector &tv)
{
HYPRE_Int size = plfr->ParFESpace()->GetTrueVSize();
HYPRE_Int size = plfr_->ParFESpace()->GetTrueVSize();
double * tvd = tv.GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
plfr->ParallelAssemble(tvr);
plfi->ParallelAssemble(tvi);
plfr_->ParallelAssemble(tvr);
plfi_->ParallelAssemble(tvi);
}
HypreParVector *
ParComplexLinearForm::ParallelAssemble()
{
const ParFiniteElementSpace * pfes = plfr->ParFESpace();
const ParFiniteElementSpace * pfes = plfr_->ParFESpace();
HypreParVector * tv = new HypreParVector(pfes->GetComm(),
2*(pfes->GlobalTrueVSize()),
tdof_offsets);
tdof_offsets_);
HYPRE_Int size = pfes->GetTrueVSize();
@@ -657,8 +575,8 @@ ParComplexLinearForm::ParallelAssemble()
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
plfr->ParallelAssemble(tvr);
plfi->ParallelAssemble(tvi);
plfr_->ParallelAssemble(tvr);
plfi_->ParallelAssemble(tvi);
return tv;
}
@@ -666,39 +584,39 @@ ParComplexLinearForm::ParallelAssemble()
complex<double>
ParComplexLinearForm::operator()(const ParComplexGridFunction &gf) const
{
double s = (conv == ComplexOperator::HERMITIAN)?1.0:-1.0;
return complex<double>((*plfr)(gf.real()) - s * (*plfi)(gf.imag()),
(*plfr)(gf.imag()) + s * (*plfi)(gf.real()));
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
return complex<double>((*plfr_)(gf.real()) - s * (*plfi_)(gf.imag()),
(*plfr_)(gf.imag()) + s * (*plfi_)(gf.real()));
}
ParSesquilinearForm::ParSesquilinearForm(ParFiniteElementSpace *pf,
ComplexOperator::Convention
convention)
: conv(convention),
pblfr(new ParBilinearForm(pf)),
pblfi(new ParBilinearForm(pf))
: conv_(convention),
pblfr_(new ParBilinearForm(pf)),
pblfi_(new ParBilinearForm(pf))
{}
ParSesquilinearForm::~ParSesquilinearForm()
{
delete pblfr;
delete pblfi;
delete pblfr_;
delete pblfi_;
}
void ParSesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { pblfr->AddDomainIntegrator(bfi_real); }
if (bfi_imag) { pblfi->AddDomainIntegrator(bfi_imag); }
if (bfi_real) { pblfr_->AddDomainIntegrator(bfi_real); }
if (bfi_imag) { pblfi_->AddDomainIntegrator(bfi_imag); }
}
void
ParSesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { pblfr->AddBoundaryIntegrator(bfi_real); }
if (bfi_imag) { pblfi->AddBoundaryIntegrator(bfi_imag); }
if (bfi_real) { pblfr_->AddBoundaryIntegrator(bfi_real); }
if (bfi_imag) { pblfi_->AddBoundaryIntegrator(bfi_imag); }
}
void
@@ -706,55 +624,30 @@ ParSesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> & bdr_marker)
{
if (bfi_real) { pblfr->AddBoundaryIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { pblfi->AddBoundaryIntegrator(bfi_imag, bdr_marker); }
}
void
ParSesquilinearForm::AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { pblfr->AddInteriorFaceIntegrator(bfi_real); }
if (bfi_imag) { pblfi->AddInteriorFaceIntegrator(bfi_imag); }
}
void
ParSesquilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { pblfr->AddBdrFaceIntegrator(bfi_real); }
if (bfi_imag) { pblfi->AddBdrFaceIntegrator(bfi_imag); }
}
void
ParSesquilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker)
{
if (bfi_real) { pblfr->AddBdrFaceIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { pblfi->AddBdrFaceIntegrator(bfi_imag, bdr_marker); }
if (bfi_real) { pblfr_->AddBoundaryIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { pblfi_->AddBoundaryIntegrator(bfi_imag, bdr_marker); }
}
void
ParSesquilinearForm::Assemble(int skip_zeros)
{
pblfr->Assemble(skip_zeros);
pblfi->Assemble(skip_zeros);
pblfr_->Assemble(skip_zeros);
pblfi_->Assemble(skip_zeros);
}
void
ParSesquilinearForm::Finalize(int skip_zeros)
{
pblfr->Finalize(skip_zeros);
pblfi->Finalize(skip_zeros);
pblfr_->Finalize(skip_zeros);
pblfi_->Finalize(skip_zeros);
}
ComplexHypreParMatrix *
ParSesquilinearForm::ParallelAssemble()
{
return new ComplexHypreParMatrix(pblfr->ParallelAssemble(),
pblfi->ParallelAssemble(),
true, true, conv);
return new ComplexHypreParMatrix(pblfr_->ParallelAssemble(),
pblfi_->ParallelAssemble(),
true, true, conv_);
}
@@ -765,14 +658,26 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &X, Vector &B,
int ci)
{
int vsize = x.Size() / 2;
ParFiniteElementSpace * pfes = pblfr_->ParFESpace();
double s = (conv == ComplexOperator::HERMITIAN)?1.0:-1.0;
int tvs = pfes->TrueVSize();
cout << "TrueVSize returns " << tvs << endl;
cout << "GetVSize returns " << pfes->GetVSize() << endl;
int vsize = x.Size() / 2;
// int vsize = pfes->GetVSize();
// int tvsize = pfes->GetTrueVSize();
cout << "x.Size/2 returns " << vsize << endl;
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
// Allocate temporary vectors
Vector b_0(vsize); b_0 = 0.0;
// Vector B_0(tvsize); B_0 = 0.0;
// Extract the real and imaginary parts of the input vectors
// MFEM_ASSERT(x.Size() == 2 * vsize, "Input GridFunction of incorrect size!");
Vector x_r(x.GetData(), vsize);
Vector x_i(&(x.GetData())[vsize], vsize);
@@ -780,12 +685,20 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector b_r(b.GetData(), vsize);
Vector b_i(&(b.GetData())[vsize], vsize);
b_i *= s;
/*
X.SetSize(2 * tvsize);
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
B.SetSize(2 * tvsize);
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
*/
OperatorHandle A_r, A_i;
Vector X_0, B_0;
cout << "pblfr fls 1" << endl << flush;
b_0 = b_r;
pblfr->FormLinearSystem(ess_tdof_list, x_r, b_0, A_r, X_0, B_0, ci);
pblfr_->FormLinearSystem(ess_tdof_list, x_r, b_0, A_r, X_0, B_0, ci);
int tvsize = B_0.Size();
X.SetSize(2 * tvsize);
@@ -795,42 +708,22 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
X_r = X_0; B_r = B_0;
cout << "pblfi fls 1" << endl << flush;
b_0 = 0.0;
pblfi->FormLinearSystem(ess_tdof_list, x_i, b_0, A_i, X_0, B_0, false);
pblfi_->FormLinearSystem(ess_tdof_list, x_i, b_0, A_i, X_0, B_0, false);
B_r -= B_0;
cout << "pblfr fls 2" << endl << flush;
b_0 = b_i;
pblfr->FormLinearSystem(ess_tdof_list, x_i, b_0, A_r, X_0, B_0, ci);
pblfr_->FormLinearSystem(ess_tdof_list, x_i, b_0, A_r, X_0, B_0, ci);
X_i = X_0; B_i = B_0;
cout << "pblfi fls 2" << endl << flush;
b_0 = 0.0;
pblfi->FormLinearSystem(ess_tdof_list, x_r, b_0, A_i, X_0, B_0, false);
pblfi_->FormLinearSystem(ess_tdof_list, x_r, b_0, A_i, X_0, B_0, false);
B_i += B_0;
B_i *= s;
b_i *= s;
// Modify RHS and offdiagonal blocks (Imaginary parts of the matrix) to
// conform with standard essential BC treatment i.e. zero out rows and
// columns and place ones on the diagonal.
if ( A_i.Type() == Operator::Hypre_ParCSR )
{
int n = ess_tdof_list.Size();
int j;
HypreParMatrix * Ah; A_i.Get(Ah);
hypre_ParCSRMatrix * Aih =
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(*Ah);
for (int k=0; k<n; k++)
{
j=ess_tdof_list[k];
Aih->diag->data[Aih->diag->i[j]] = 0.0;
B_r(j) = X_r(j);
B_i(j) = X_i(j);
}
}
// A = A_r + i A_i
A.Clear();
if ( A_r.Type() == Operator::Hypre_ParCSR &&
@@ -841,7 +734,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
A_i.As<HypreParMatrix>(),
A_r.OwnsOperator(),
A_i.OwnsOperator(),
conv);
conv_);
A.Reset<ComplexHypreParMatrix>(A_hyp, true);
}
else
@@ -851,7 +744,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
A_i.As<Operator>(),
A_r.OwnsOperator(),
A_i.OwnsOperator(),
conv);
conv_);
A.Reset<ComplexOperator>(A_op, true);
}
}
@@ -860,7 +753,7 @@ void
ParSesquilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x)
{
ParFiniteElementSpace * pfes = pblfr->ParFESpace();
ParFiniteElementSpace * pfes = pblfr_->ParFESpace();
const Operator &P = *pfes->GetProlongationMatrix();
@@ -881,8 +774,8 @@ ParSesquilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
void
ParSesquilinearForm::Update(FiniteElementSpace *nfes)
{
if ( pblfr ) { pblfr->Update(nfes); }
if ( pblfi ) { pblfi->Update(nfes); }
if ( pblfr_ ) { pblfr_->Update(nfes); }
if ( pblfi_ ) { pblfi_->Update(nfes); }
}
+72 -246
View File
@@ -26,18 +26,19 @@
namespace mfem
{
/// Class for complex-valued grid function - real + imaginary part Vector with
/// associated FE space.
/// Class for complex-valued grid function - Vector with associated FE space.
class ComplexGridFunction : public Vector
{
private:
GridFunction * gfr;
GridFunction * gfi;
GridFunction * gfr_;
GridFunction * gfi_;
protected:
void Destroy() { delete gfr; delete gfi; }
void Destroy() { delete gfr_; delete gfi_; }
public:
/* @brief Construct a ComplexGridFunction associated with the
FiniteElementSpace @a *f. */
ComplexGridFunction(FiniteElementSpace *f);
@@ -46,7 +47,7 @@ public:
/// Assign constant values to the ComplexGridFunction data.
ComplexGridFunction &operator=(const std::complex<double> & value)
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
{ *gfr_ = value.real(); *gfi_ = value.imag(); return *this; }
virtual void ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff);
@@ -63,88 +64,48 @@ public:
VectorCoefficient &imag_coeff,
Array<int> &attr);
FiniteElementSpace *FESpace() { return gfr->FESpace(); }
const FiniteElementSpace *FESpace() const { return gfr->FESpace(); }
FiniteElementSpace *FESpace() { return gfr_->FESpace(); }
const FiniteElementSpace *FESpace() const { return gfr_->FESpace(); }
GridFunction & real() { return *gfr; }
GridFunction & imag() { return *gfi; }
const GridFunction & real() const { return *gfr; }
const GridFunction & imag() const { return *gfi; }
GridFunction & real() { return *gfr_; }
GridFunction & imag() { return *gfi_; }
const GridFunction & real() const { return *gfr_; }
const GridFunction & imag() const { return *gfi_; }
/// Destroys the grid function.
/// Destroys grid function.
virtual ~ComplexGridFunction() { Destroy(); }
};
/** Class for a complex-valued linear form
The @a convention argument in the class's constructor is documented in the
mfem::ComplexOperator class found in linalg/complex_operator.hpp.
When supplying integrators to the ComplexLinearForm either the real or
imaginary integrator can be NULL. This indicates that the corresponding
portion of the complex-valued field is equal to zero.
*/
class ComplexLinearForm : public Vector
{
private:
ComplexOperator::Convention conv;
ComplexOperator::Convention conv_;
protected:
LinearForm * lfr;
LinearForm * lfi;
LinearForm * lfr_;
LinearForm * lfi_;
// HYPRE_Int * tdof_offsets_;
public:
ComplexLinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
virtual ~ComplexLinearForm();
ComplexOperator::Convention GetConvention() const { return conv; }
void SetConvention(const ComplexOperator::Convention &
convention) { conv = convention; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
FiniteElementSpace *FESpace() const { return lfr_->FESpace(); }
/** @brief Add new Boundary Integrator, restricted to the given boundary
attributes.
Assumes ownership of @a lfi_real and @a lfi_imag.
The array @a bdr_attr_marker is stored internally as a pointer to the
given Array<int> object. */
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker);
/// Adds new Boundary Face Integrator. Assumes ownership of @a lfi.
void AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
/** @brief Add new Boundary Face Integrator, restricted to the given boundary
attributes.
Assumes ownership of @a lfi_real and @a lfi_imag.
The array @a bdr_attr_marker is stored internally as a pointer to the
given Array<int> object. */
void AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker);
FiniteElementSpace *FESpace() const { return lfr->FESpace(); }
LinearForm & real() { return *lfr; }
LinearForm & imag() { return *lfi; }
const LinearForm & real() const { return *lfr; }
const LinearForm & imag() const { return *lfi; }
LinearForm & real() { return *lfr_; }
LinearForm & imag() { return *lfi_; }
const LinearForm & real() const { return *lfr_; }
const LinearForm & imag() const { return *lfi_; }
void Update();
void Update(FiniteElementSpace *f);
@@ -153,44 +114,32 @@ public:
void Assemble();
std::complex<double> operator()(const ComplexGridFunction &gf) const;
};
/** Class for sesquilinear form
A sesquilinear form is a generalization of a bilinear form to complex-valued
fields. Sesquilinear forms are linear in the second argument but the first
argument involves a complex conjugate in the sense that:
a(alpha u, beta v) = conj(alpha) beta a(u, v)
The @a convention argument in the class's constructor is documented in the
mfem::ComplexOperator class found in linalg/complex_operator.hpp.
When supplying integrators to the SesquilinearForm either the real or
imaginary integrator can be NULL. This indicates that the corresponding
portion of the complex-valued material coefficient is equal to zero.
*/
// Class for sesquilinear form
class SesquilinearForm
{
private:
ComplexOperator::Convention conv;
ComplexOperator::Convention conv_;
BilinearForm *blfr;
BilinearForm *blfi;
//protected:
BilinearForm *blfr_;
BilinearForm *blfi_;
public:
SesquilinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
ComplexOperator::Convention GetConvention() const { return conv; }
ComplexOperator::Convention GetConvention() const { return conv_; }
void SetConvention(const ComplexOperator::Convention &
convention) { conv = convention; }
convention) { conv_ = convention; }
BilinearForm & real() { return *blfr; }
BilinearForm & imag() { return *blfi; }
const BilinearForm & real() const { return *blfr; }
const BilinearForm & imag() const { return *blfi; }
BilinearForm & real() { return *blfr_; }
BilinearForm & imag() { return *blfi_; }
const BilinearForm & real() const { return *blfr_; }
const BilinearForm & imag() const { return *blfi_; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
@@ -205,25 +154,6 @@ public:
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker);
/// Adds new interior Face Integrator. Assumes ownership of @a bfi.
void AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new boundary Face Integrator. Assumes ownership of @a bfi.
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/** @brief Adds new boundary Face Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The array @a bdr_marker is stored internally as a pointer to the given
Array<int> object. */
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker);
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
@@ -232,10 +162,10 @@ public:
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix has to be deleted by the caller. */
ComplexSparseMatrix *AssembleComplexSparseMatrix();
ComplexSparseMatrix *AssembleCompSpMat();
/// Return the parallel FE space associated with the ParBilinearForm.
FiniteElementSpace *FESpace() const { return blfr->FESpace(); }
FiniteElementSpace *FESpace() const { return blfr_->FESpace(); }
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
@@ -253,17 +183,16 @@ public:
#ifdef MFEM_USE_MPI
/// Class for parallel complex-valued grid function - real + imaginary part
/// Vector with associated parallel FE space.
/// Class for complex-valued grid function - Vector with associated FE space.
class ParComplexGridFunction : public Vector
{
private:
ParGridFunction * pgfr;
ParGridFunction * pgfi;
ParGridFunction * pgfr_;
ParGridFunction * pgfi_;
protected:
void Destroy() { delete pgfr; delete pgfi; }
void Destroy() { delete pgfr_; delete pgfi_; }
public:
@@ -275,7 +204,7 @@ public:
/// Assign constant values to the ParComplexGridFunction data.
ParComplexGridFunction &operator=(const std::complex<double> & value)
{ *pgfr = value.real(); *pgfi = value.imag(); return *this; }
{ *pgfr_ = value.real(); *pgfi_ = value.imag(); return *this; }
virtual void ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff);
@@ -298,60 +227,29 @@ public:
/// Returns the vector restricted to the true dofs.
void ParallelProject(Vector &tv) const;
FiniteElementSpace *FESpace() { return pgfr->FESpace(); }
const FiniteElementSpace *FESpace() const { return pgfr->FESpace(); }
ParFiniteElementSpace *ParFESpace() { return pgfr->ParFESpace(); }
const ParFiniteElementSpace *ParFESpace() const { return pgfr->ParFESpace(); }
ParGridFunction & real() { return *pgfr; }
ParGridFunction & imag() { return *pgfi; }
const ParGridFunction & real() const { return *pgfr; }
const ParGridFunction & imag() const { return *pgfi; }
virtual double ComputeL2Error(Coefficient &exsolr, Coefficient &exsoli,
const IntegrationRule *irs[] = NULL) const
{
double err_r = pgfr->ComputeL2Error(exsolr, irs);
double err_i = pgfi->ComputeL2Error(exsoli, irs);
return sqrt(err_r * err_r + err_i * err_i);
}
virtual double ComputeL2Error(VectorCoefficient &exsolr,
VectorCoefficient &exsoli,
const IntegrationRule *irs[] = NULL,
Array<int> *elems = NULL) const
{
double err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
double err_i = pgfi->ComputeL2Error(exsoli, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
FiniteElementSpace *FESpace() { return pgfr_->FESpace(); }
const FiniteElementSpace *FESpace() const { return pgfr_->FESpace(); }
ParGridFunction & real() { return *pgfr_; }
ParGridFunction & imag() { return *pgfi_; }
const ParGridFunction & real() const { return *pgfr_; }
const ParGridFunction & imag() const { return *pgfi_; }
/// Destroys grid function.
virtual ~ParComplexGridFunction() { Destroy(); }
};
/** Class for a complex-valued, parallel linear form
The @a convention argument in the class's constructor is documented in the
mfem::ComplexOperator class found in linalg/complex_operator.hpp.
When supplying integrators to the ParComplexLinearForm either the real or
imaginary integrator can be NULL. This indicates that the corresponding
portion of the complex-valued field is equal to zero.
*/
class ParComplexLinearForm : public Vector
{
private:
ComplexOperator::Convention conv;
ComplexOperator::Convention conv_;
protected:
ParLinearForm * plfr;
ParLinearForm * plfi;
ParLinearForm * plfr_;
ParLinearForm * plfi_;
HYPRE_Int * tdof_offsets;
HYPRE_Int * tdof_offsets_;
public:
@@ -361,50 +259,16 @@ public:
virtual ~ParComplexLinearForm();
ComplexOperator::Convention GetConvention() const { return conv; }
void SetConvention(const ComplexOperator::Convention &
convention) { conv = convention; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
ParFiniteElementSpace *ParFESpace() const { return plfr_->ParFESpace(); }
/** @brief Add new Boundary Integrator, restricted to the given boundary
attributes.
Assumes ownership of @a lfi_real and @a lfi_imag.
The array @a bdr_attr_marker is stored internally as a pointer to the
given Array<int> object. */
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker);
/// Adds new Boundary Face Integrator. Assumes ownership of @a lfi.
void AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
/** @brief Add new Boundary Face Integrator, restricted to the given boundary
attributes.
Assumes ownership of @a lfi_real and @a lfi_imag.
The array @a bdr_attr_marker is stored internally as a pointer to the
given Array<int> object. */
void AddBdrFaceIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &bdr_attr_marker);
ParFiniteElementSpace *ParFESpace() const { return plfr->ParFESpace(); }
ParLinearForm & real() { return *plfr; }
ParLinearForm & imag() { return *plfi; }
const ParLinearForm & real() const { return *plfr; }
const ParLinearForm & imag() const { return *plfi; }
ParLinearForm & real() { return *plfr_; }
ParLinearForm & imag() { return *plfi_; }
const ParLinearForm & real() const { return *plfr_; }
const ParLinearForm & imag() const { return *plfi_; }
void Update(ParFiniteElementSpace *pf = NULL);
@@ -421,42 +285,29 @@ public:
};
/** Class for a parallel sesquilinear form
A sesquilinear form is a generalization of a bilinear form to complex-valued
fields. Sesquilinear forms are linear in the second argument but but the
first argument involves a complex conjugate in the sense that:
a(alpha u, beta v) = conj(alpha) beta a(u, v)
The @a convention argument in the class's constructor is documented in the
mfem::ComplexOperator class found in linalg/complex_operator.hpp.
When supplying integrators to the ParSesquilinearForm either the real or
imaginary integrator can be NULL. This indicates that the corresponding
portion of the complex-valued material coefficient is equal to zero.
*/
// Class for parallel sesquilinear form
class ParSesquilinearForm
{
private:
ComplexOperator::Convention conv;
ComplexOperator::Convention conv_;
ParBilinearForm *pblfr;
ParBilinearForm *pblfi;
//protected:
ParBilinearForm *pblfr_;
ParBilinearForm *pblfi_;
public:
ParSesquilinearForm(ParFiniteElementSpace *pf,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
ComplexOperator::Convention GetConvention() const { return conv; }
ComplexOperator::Convention GetConvention() const { return conv_; }
void SetConvention(const ComplexOperator::Convention &
convention) { conv = convention; }
convention) { conv_ = convention; }
ParBilinearForm & real() { return *pblfr; }
ParBilinearForm & imag() { return *pblfi; }
const ParBilinearForm & real() const { return *pblfr; }
const ParBilinearForm & imag() const { return *pblfi; }
ParBilinearForm & real() { return *pblfr_; }
ParBilinearForm & imag() { return *pblfi_; }
const ParBilinearForm & real() const { return *pblfr_; }
const ParBilinearForm & imag() const { return *pblfi_; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
@@ -466,36 +317,11 @@ public:
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/** @brief Adds new boundary Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The array @a bdr_marker is stored internally as a pointer to the given
Array<int> object. */
/// Adds new Boundary Integrator, restricted to specific boundary attributes.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker);
/// Adds new interior Face Integrator. Assumes ownership of @a bfi.
void AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new boundary Face Integrator. Assumes ownership of @a bfi.
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/** @brief Adds new boundary Face Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The array @a bdr_marker is stored internally as a pointer to the given
Array<int> object. */
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker);
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
@@ -507,7 +333,7 @@ public:
ComplexHypreParMatrix *ParallelAssemble();
/// Return the parallel FE space associated with the ParBilinearForm.
ParFiniteElementSpace *ParFESpace() const { return pblfr->ParFESpace(); }
ParFiniteElementSpace *ParFESpace() const { return pblfr_->ParFESpace(); }
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
+5 -386
View File
@@ -14,6 +14,7 @@
#include "../general/text.hpp"
#include "picojson.h"
#include <fstream>
#include <cerrno> // errno
#include <sstream>
@@ -107,7 +108,6 @@ DataCollection::DataCollection(const std::string& collection_name, Mesh *mesh_)
precision = precision_default;
pad_digits_cycle = pad_digits_rank = pad_digits_default;
format = SERIAL_FORMAT; // use serial mesh format
compression = false;
error = NO_ERROR;
}
@@ -161,14 +161,6 @@ void DataCollection::SetFormat(int fmt)
format = fmt;
}
void DataCollection::SetCompression(bool comp)
{
compression = comp;
#ifdef MFEM_USE_GZSTREAM
MFEM_ASSERT(!compression, "GZStream not enabled in MFEM build.");
#endif
}
void DataCollection::SetPrefixPath(const std::string& prefix)
{
if (!prefix.empty())
@@ -227,8 +219,7 @@ void DataCollection::SaveMesh()
}
std::string mesh_name = GetMeshFileName();
const char *mode = (compression) ? "zwb6" : "w";
ofgzstream mesh_file(mesh_name.c_str(), mode);
std::ofstream mesh_file(mesh_name.c_str());
mesh_file.precision(precision);
#ifdef MFEM_USE_MPI
const ParMesh *pmesh = dynamic_cast<const ParMesh*>(mesh);
@@ -276,9 +267,7 @@ const
void DataCollection::SaveOneField(const FieldMapIterator &it)
{
const char *mode = (compression) ? "zwb6" : "w";
ofgzstream field_file(GetFieldFileName(it->first).c_str(), mode);
std::ofstream field_file(GetFieldFileName(it->first).c_str());
field_file.precision(precision);
(it->second)->Save(field_file);
if (!field_file)
@@ -290,8 +279,7 @@ void DataCollection::SaveOneField(const FieldMapIterator &it)
void DataCollection::SaveOneQField(const QFieldMapIterator &it)
{
const char *mode = (compression) ? "zwb6" : "w";
ofgzstream q_field_file(GetFieldFileName(it->first).c_str(), mode);
std::ofstream q_field_file(GetFieldFileName(it->first).c_str());
q_field_file.precision(precision);
(it->second)->Save(q_field_file);
if (!q_field_file)
@@ -588,7 +576,7 @@ void VisItDataCollection::LoadFields()
it != field_info_map.end(); ++it)
{
std::string fname = path_left + it->first + path_right;
ifgzstream file(fname.c_str());
std::ifstream file(fname.c_str());
// TODO: in parallel, check for errors on all processors
if (!file)
{
@@ -725,373 +713,4 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
}
}
ParaViewDataCollection::~ParaViewDataCollection()
{
if (myrank==0)
{
// Close the data collection
pvd_stream << "</Collection>" << std::endl;
pvd_stream << "</VTKFile>" << std::endl;
pvd_stream.close();
}
}
ParaViewDataCollection::ParaViewDataCollection(const std::string&
collection_name,
mfem::Mesh *mesh_)
:DataCollection(collection_name, mesh_)
{
myrank = 0;
nprocs = 1;
levels_of_detail = 1;
#ifdef MFEM_USE_MPI
lcomm = MPI_COMM_SELF;
#endif
std::string dpath=GenerateCollectionPath();
std::string pvdname=dpath+"/"+GeneratePVDFileName();
create_directory(dpath); // this one is a serial
pvd_stream.open(pvdname.c_str(),std::ios::out);
// initialize the file
pvd_stream << "<?xml version=\"1.0\"?>" << std::endl;
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"" << std::endl;
pvd_stream << " byte_order=\"LittleEndian\"" << std::endl;
pvd_stream << " compressor=\"vtkZLibDataCompressor\">" << std::endl;
pvd_stream << "<Collection>" << std::endl;
}
void ParaViewDataCollection::SetMesh(mfem::Mesh * new_mesh)
{
DataCollection::SetMesh(new_mesh);
}
void ParaViewDataCollection::RegisterField(const std::string& field_name,
mfem::GridFunction *gf)
{
DataCollection::RegisterField(field_name,gf);
}
void ParaViewDataCollection::SetLevelsOfDetail(int levels_of_detail_)
{
levels_of_detail = levels_of_detail_;
}
void ParaViewDataCollection::Load(int )
{
MFEM_WARNING("ParaViewDataCollection::Load() is not implemented!");
}
std::string ParaViewDataCollection::GenerateCollectionPath()
{
std::string out = "";
out=DataCollection::GetPrefixPath() + DataCollection::GetCollectionName();
return out;
}
std::string ParaViewDataCollection::GeneratePVTUPath()
{
std::string out = "Cycle" + to_padded_string(cycle,pad_digits_cycle);
return out;
}
std::string ParaViewDataCollection::GenerateVTUPath()
{
std::string out = GeneratePVTUPath();
return out;
}
std::string ParaViewDataCollection::GeneratePVDFileName()
{
std::string out = GetCollectionName()+".pvd";
return out;
}
std::string ParaViewDataCollection::GeneratePVTUFileName()
{
std::string out = "data.pvtu";
return out;
}
std::string ParaViewDataCollection::GenerateVTUFileName()
{
std::string out = "proc" + to_padded_string(myrank,pad_digits_rank)+".vtu";
return out;
}
std::string ParaViewDataCollection::GenerateVTUFileName(int crank)
{
std::string out = "proc" + to_padded_string(crank,pad_digits_rank)+".vtu";
return out;
}
void ParaViewDataCollection::Save()
{
// add a new collection to the PDV file
// check if the directories are created
{
std::string path = GenerateCollectionPath()+"/"+GenerateVTUPath();
#ifndef MFEM_USE_MPI
int err = create_directory(path);
#else
int err;
if (nprocs==1)
{
err = create_directory(path);
}
else
{
err = create_directory(path,myrank,lcomm);
}
#endif
if (err)
{
error = WRITE_ERROR;
MFEM_WARNING("Error creating directory: " << path);
return; // do not even try to write the mesh
}
}
// the directory is created
// define the vtu file
{
std::string fname = GenerateCollectionPath()+"/"+GenerateVTUPath()+"/"
+GenerateVTUFileName();
std::fstream out; out.open(fname.c_str(), std::ios::out);
SaveDataVTU(out,levels_of_detail);
out.close();
}
// define the pvtu file only on process 0
if (myrank==0)
{
std::string fname = GenerateCollectionPath()+"/"+GeneratePVTUPath()+"/"
+GeneratePVTUFileName();
std::fstream out; out.open(fname.c_str(), std::ios::out);
out << "<?xml version=\"1.0\"?>" << std::endl;
out << "<VTKFile type=\"PUnstructuredGrid\"";
out << " version =\"0.1\" byte_order=\"LittleEndian\"> " << std::endl;
out << "<PUnstructuredGrid GhostLevel=\"0\">" << std::endl ;
out << "<PPoints>" << std::endl;
out << "\t<PDataArray type=\"Float64\" ";
out << " Name=\"Points\" NumberOfComponents=\"3\"/>" << std::endl;
out << "</PPoints>" << std::endl;
out << "<PCells>" << std::endl ;
out << "\t<PDataArray type=\"Int32\" ";
out << " Name=\"connectivity\" NumberOfComponents=\"1\"/>" << std::endl ;
out << "\t<PDataArray type=\"Int32\" ";
out << " Name=\"offsets\" NumberOfComponents=\"1\"/>" << std::endl ;
out << "\t<PDataArray type=\"UInt8\" ";
out << " Name=\"types\" NumberOfComponents=\"1\"/>" << std::endl ;
out << "</PCells>" << std::endl ;
out << "<PPointData>" << std::endl ;
for (FieldMapIterator it=field_map.begin(); it!=field_map.end(); ++it)
{
out << "<PDataArray type=\"Float64\" Name=\"" << it->first;
int vec_dim=it->second->VectorDim();
out<<"\" NumberOfComponents=\""<< vec_dim <<"\" format=\"ascii\" />" <<
std::endl;
}
out << "</PPointData>" << std::endl ;
// CELL DATA
out << "<PCellData>" << std::endl ;
out << "\t<PDataArray type=\"Int32\" Name=\"" << "material"
<<"\" NumberOfComponents=\"1\"/> " << std::endl ;
out << "</PCellData>" << std::endl ;
for (int ii=0; ii<nprocs; ii++)
{
// this one is generated without the path
std::string nfname=GenerateVTUFileName(ii);
out << "<Piece Source=\"" << nfname << "\"/>" << std::endl;
}
out << "</PUnstructuredGrid>" << std::endl;
out << "</VTKFile>" << std::endl;
out.close();
fname = GeneratePVTUPath()+"/"+GeneratePVTUFileName();
// add the pvtu file to the pvd_stream
pvd_stream << "<DataSet timestep=\"" << GetTime(); // GetCycle();
pvd_stream << "\" group=\"\" part=\"" << 0 << "\" file=\"";
pvd_stream << fname << "\"/>" << std::endl;
}
}
void ParaViewDataCollection::SaveDataVTU(std::ostream &out, int ref)
{
out << "<VTKFile type=\"UnstructuredGrid\" ";
out << " version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl;
out << "<UnstructuredGrid>" << std::endl;
mesh->PrintVTU(out,ref);
// dump out the grid functions as point data
out << "<PointData >" << std::endl;
// save the grid functions
// iterate over all grid functions
for (FieldMapIterator it=field_map.begin(); it!=field_map.end(); ++it)
{
SaveGFieldVTU(out,ref,it);
}
// iterate over all quadrature functions
// if the Quadrature functions are dumped as cell data
// the cycle should be moved before the grid functions
// and the PrintVTU CellData section should be open in the mesh dump
for (QFieldMapIterator it=q_field_map.begin(); it!=q_field_map.end(); ++it)
{
// save the quadrature functions
// this one is not implemented yet
SaveQFieldVTU(out,ref,it);
}
out << "</PointData>" << std::endl;
// close the mesh
out << "</Piece>" << std::endl; // close the piece open in the PrintVTU method
out << "</UnstructuredGrid>" << std::endl;
out << "</VTKFile>" << std::endl;
}
void ParaViewDataCollection::SaveQFieldVTU(std::ostream &out, int ref,
const QFieldMapIterator& it )
{
MFEM_WARNING("SaveQFieldVTU is wotk in progress - field name:"<<it->second);
}
void ParaViewDataCollection::SaveGFieldVTU(std::ostream &out, int ref_,
const FieldMapIterator& it)
{
RefinedGeometry *RefG;
Vector val;
DenseMatrix vval, pmat;
int vec_dim = it->second->VectorDim();
if (vec_dim == 1)
{
// scalar data
out << "<DataArray type=\"Float64\" Name=\"" << it->first;
out << "\" NumberOfComponents=\"1\" format=\"ascii\" >" << std::endl;
for (int i = 0; i < mesh->GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(i), ref_, 1);
it->second->GetValues(i, RefG->RefPts, val, pmat);
for (int j = 0; j < val.Size(); j++)
{
out << val(j) << '\n';
}
}
}
else
{
// vector data
out << "<DataArray type=\"Float64\" Name=\"" << it->first;
out << "\" NumberOfComponents=\"" << vec_dim << "\" format=\"ascii\" >" <<
std::endl;
for (int i = 0; i < mesh->GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(i), ref_, 1);
it->second->GetVectorValues(i, RefG->RefPts, vval, pmat);
for (int jj = 0; jj < vval.Width(); jj++)
{
for (int ii = 0; ii < vval.Height(); ii++)
{
out << vval(ii, jj) << ' ';
}
out << std::endl;
}
}
}
out << "</DataArray>" << std::endl;
out.flush();
}
int ParaViewDataCollection::create_directory(const std::string &dir_name)
{
// create directories recursively
const char path_delim = '/';
std::string::size_type pos = 0;
int err;
do
{
pos = dir_name.find(path_delim, pos+1);
std::string subdir = dir_name.substr(0, pos);
err = mkdir(subdir.c_str(), 0777);
err = (err && (errno != EEXIST)) ? 1 : 0;
}
while ( pos != std::string::npos );
return err;
}
#ifdef MFEM_USE_MPI
ParaViewDataCollection::ParaViewDataCollection(const std::string&
collection_name,
mfem::ParMesh *mesh_)
:DataCollection(collection_name,mesh_)
{
lcomm = mesh_->GetComm();
MPI_Comm_rank(lcomm, &myrank);
MPI_Comm_size(lcomm, &nprocs);
levels_of_detail = 1;
std::string dpath = GenerateCollectionPath();
std::string pvdname = dpath+"/"+GeneratePVDFileName();
int err = create_directory(dpath,myrank,lcomm);
if (err) { MFEM_ABORT("Cannot create the directory:"<<dpath);}
if (myrank==0)
{
pvd_stream.open(pvdname.c_str(),std::ios::out);
pvd_stream << "<?xml version=\"1.0\"?>" << std::endl;
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"" << std::endl;
pvd_stream << " byte_order=\"LittleEndian\"" << std::endl;
pvd_stream << " compressor=\"vtkZLibDataCompressor\">" << std::endl;
pvd_stream << "<Collection>" << std::endl;
}
}
int ParaViewDataCollection::create_directory(const std::string &dir_name,
int myid,
MPI_Comm lcomm_)
{
// create directories recursively
const char path_delim = '/';
std::string::size_type pos = 0;
int err;
// create the directories only on process 0
if (myid==0)
{
do
{
pos = dir_name.find(path_delim, pos+1);
std::string subdir = dir_name.substr(0, pos);
err = mkdir(subdir.c_str(), 0777);
err = (err && (errno != EEXIST)) ? 1 : 0;
}
while ( pos != std::string::npos );
}
// broadcast the error
MPI_Bcast(&err, 1, MPI_INT, 0, lcomm_);
return err;
}
void ParaViewDataCollection::SetMesh(MPI_Comm comm, mfem::Mesh *new_mesh)
{
DataCollection::SetMesh(new_mesh);
lcomm = comm;
MPI_Comm_rank(comm, &myrank);
MPI_Comm_size(comm, &nprocs);
}
#endif
} // end namespace MFEM

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