Compare commits

..
Author SHA1 Message Date
Tucker Babcock 8bcd14bcfb Cleaned up unordered_map implementation and added safer float comparison as a fallback for out_of_range errors thrown by the map. 2019-05-17 12:11:49 -04:00
Tucker Babcock 04029239d4 Merge branch 'sbp-dev' into hashtable-sbp-dev
Merging sbp-dev into hashtable-sbp-dev to test hashtable implementation with more recent sbp-dev branch before merging back to sbp-dev
2019-05-17 10:57:57 -04:00
Tucker Babcock 331b15563d moving timing to CalcDShape() 2019-05-16 21:23:32 -04:00
Tucker Babcock 13a48c2058 changed exSBP to use SBP operator as default 2019-05-16 20:13:14 -04:00
Tucker Babcock 95a0d63574 changed exSBP to use SBP operator as default 2019-05-16 20:02:39 -04:00
Jason Hicken 75a1fd2388 Merge branch 'sbp-dev' of github.com:mfem/mfem into sbp-dev 2019-05-16 17:05:24 -04:00
Jason Hicken d47185fdd3 added FunctionSpace::SBPk and started using this to determine quadrature rule for SBP discretizations 2019-05-16 17:02:38 -04:00
Tucker Babcock 55951f3778 moved Dx and Dy SBP matrices from SBP_TriangleElement() constructor to fe_sbp.hpp header 2019-05-16 16:02:04 -04:00
Tucker Babcock 6d2e45576d added assembly timing to SBP example 2019-05-06 15:51:44 -04:00
Tucker Babcock f61773132d hashtable implemented for SBP_TriangleElement with error handling to revert to linear search if unordered_map::at() throws an out of range error. 2019-05-06 15:23:05 -04:00
Tucker Babcock cda16bf147 added unordered map the SBP_TriangleElement 2019-05-06 12:31:08 -04:00
Tucker Babcock 298417664b modified .gitignore to ingnore meshes generated by exSBP as well as editor files created by Visual Studio Code 2019-05-03 12:25:39 -05:00
Tucker Babcock d386bdd4b4 modified SBP_TriangleElement::CacDShape() to get and set column/row references for Dx, Dy, and dshape using DenseMatrix member functions GetRow() and SetCol(). 2019-05-03 12:24:48 -05:00
Jason Hicken 4798173ecf renamed C_SBPSegmentElement and C_SBPTriangleElement to SBP_SegmentElement and SBP_TriangleElement 2019-05-02 16:08:37 -04:00
Tucker Babcock 0aed9d2752 Added new finite element collection, C_SBPCollection, which includes new finite elements C_SBPTriangleElement and C_SBPSegmentElement. These elements are collocated summation-by-parts operators. This required creation of new IntegrationRules::Get() to allow integration rurule for these elements to be specified. This overloaded method passes in the finite element, and switches on the operator type. If it is an SBP operator, the element's are returned as the integration rule, since in the FiniteElement class the member is an integration rule type. If the operator is a traditional finite element, the existing Get() method is called. This required forward declaration of FiniteElement class in intrules.hpp. Usage of IntegrationRules::Get() has been switched to overloaded version in DiffusionIntegrator::AssembleElementMatrix, DomainLFIntegrator::AssembleElementRHSVector, and GridFunciton::ComputerLpError. 2019-05-02 12:56:57 -05:00
Tucker Babcock ed3df1c25a Added new example, exSBP and added it as a target in the makefile 2019-05-02 12:49:43 -05:00
Tucker Babcock 9898923e66 Adding test mesh for exSBP example code. 2019-05-02 12:44:25 -05:00
190 changed files with 11403 additions and 18261 deletions
+7 -6
View File
@@ -26,18 +26,19 @@ 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
+7 -6
View File
@@ -39,6 +39,9 @@ doc/CodeDocumentation
*.dSYM
.DS_Store
# Editor files
.vscode
# Example and miniapp binaries and outputs
examples/ex[1-9]
@@ -82,9 +85,10 @@ 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/exSBP*
examples/sundials/ex9
examples/sundials/ex1[06]
@@ -183,6 +187,3 @@ miniapps/nurbs/Example1*
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
# VPATH builds
build-*/*
+36 -93
View File
@@ -8,59 +8,22 @@
http://mfem.org
Version 4.0.1 (development)
===========================
Version 4.0-RC2, Apr 24, 2019
=============================
Improved GPU support
--------------------
- 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, the
list of backends is: "occa-cuda", "raja-cuda", "cuda", "hip", "occa-omp",
"raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
Requirements and Limitations
----------------------------
- This is a release candidate for mfem-4.0.
- Use at your own risk -- not everything will work and the API may change.
- We are looking for feedback from friendly users.
- Unlike previous MFEM releases, this version requires a C++11 compiler.
- Improved RAJA backend and multi-GPU MPI communications.
Discretization improvements
---------------------------
- Added support for non-conforming prism AMR, including coarsening and parallel
load balancing. Anisotropic prism refinement is only available in the serial
version at the moment.
Meshing improvements
--------------------
- 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.
- The TMOP mesh optimization algorithms have been improved to support AMR meshes.
- 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.
- Improved element numbering after uniform mesh refinement.
New and updated examples and miniapps
-------------------------------------
- The mesh-optimizer and pmesh-optimizer miniapps have been updated to
demonstrate the new r-adaptivity capabilities of TMOP.
- The (p)mesh-optimizer miniapp has been updated to demonstrate mesh
optimization for an AMR mesh.
Miscellaneous
-------------
- Upgraded the SUNDIALS interface to utilize SUNDIALS version 5.0. This
necessitated a complete rework of the interface and requires changes at
the application level. Example usage of this new interface can be found
in the examples/sundials directory.
Version 4.0, released on May 24, 2019
=====================================
Unlike previous MFEM releases, this version requires a C++11 compiler.
- GPU-related limitations:
* Hypre preconditioners are not yet available in GPU mode.
* Only constant coefficients are currently supported on GPUs.
* Full-assembly (on device), element assembly, and matrix-free bilinear forms
are not supported yet. Element batching is currently ignored.
* Partial assembly kernels are not implemented yet for simplices.
GPU support
-----------
@@ -71,7 +34,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
@@ -79,43 +42,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
@@ -127,13 +73,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.
@@ -154,10 +100,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
@@ -171,6 +113,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
@@ -186,7 +132,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
@@ -198,24 +144,21 @@ New and improved solvers and preconditioners
Miscellaneous
-------------
- Added unit tests based on the Catch++ library in the test/ directory.
- 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.
- 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.
+7 -6
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})
@@ -254,7 +254,7 @@ endif()
# Axom/Sidre
if (MFEM_USE_SIDRE)
find_package(Axom REQUIRED Axom)
find_package(Axom REQUIRED Sidre SLIC axom_utils)
endif()
# PUMI
@@ -286,6 +286,7 @@ if (MFEM_USE_CUDA)
set(CUDA_CCBIN_COMPILER ${CMAKE_CXX_COMPILER})
endif()
string(APPEND CMAKE_CUDA_FLAGS " -ccbin ${CUDA_CCBIN_COMPILER}")
set(MFEM_USE_MM YES CACHE BOOL "Enable MFEM's memory manager" FORCE)
endif()
# OCCA
@@ -395,11 +396,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.
@@ -415,7 +416,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:
+25 -45
View File
@@ -28,16 +28,13 @@ 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
@@ -78,10 +75,6 @@ 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
@@ -168,18 +161,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.
@@ -383,11 +372,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
@@ -415,19 +404,18 @@ MFEM_USE_PUMI = YES/NO
models and effectively supports automated adaptive analysis. PUMI enables
support for parallel unstructured mesh modifications in MFEM.
MFEM_USE_MM = YES/NO
Enables support for the MFEM's memory manager (MM), which is required to
support devices with different memory spaces. This option is required when
CUDA support is enabled, i.e. when MFEM_USE_CUDA=YES.
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
@@ -488,7 +476,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
@@ -543,8 +530,7 @@ 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.
@@ -563,16 +549,11 @@ The specific libraries and their options are:
URL: https://developer.nvidia.com/cuda-toolkit
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
- HIP, used when MFEM_USE_HIP = YES.
URL: https://rocm.github.io/ROCmInstall.html
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
- OCCA, used when MFEM_USE_OCCA = YES.
URL: https://libocca.org
Options: OCCA_DIR, OCCA_OPT, OCCA_LIB.
- RAJA, used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
@@ -715,7 +696,7 @@ MFEM_USE_PUMI
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_RAJA
MFEM_USE_SIDRE
MFEM_USE_MM
The following options are CMake specific:
@@ -764,7 +745,6 @@ The CMake build system adds auto-detection for the following packages/libraries:
- PUMI
- OCCA
- RAJA
- AXOM - Used when MFEM_USE_SIDRE is enabled
The following built-in CMake packages are also used:
+1
View File
@@ -41,6 +41,7 @@ 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_MM @MFEM_USE_MM@)
set(MFEM_USE_CUDA @MFEM_USE_CUDA@)
set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
+3
View File
@@ -120,6 +120,9 @@
// Enable MFEM functionality based on the OCCA library
#cmakedefine MFEM_USE_OCCA
// Enable MFEM's internal Memory Manager (needed e.g. for MFEM_USE_CUDA)
#cmakedefine MFEM_USE_MM
// 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)
@@ -720,7 +720,8 @@ 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 MFEM_USE_MM MFEM_USE_CUDA MFEM_USE_OCCA
MFEM_USE_RAJA)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
+11 -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
@@ -53,4 +56,9 @@
#endif
#endif // MFEM_USE_MPI not defined
// CUDA requires the memory manager
#if defined(MFEM_USE_CUDA) && !defined(MFEM_USE_MM)
#error Building with CUDA (MFEM_USE_CUDA=YES) requires MFEM_USE_MM=YES
#endif
#endif // MFEM_CONFIG_HPP
+4 -5
View File
@@ -121,20 +121,19 @@
// Enable MFEM functionality based on the PUMI library
// #define MFEM_USE_PUMI
// 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 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 -1
View File
@@ -42,9 +42,9 @@ MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
MFEM_USE_PUMI = @MFEM_USE_PUMI@
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_MM = @MFEM_USE_MM@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
+3 -2
View File
@@ -42,6 +42,7 @@ 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_MM "Enable MFEM's memory manager" OFF)
option(MFEM_USE_CUDA "Enable CUDA" OFF)
option(MFEM_USE_OCCA "Enable OCCA" OFF)
option(MFEM_USE_RAJA "Enable RAJA" OFF)
@@ -81,7 +82,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"
@@ -154,7 +155,7 @@ 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
+5 -17
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
@@ -130,9 +122,9 @@ MFEM_USE_SIDRE = NO
MFEM_USE_CONDUIT = NO
MFEM_USE_PUMI = NO
MFEM_USE_CUDA = NO
MFEM_USE_HIP = NO
MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_MM = NO
# Compile and link options for zlib.
ZLIB_DIR =
@@ -182,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)
@@ -209,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)
@@ -299,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
@@ -312,10 +304,6 @@ PUMI_LIB = -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf -lpcu -lgmi -lparma\
CUDA_OPT =
CUDA_LIB =
# HIP library configuration (currently not needed)
HIP_OPT =
HIP_LIB =
# OCCA library configuration
OCCA_DIR = @MFEM_DIR@/../occa
OCCA_OPT = -I$(OCCA_DIR)/include
+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
+2 -1
View File
@@ -276,7 +276,8 @@ case "$1" in
;;
-dev)
device_runs="yes"
mfem_config+=" MFEM_USE_CUDA=YES MFEM_USE_OCCA=YES MFEM_USE_RAJA=YES MFEM_USE_OPENMP=YES"
mfem_config+=" MFEM_USE_CUDA=YES MFEM_USE_MM=YES \
MFEM_USE_OCCA=YES MFEM_USE_RAJA=YES MFEM_USE_OPENMP=YES"
;;
-v)
valgrind="yes"
+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)
File diff suppressed because it is too large Load Diff
+1 -1
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
+2 -4
View File
@@ -37,9 +37,7 @@ namespace mfem {
*
* <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
@@ -79,8 +77,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>: 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
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

+2 -2
View File
@@ -27,7 +27,7 @@ list(APPEND ALL_EXE_SRCS
ex18.cpp
ex19.cpp
ex20.cpp
ex21.cpp
ex22.cpp
)
if (MFEM_USE_MPI)
@@ -52,7 +52,7 @@ if (MFEM_USE_MPI)
ex18p.cpp
ex19p.cpp
ex20p.cpp
ex21p.cpp
ex22p.cpp
)
endif()
+164 -245
View File
File diff suppressed because one or more lines are too long
+19 -15
View File
@@ -62,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);
@@ -75,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",
@@ -88,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.
@@ -112,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;
@@ -133,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.
@@ -145,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);
@@ -153,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.
@@ -202,7 +203,10 @@ int main(int argc, char *argv[])
// 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);
@@ -211,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";
@@ -221,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;
+4 -4
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-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 4544 -n 6 -o 3 -elast
// 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
//
+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;
+20 -16
View File
@@ -65,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);
@@ -78,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",
@@ -98,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.
@@ -122,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);
@@ -135,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;
@@ -162,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.
@@ -174,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);
@@ -182,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.
@@ -224,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;
@@ -240,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";
@@ -251,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;
+14 -14
View File
@@ -1,16 +1,16 @@
// MFEM Example 21
// MFEM Example 22
//
// Compile with: make ex21
// Compile with: make ex22
//
// 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: 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 is a version of Example 2 with a simple adaptive mesh
// refinement loop. The problem being solved is again the linear
@@ -287,11 +287,11 @@ int main(int argc, char *argv[])
}
{
ofstream mesh_ref_out("ex21_reference.mesh");
ofstream mesh_ref_out("ex22_reference.mesh");
mesh_ref_out.precision(16);
mesh.Print(mesh_ref_out);
ofstream mesh_out("ex21_deformed.mesh");
ofstream mesh_out("ex22_deformed.mesh");
mesh_out.precision(16);
GridFunction nodes(&fespace), *nodes_p = &nodes;
mesh.GetNodes(nodes);
@@ -301,7 +301,7 @@ int main(int argc, char *argv[])
mesh.Print(mesh_out);
mesh.SwapNodes(nodes_p, own_nodes);
ofstream x_out("ex21_displacement.sol");
ofstream x_out("ex22_displacement.sol");
x_out.precision(16);
x.Save(x_out);
}
+14 -14
View File
@@ -1,15 +1,15 @@
// MFEM Example 21
// MFEM Example 22
//
// Compile with: make ex21p
// Compile with: make ex22p
//
// 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 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 is a version of Example 2p with a simple adaptive mesh
// refinement loop. The problem being solved is again the linear
@@ -330,7 +330,7 @@ int main(int argc, char *argv[])
x.Update();
}
// 21. Inform also the bilinear and linear forms that the space has
// 22. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
@@ -338,9 +338,9 @@ int main(int argc, char *argv[])
{
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;
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);
+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();
+12 -11
View File
@@ -49,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);
@@ -59,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",
@@ -72,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)
@@ -96,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.
@@ -169,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
@@ -204,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.
+16 -15
View File
@@ -55,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);
@@ -65,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",
@@ -85,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)
@@ -107,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;
@@ -117,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.
@@ -201,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.
@@ -232,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.
+359
View File
@@ -0,0 +1,359 @@
// MFEM Example 1
//
// Compile with: make exSBP
//
// Sample runs: exSBP -sbp -o 0 -p 0 -r 1
// exSBP -sbp -o 4 -p 3
//
//
// Description: This example code builds on Example 1 but adds SBP operators.
// It 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 or SBP 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>
#include <chrono>
using namespace std;
using namespace mfem;
int problem;
// Prescribed time-independent boundary and right-hand side functions.
double bdr_func(const Vector &pt);
double rhs_func(const Vector &pt);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/unitGridTestMesh.msh";
int order = 1;
bool static_cond = false;
bool visualization = 1;
bool sbp = 1;
problem = 1;
int ref_levels = 0;
bool convOut = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&sbp, "-sbp", "--summationbyparts", "-no-sbp",
"--no-summationbyparts",
"Enable or disable use of SBP operators.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use: 0 = transcendental manufactured solution, "
"1 = linear displacement, "
"2 = quadratic displacement, "
"3 = cubic displacement, "
"4 = quartic displacement.");
args.AddOption(&ref_levels, "-r", "--ref-levels",
"Number of initial uniform refinement levels.");
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 triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
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
// '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.
{
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 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;
if (sbp)
{
fec = new C_SBPCollection(order, dim);
}
else 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;
// 7. 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;
// Create function coefficient bdr which holds the exact solution and is
// used to strongly impose boundary conditions.
FunctionCoefficient bdr(bdr_func);
// 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.
Array<int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
// Project boundary conditions onto grid function to strongly impose
// boundary conditions. BC's are defined in the function `bdr_func`.
x.ProjectBdrCoefficient(bdr, ess_bdr);
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 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);
FunctionCoefficient rhs(rhs_func);
b->AddDomainIntegrator(new DomainLFIntegrator(rhs));
if (problem < 0 || problem > 4)
{
mfem::out << "Invalid problem type: " << problem << "\n";
delete mesh;
return 3;
}
// // Start timing
// std::chrono::time_point<std::chrono::high_resolution_clock> start = std::chrono::high_resolution_clock::now();
b->Assemble();
// // End timing and compute interval
// std::chrono::time_point<std::chrono::high_resolution_clock> finish = std::chrono::high_resolution_clock::now();
// std::chrono::duration<double> elapsed = finish - start;
// std::cout << "\nb->Assemble() elapsed time: " << elapsed.count() << " s\n";
// 8. 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);
ConstantCoefficient one(1.0);
a->AddDomainIntegrator(new DiffusionIntegrator(one));
// 9. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
// // Start timing
// start = std::chrono::high_resolution_clock::now();
a->Assemble();
// // End timing and compute interval
// finish = std::chrono::high_resolution_clock::now();
// elapsed = finish - start;
// std::cout << "\na->Assemble() elapsed time: " << elapsed.count() << " s\n";
SparseMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
mfem::out << "Size of linear system: " << A.Height() << endl;
#ifndef MFEM_USE_SUITESPARSE
// 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system A X = B with PCG.
GSSmoother M(A);
PCG(A, M, B, X, 1, 1000, 1e-12, 0.0);
#else
// 10. 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
// 11. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
// 12. Compute and print the L^2 norm of the error.
mfem::out << "\n|| u_h - u ||_{L^2} = " << x.ComputeL2Error(bdr) << '\n' << endl;
// mfem::out << "h: " << 0.1 / pow(2, ref_levels) << "\n";
// 12. Save the refined mesh and the solution. This output can be viewed later
// using GLVis: "glvis -m refined.mesh -g sol.gf".
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
// mesh->PrintVTK(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
x.Save(sol_ofs);
// Save solution mesh in vtk file
char solFileName[32];
if (sbp)
{
snprintf(solFileName, 32, "exSBP_SBP_O%d_P%d.vtk", order, problem);
}
else
{
snprintf(solFileName, 32, "exSBP_FE_O%d_P%d.vtk", order, problem);
}
if (convOut)
{
// Save convergence study information in output file
char outfileName[32];
if (problem == 0)
{
snprintf(outfileName, 32, "convOutputP%d_manufactured.txt", order);
}
else if (problem == 1)
{
snprintf(outfileName, 32, "convOutputP%d_lin.txt", order);
}
else if (problem == 2)
{
snprintf(outfileName, 32, "convOutputP%d_quad.txt", order);
}
else if (problem == 3)
{
snprintf(outfileName, 32, "convOutputP%d_cubic.txt", order);
}
else if (problem == 4)
{
snprintf(outfileName, 32, "convOutputP%d_quartic.txt", order);
}
ofstream outputFile;
outputFile.open(outfileName, ios::out | ios::app);
if (outputFile.is_open())
{
outputFile << x.ComputeL2Error(bdr) << ", " << 0.1 / pow(2, ref_levels) << "\n";
}
outputFile.close();
}
ofstream omesh(solFileName);
omesh.precision(14);
mesh->PrintVTK(omesh, 1);
x.SaveVTK(omesh, "sol", 1);
// 13. 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;
}
// 14. Free the used memory.
delete a;
delete b;
delete fespace;
if (order > 0) { delete fec; }
delete mesh;
return 0;
}
// Exact solution, used for the Dirichlet BC.
double bdr_func(const Vector &pt)
{
double x = pt(0), y = pt(1), z = 0.0;
if (problem == 0) // manufactured solution
{
z = sin(M_PI*x)*sin(M_PI*y);
}
else if (problem == 1) // linear displacement
{
z = 0.5*x + 0.5*y;
}
else if (problem == 2) // quadratic displacement
{
z = 0.5*x*x + 0.5*y*y;
}
else if (problem == 3) // manufactured solution
{
z = 0.5*x*x*x + 0.5*y*y*y;
}
else if (problem == 4) // manufactured solution
{
z = 0.5*x*x*x*x + 0.5*y*y*y*y;
}
return z;
}
// right hand side function for manufactured solution
double rhs_func(const Vector &pt)
{
double x = pt(0), y = pt(1), z = 0.0;
if (problem == 0)
{
z = 2*M_PI*M_PI*sin(M_PI*x)*sin(M_PI*y);
}
else if (problem == 1)
{
z = 0;
}
else if (problem == 2)
{
z = -2;
}
else if (problem == 3)
{
z = -3*(x+y);
}
else if (problem == 4)
{
z = -6*(x*x + y*y);
}
return z;
}
+3 -3
View File
@@ -22,9 +22,9 @@ MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 ex17\
ex18 ex19 ex20 ex21
ex18 ex19 ex20 ex22 exSBP
PAR_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p ex12p\
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex22p
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
@@ -125,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 -4
View File
@@ -13,8 +13,7 @@ set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
bilininteg_diffusion.cpp
bilininteg_mass.cpp
bilininteg_ext.cpp
coefficient.cpp
datacollection.cpp
eltrans.cpp
@@ -32,13 +31,13 @@ set(SRCS
nonlininteg.cpp
staticcond.cpp
tmop.cpp
tmop_tools.cpp
)
set(HDRS
bilinearform.hpp
bilinearform_ext.hpp
bilininteg.hpp
bilininteg_ext.hpp
coefficient.hpp
datacollection.hpp
eltrans.hpp
@@ -65,7 +64,6 @@ set(HDRS
tfespace.hpp
tintrules.hpp
tmop.hpp
tmop_tools.hpp
)
if (MFEM_USE_SIDRE)
+55 -272
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:
@@ -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();
@@ -616,6 +592,10 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
if (ext)
{
if (P != NULL && assembly != AssemblyLevel::FULL && Device::IsEnabled())
{
P->BuildTranspose();
}
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
return;
}
@@ -645,8 +625,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); }
}
}
@@ -747,10 +727,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
@@ -1049,13 +1025,9 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
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;
dom = mbf->dom;
bdr = mbf->bdr;
skt = mbf->skt;
}
double & MixedBilinearForm::Elem (int i, int j)
@@ -1109,42 +1081,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)
{
int i, k;
Array<int> tr_vdofs, te_vdofs;
ElementTransformation *eltrans;
DenseMatrix elemmat;
@@ -1156,75 +1108,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);
@@ -1244,70 +1169,14 @@ 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()
@@ -1336,93 +1205,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());
@@ -1445,12 +1229,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;
@@ -1484,10 +1268,9 @@ MixedBilinearForm::~MixedBilinearForm()
if (!extern_bfs)
{
int i;
for (i = 0; i < dbfi.Size(); i++) { delete dbfi[i]; }
for (i = 0; i < bbfi.Size(); i++) { delete bbfi[i]; }
for (i = 0; i < tfbfi.Size(); i++) { delete tfbfi[i]; }
for (i = 0; i < btfbfi.Size(); i++) { delete btfbfi[i]; }
for (i = 0; i < 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]; }
}
}
@@ -1504,7 +1287,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++)
{
@@ -1514,17 +1297,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++)
@@ -1535,10 +1318,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);
+12 -130
View File
@@ -413,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);
@@ -553,26 +513,16 @@ protected:
FiniteElementSpace *trial_fes, ///< Not owned
*test_fes; ///< Not owned
/** @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.
@@ -636,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
@@ -647,32 +593,14 @@ 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; }
@@ -685,59 +613,13 @@ 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);
virtual void EliminateTestDofs(Array<int> &bdr_attr_is_ess);
void Update();
@@ -802,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 -50
View File
@@ -36,18 +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()
@@ -56,7 +54,7 @@ void PABilinearFormExtension::Assemble()
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssemblePA(*a->FESpace());
integrators[i]->Assemble(*a->FESpace());
}
}
@@ -66,13 +64,12 @@ 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,
@@ -100,52 +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 (elem_restrict_lex)
for (int i = 0; i < iSz; ++i)
{
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);
}
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]->AddMultPA(x, 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)
for (int i = 0; i < iSz; ++i)
{
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);
integrators[i]->MultAssembledTranspose(localX, localY);
}
else
elem_restrict->MultTranspose(localY, y);
}
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)
{
for (int e = 0; e < ne; ++e)
{
y.UseDevice(true);
y = 0.0;
for (int i = 0; i < iSz; ++i)
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)
{
integrators[i]->AddMultTransposePA(x, y);
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 ElemRestriction::Mult(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:ndofs, t?ndofs:vd);
DeviceMatrix d_y(y, t?vd:nedofs, t?nedofs:vd);
MFEM_FORALL(i, ndofs,
{
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;
}
}
});
}
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,
{
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 -11
View File
@@ -14,16 +14,32 @@
#include "../config/config.hpp"
#include "fespace.hpp"
#include "../general/device.hpp"
namespace mfem
{
class BilinearForm;
/// 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:
@@ -32,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;
@@ -67,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() {}
};
@@ -87,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() {}
};
@@ -95,9 +106,9 @@ 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*);
@@ -112,6 +123,8 @@ 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
@@ -130,7 +143,6 @@ public:
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
void Update() {}
~MFBilinearFormExtension() {}
};
+109 -55
View File
@@ -19,20 +19,19 @@ using namespace std;
namespace mfem
{
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
void BilinearFormIntegrator::Assemble(const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::Assemble (...)\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.");
@@ -379,7 +378,6 @@ void MixedScalarVectorIntegrator::AssembleElementMatrix2(
}
}
void DiffusionIntegrator::AssembleElementMatrix
( const FiniteElement &el, ElementTransformation &Trans,
DenseMatrix &elmat )
@@ -399,7 +397,36 @@ 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 if (el.Space() == FunctionSpace::SBPk)
{
ir = &el.GetNodes(); // SBP elements have collocated quadrature nodes
// and DOFs, weights are included in element
// construction so complete integration rule is
// defined by the element's `Nodes`
}
else
{
ir = &IntRules.Get(el.GetGeomType(), order);
}
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -455,7 +482,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++)
@@ -510,7 +558,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++)
@@ -670,27 +740,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,
@@ -706,7 +755,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++)
@@ -741,8 +804,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++)
@@ -763,20 +831,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,
@@ -848,7 +902,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++)
@@ -889,7 +943,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++)
@@ -1370,7 +1424,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);
}
}
@@ -3216,7 +3270,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());
@@ -3251,7 +3305,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());
@@ -3289,7 +3343,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());
@@ -3336,11 +3390,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
{
@@ -3389,7 +3443,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());
+63 -128
View File
@@ -15,6 +15,7 @@
#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "fespace.hpp"
#include "bilininteg_ext.hpp"
namespace mfem
{
@@ -22,45 +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).
/// 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);
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,
@@ -309,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;
@@ -383,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;
@@ -464,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;
@@ -1662,34 +1637,27 @@ protected:
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 DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
DofToQuad *maps;
GeometryExtension *geom;
int dim, ne, dofs1D, quad1D;
Vector pa_data;
public:
/// Construct a diffusion integrator with coefficient Q = 1
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; }
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; }
DiffusionIntegrator (MatrixCoefficient &q) : MQ(&q) { Q = NULL; maps = NULL; geom = NULL; }
/** Given a particular Finite Element
computes the element stiffness matrix elmat. */
@@ -1717,12 +1685,11 @@ public:
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
virtual void AssemblePA(const FiniteElementSpace&);
/// PA extension
virtual void Assemble(const FiniteElementSpace&);
virtual void MultAssembled(Vector&, Vector&);
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
virtual ~DiffusionIntegrator();
};
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
@@ -1734,15 +1701,13 @@ protected:
#endif
Coefficient *Q;
// PA extension
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;
public:
MassIntegrator(const IntegrationRule *ir = NULL)
: 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; }
@@ -1756,14 +1721,11 @@ public:
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
/// PA extension
virtual void Assemble(const FiniteElementSpace&);
virtual void MultAssembled(Vector&, Vector&);
virtual void AssemblePA(const FiniteElementSpace&);
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans);
virtual ~MassIntegrator();
};
class BoundaryMassIntegrator : public MassIntegrator
@@ -1782,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 &);
@@ -1803,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 &);
@@ -1829,17 +1787,16 @@ private:
Vector shape, te_shape, vec;
DenseMatrix partelmat;
DenseMatrix mcoeff;
int Q_order;
protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
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
@@ -1878,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; }
@@ -1903,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; }
@@ -1930,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; }
@@ -1952,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)
@@ -1984,8 +1930,6 @@ private:
DenseMatrix curlshape, curlshape_dFt, M;
DenseMatrix vshape, projcurl;
#endif
protected:
Coefficient *Q;
MatrixCoefficient *MQ;
@@ -2019,8 +1963,6 @@ private:
#ifndef MFEM_THREAD_SAFE
DenseMatrix dshape_hat, dshape, curlshape, Jadj, grad_hat, grad;
#endif
protected:
Coefficient *Q;
public:
@@ -2042,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; }
@@ -2053,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); }
@@ -2080,10 +2020,9 @@ 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;
@@ -2104,10 +2043,9 @@ public:
/// (Q div u, div v) for RT elements
class DivDivIntegrator: public BilinearFormIntegrator
{
protected:
private:
Coefficient *Q;
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape;
#endif
@@ -2129,10 +2067,9 @@ public:
diffusion matrix in each diagonal block. */
class VectorDiffusionIntegrator : public BilinearFormIntegrator
{
protected:
private:
Coefficient *Q;
private:
DenseMatrix Jinv;
DenseMatrix dshape;
DenseMatrix gshape;
@@ -2157,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;
@@ -2218,12 +2154,11 @@ public:
points. */
class DGTraceIntegrator : public BilinearFormIntegrator
{
protected:
private:
Coefficient *rho;
VectorCoefficient *u;
double alpha, beta;
private:
Vector shape1, shape2;
public:
@@ -2510,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,
@@ -2518,7 +2453,7 @@ public:
DenseMatrix &elmat);
protected:
Coefficient *Q;
Coefficient &Q;
};
/** Interpolator of a scalar coefficient multiplied by a vector field onto
@@ -2528,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
@@ -2545,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
@@ -2562,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
@@ -2578,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
+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
-801
View File
@@ -1,801 +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 MassIntegrator::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 : &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());
Vector coeff;
if (Q == nullptr)
{
coeff.SetSize(1);
coeff(0) = 1.0;
}
else if (ConstantCoefficient* cQ = dynamic_cast<ConstantCoefficient*>(Q))
{
coeff.SetSize(1);
coeff(0) = cQ->constant;
}
else
{
coeff.SetSize(nq * ne);
auto C = Reshape(coeff.Write(), nq, ne);
for (int e = 0; e < ne; ++e)
{
ElementTransformation& T = *fes.GetElementTransformation(e);
for (int q = 0; q < nq; ++q)
{
C(q,e) = Q->Eval(T, ir->IntPoint(q));
}
}
}
if (dim==1) { MFEM_ABORT("Not supported yet... stay tuned!"); }
if (dim==2)
{
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
auto w = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
auto C =
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,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);
const double coeff = const_c ? C(0,0) : C(q,e);
v(q,e) = w[q] * coeff * detJ;
}
});
}
if (dim==3)
{
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
auto W = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
auto C =
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,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);
const double coeff = const_c ? C(0,0) : C(q,e);
v(q,e) = W[q] * coeff * detJ;
}
});
}
}
#ifdef MFEM_USE_OCCA
// OCCA PA Mass Apply 2D kernel
static void OccaPAMassApply2D(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)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_op = OccaMemoryRead(op.GetMemory(), op.Size());
const occa::memory o_x = OccaMemoryRead(x.GetMemory(), x.Size());
occa::memory o_y = OccaMemoryReadWrite(y.GetMemory(), y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaMassApply2D_cpu;
if (OccaMassApply2D_cpu.find(id) == OccaMassApply2D_cpu.end())
{
const occa::kernel MassApply2D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply2D_CPU", props);
OccaMassApply2D_cpu.emplace(id, MassApply2D_CPU);
}
OccaMassApply2D_cpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
}
else
{
static occa_kernel_t OccaMassApply2D_gpu;
if (OccaMassApply2D_gpu.find(id) == OccaMassApply2D_gpu.end())
{
const occa::kernel MassApply2D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply2D_GPU", props);
OccaMassApply2D_gpu.emplace(id, MassApply2D_GPU);
}
OccaMassApply2D_gpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
}
}
// OCCA PA Mass Apply 3D kernel
static void OccaPAMassApply3D(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)
{
occa::properties props;
props["defines/D1D"] = D1D;
props["defines/Q1D"] = Q1D;
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
const occa::memory o_op = OccaMemoryRead(op.GetMemory(), op.Size());
const occa::memory o_x = OccaMemoryRead(x.GetMemory(), x.Size());
occa::memory o_y = OccaMemoryReadWrite(y.GetMemory(), y.Size());
const occa_id_t id = std::make_pair(D1D,Q1D);
if (!Device::Allows(Backend::OCCA_CUDA))
{
static occa_kernel_t OccaMassApply3D_cpu;
if (OccaMassApply3D_cpu.find(id) == OccaMassApply3D_cpu.end())
{
const occa::kernel MassApply3D_CPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply3D_CPU", props);
OccaMassApply3D_cpu.emplace(id, MassApply3D_CPU);
}
OccaMassApply3D_cpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
}
else
{
static occa_kernel_t OccaMassApply3D_gpu;
if (OccaMassApply3D_gpu.find(id) == OccaMassApply3D_gpu.end())
{
const occa::kernel MassApply3D_GPU =
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
"MassApply3D_GPU", props);
OccaMassApply3D_gpu.emplace(id, MassApply3D_GPU);
}
OccaMassApply3D_gpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
}
}
#endif // MFEM_USE_OCCA
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void PAMassApply2D(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;
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, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, 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 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,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,e) += q2d * sol_x[dx];
}
}
}
});
}
template<const int T_D1D = 0,
const int T_Q1D = 0,
const int T_NBZ = 0>
static void SmemPAMassApply2D(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 NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
MFEM_SHARED double BBt[MQ1*MD1];
double (*B)[MD1] = (double (*)[MD1]) BBt;
double (*Bt)[MQ1] = (double (*)[MQ1]) BBt;
MFEM_SHARED double sm0[NBZ][MDQ*MDQ];
MFEM_SHARED double sm1[NBZ][MDQ*MDQ];
double (*X)[MD1] = (double (*)[MD1]) (sm0 + tidz);
double (*DQ)[MQ1] = (double (*)[MQ1]) (sm1 + tidz);
double (*QQ)[MQ1] = (double (*)[MQ1]) (sm0 + tidz);
double (*QD)[MD1] = (double (*)[MD1]) (sm1 + tidz);
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
X[dy][dx] = x(dx,dy,e);
}
}
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double dq = 0.0;
for (int dx = 0; dx < D1D; ++dx)
{
dq += X[dy][dx] * B[qx][dx];
}
DQ[dy][qx] = dq;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double qq = 0.0;
for (int dy = 0; dy < D1D; ++dy)
{
qq += DQ[dy][qx] * B[qy][dy];
}
QQ[qy][qx] = qq * op(qx, qy, e);
}
}
MFEM_SYNC_THREAD;
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
Bt[d][q] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double dq = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
dq += QQ[qy][qx] * Bt[dx][qx];
}
QD[qy][dx] = dq;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double dd = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
dd += (QD[qy][dx] * Bt[dy][qy]);
}
y(dx, dy, e) += dd;
}
}
});
}
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void PAMassApply3D(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;
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, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, 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 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,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,e) += wz * sol_xy[dy][dx];
}
}
}
}
});
}
template<const int T_D1D = 0,
const int T_Q1D = 0>
static void SmemPAMassApply3D(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 M1Q = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int M1D = T_D1D ? T_D1D : MAX_D1D;
MFEM_VERIFY(D1D <= M1D, "");
MFEM_VERIFY(Q1D <= M1Q, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
MFEM_SHARED double sDQ[MQ1*MD1];
double (*B)[MD1] = (double (*)[MD1]) sDQ;
double (*Bt)[MQ1] = (double (*)[MQ1]) sDQ;
MFEM_SHARED double sm0[MDQ*MDQ*MDQ];
MFEM_SHARED double sm1[MDQ*MDQ*MDQ];
double (*X)[MD1][MD1] = (double (*)[MD1][MD1]) sm0;
double (*DDQ)[MD1][MQ1] = (double (*)[MD1][MQ1]) sm1;
double (*DQQ)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) sm0;
double (*QQQ)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) sm1;
double (*QQD)[MQ1][MD1] = (double (*)[MQ1][MD1]) sm0;
double (*QDD)[MD1][MD1] = (double (*)[MD1][MD1]) sm1;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
}
}
}
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0;
for (int dx = 0; dx < D1D; ++dx)
{
u += X[dz][dy][dx] * B[qx][dx];
}
DDQ[dz][dy][qx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0;
for (int dy = 0; dy < D1D; ++dy)
{
u += DDQ[dz][dy][qx] * B[qy][dy];
}
DQQ[dz][qy][qx] = u;
}
}
}
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;
for (int dz = 0; dz < D1D; ++dz)
{
u += DQQ[dz][qy][qx] * B[qz][dz];
}
QQQ[qz][qy][qx] = u * op(qx,qy,qz,e);
}
}
}
MFEM_SYNC_THREAD;
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
{
Bt[d][q] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += QQQ[qz][qy][qx] * Bt[dx][qx];
}
QQD[qz][qy][dx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += QQD[qz][qy][dx] * Bt[dy][qy];
}
QDD[qz][dy][dx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += QDD[qz][dy][dx] * Bt[dz][qz];
}
y(dx,dy,dz,e) += u;
}
}
}
});
}
static void PAMassApply(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)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
if (dim == 2)
{
OccaPAMassApply2D(D1D, Q1D, NE, B, Bt, op, x, y);
return;
}
if (dim == 3)
{
OccaPAMassApply3D(D1D, Q1D, NE, B, Bt, op, x, y);
return;
}
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
}
#endif // MFEM_USE_OCCA
if (dim == 2)
{
switch ((D1D << 4) | Q1D)
{
case 0x22: return SmemPAMassApply2D<2,2,16>(NE, B, Bt, op, x, y);
case 0x33: return SmemPAMassApply2D<3,3,16>(NE, B, Bt, op, x, y);
case 0x44: return SmemPAMassApply2D<4,4,8>(NE, B, Bt, op, x, y);
case 0x55: return SmemPAMassApply2D<5,5,8>(NE, B, Bt, op, x, y);
case 0x66: return SmemPAMassApply2D<6,6,4>(NE, B, Bt, op, x, y);
case 0x77: return SmemPAMassApply2D<7,7,4>(NE, B, Bt, op, x, y);
case 0x88: return SmemPAMassApply2D<8,8,2>(NE, B, Bt, op, x, y);
case 0x99: return SmemPAMassApply2D<9,9,2>(NE, B, Bt, op, x, y);
default: return PAMassApply2D(NE, B, Bt, op, x, y, D1D, Q1D);
}
}
else if (dim == 3)
{
switch ((D1D << 4) | Q1D)
{
case 0x23: return SmemPAMassApply3D<2,3>(NE, B, Bt, op, x, y);
case 0x34: return SmemPAMassApply3D<3,4>(NE, B, Bt, op, x, y);
case 0x45: return SmemPAMassApply3D<4,5>(NE, B, Bt, op, x, y);
case 0x56: return SmemPAMassApply3D<5,6>(NE, B, Bt, op, x, y);
case 0x67: return SmemPAMassApply3D<6,7>(NE, B, Bt, op, x, y);
case 0x78: return SmemPAMassApply3D<7,8>(NE, B, Bt, op, x, y);
case 0x89: return SmemPAMassApply3D<8,9>(NE, B, Bt, op, x, y);
default: return PAMassApply3D(NE, B, Bt, op, x, y, D1D, Q1D);
}
}
MFEM_ABORT("Unknown kernel.");
}
void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PAMassApply(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; }
+4 -17
View File
@@ -108,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;
}
@@ -162,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())
@@ -228,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);
@@ -277,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)
@@ -291,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)
@@ -589,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)
{
-4
View File
@@ -205,7 +205,6 @@ protected:
/// Output mesh format: see the #Format enumeration
int format;
bool compression;
/// Should the collection delete its mesh and fields
bool own_data;
@@ -347,9 +346,6 @@ public:
validation. */
virtual void SetFormat(int fmt);
/// Set the flag for use of gz compressed files
void SetCompression(bool comp);
/// Set the path where the DataCollection will be saved.
void SetPrefixPath(const std::string &prefix);
+370 -110
View File
@@ -203,22 +203,6 @@ void FiniteElement::CalcPhysDShape(ElementTransformation &Trans,
Mult(vshape, Trans.InverseJacobian(), dshape);
}
const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &,
DofToQuad::Mode) const
{
mfem_error("FiniteElement::GetDofToQuad(...) is not implemented for "
"this element!");
return *dof2quad_array[0]; // suppress a warning
}
FiniteElement::~FiniteElement()
{
for (int i = 0; i < dof2quad_array.Size(); i++)
{
delete dof2quad_array[i];
}
}
void ScalarFiniteElement::NodalLocalInterpolation (
ElementTransformation &Trans, DenseMatrix &I,
@@ -294,95 +278,6 @@ void ScalarFiniteElement::ScalarLocalInterpolation(
}
}
const DofToQuad &ScalarFiniteElement::GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
MFEM_VERIFY(mode == DofToQuad::FULL, "invalid mode requested");
for (int i = 0; i < dof2quad_array.Size(); i++)
{
const DofToQuad &d2q = *dof2quad_array[i];
if (d2q.IntRule == &ir && d2q.mode == mode) { return d2q; }
}
DofToQuad *d2q = new DofToQuad;
const int nqpt = ir.GetNPoints();
d2q->FE = this;
d2q->IntRule = &ir;
d2q->mode = mode;
d2q->ndof = Dof;
d2q->nqpt = nqpt;
d2q->B.SetSize(nqpt*Dof);
d2q->Bt.SetSize(Dof*nqpt);
d2q->G.SetSize(nqpt*Dim*Dof);
d2q->Gt.SetSize(Dof*nqpt*Dim);
#ifdef MFEM_THREAD_SAFE
Vector c_shape(Dof);
DenseMatrix vshape(Dof, Dim);
#endif
for (int i = 0; i < nqpt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
CalcShape(ip, c_shape);
for (int j = 0; j < Dof; j++)
{
d2q->B[i+nqpt*j] = d2q->Bt[j+Dof*i] = c_shape(j);
}
CalcDShape(ip, vshape);
for (int d = 0; d < Dim; d++)
{
for (int j = 0; j < Dof; j++)
{
d2q->G[i+nqpt*(d+Dim*j)] = d2q->Gt[j+Dof*(i+nqpt*d)] = vshape(j,d);
}
}
}
dof2quad_array.Append(d2q);
return *d2q;
}
// protected method
const DofToQuad &ScalarFiniteElement::GetTensorDofToQuad(
const TensorBasisElement &tb,
const IntegrationRule &ir, DofToQuad::Mode mode) const
{
MFEM_VERIFY(mode == DofToQuad::TENSOR, "invalid mode requested");
for (int i = 0; i < dof2quad_array.Size(); i++)
{
const DofToQuad &d2q = *dof2quad_array[i];
if (d2q.IntRule == &ir && d2q.mode == mode) { return d2q; }
}
DofToQuad *d2q = new DofToQuad;
const Poly_1D::Basis &basis_1d = tb.GetBasis1D();
const int ndof = Order + 1;
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/Dim) + 0.5);
d2q->FE = this;
d2q->IntRule = &ir;
d2q->mode = mode;
d2q->ndof = ndof;
d2q->nqpt = nqpt;
d2q->B.SetSize(nqpt*ndof);
d2q->Bt.SetSize(ndof*nqpt);
d2q->G.SetSize(nqpt*ndof);
d2q->Gt.SetSize(ndof*nqpt);
Vector val(ndof), grad(ndof);
for (int i = 0; i < nqpt; i++)
{
// The first 'nqpt' points in 'ir' have the same x-coordinates as those
// of the 1D rule.
basis_1d.Eval(ir.IntPoint(i).x, val, grad);
for (int j = 0; j < ndof; j++)
{
d2q->B[i+nqpt*j] = d2q->Bt[j+ndof*i] = val(j);
d2q->G[i+nqpt*j] = d2q->Gt[j+ndof*i] = grad(j);
}
}
dof2quad_array.Append(d2q);
return *d2q;
}
void NodalFiniteElement::ProjectCurl_2D(
const FiniteElement &fe, ElementTransformation &Trans,
@@ -9635,7 +9530,6 @@ void L2_TetrahedronElement::ProjectDelta(int vertex, Vector &dofs) const
const IntegrationPoint &ip = Nodes.IntPoint(i);
dofs[i] = pow(ip.y, Order);
}
break;
case 3:
for (int i = 0; i < Dof; i++)
{
@@ -11951,6 +11845,376 @@ void NURBS3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
}
}
/// SBP_SegmentElement is a segment element with nodes at Gauss Lobatto
/// points with ordering consistent with SBP_TriangleElement's edges.
//////////////////////////////////////////////////////////////////////////
/// Not currently implemented as collocated SBP type element
//////////////////////////////////////////////////////////////////////////
SBP_SegmentElement::SBP_SegmentElement(const int p)
: NodalTensorFiniteElement(1, p+1, BasisType::GaussLobatto, H1_DOF_MAP)
{
const double *cp = poly1d.ClosedPoints(p+1, b_type);
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p+2);
dshape_x.SetSize(p+2);
#endif
Nodes.IntPoint(0).x = cp[0];
Nodes.IntPoint(1).x = cp[p+1];
switch (p)
{
case 1:
Nodes.IntPoint(2).x = cp[1];
break;
case 2:
Nodes.IntPoint(2).x = cp[1];
Nodes.IntPoint(3).x = cp[2];
break;
case 3:
Nodes.IntPoint(2).x = cp[2];
Nodes.IntPoint(3).x = cp[1];
Nodes.IntPoint(4).x = cp[3];
break;
case 4:
Nodes.IntPoint(2).x = cp[2];
Nodes.IntPoint(3).x = cp[3];
Nodes.IntPoint(4).x = cp[1];
Nodes.IntPoint(5).x = cp[4];
break;
}
}
void SBP_SegmentElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = Order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p+2);
#endif
basis1d.Eval(ip.x, shape_x);
shape(0) = shape_x(0);
shape(1) = shape_x(p+1);
switch (p)
{
case 1:
shape(2) = shape_x(1);
break;
case 2:
shape(2) = shape_x(1);
shape(3) = shape_x(2);
break;
case 3:
shape(2) = shape_x(2);
shape(3) = shape_x(1);
shape(4) = shape_x(3);
break;
case 4:
shape(2) = shape_x(2);
shape(3) = shape_x(3);
shape(4) = shape_x(1);
shape(5) = shape_x(4);
break;
}
}
void SBP_SegmentElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = Order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p+2), dshape_x(p+2);
#endif
basis1d.Eval(ip.x, shape_x, dshape_x);
dshape(0,0) = dshape_x(0);
dshape(1,0) = dshape_x(p+1);
switch (p)
{
case 1:
dshape(2,0) = dshape_x(1);
break;
case 2:
dshape(2,0) = dshape_x(1);
dshape(3,0) = dshape_x(2);
break;
case 3:
dshape(2,0) = dshape_x(2);
dshape(3,0) = dshape_x(1);
dshape(4,0) = dshape_x(3);
break;
case 4:
dshape(2,0) = dshape_x(2);
dshape(3,0) = dshape_x(3);
dshape(4,0) = dshape_x(1);
dshape(5,0) = dshape_x(4);
break;
}
}
// Leftover function from H1_Segment element
// void SBP_SegmentElement::ProjectDelta(int vertex, Vector &dofs) const
// {
// const int p = Order;
// const double *cp = poly1d.ClosedPoints(p, b_type);
// switch (vertex)
// {
// case 0:
// dofs(0) = poly1d.CalcDelta(p, (1.0 - cp[0]));
// dofs(1) = poly1d.CalcDelta(p, (1.0 - cp[p]));
// for (int i = 1; i < p; i++)
// {
// dofs(i+1) = poly1d.CalcDelta(p, (1.0 - cp[i]));
// }
// break;
// case 1:
// dofs(0) = poly1d.CalcDelta(p, cp[0]);
// dofs(1) = poly1d.CalcDelta(p, cp[p]);
// for (int i = 1; i < p; i++)
// {
// dofs(i+1) = poly1d.CalcDelta(p, cp[i]);
// }
// break;
// }
// }
SBP_TriangleElement::SBP_TriangleElement(const int p, const int Do)
: NodalFiniteElement(2, Geometry::TRIANGLE, Do, p,
FunctionSpace::SBPk)
{
/// Header file including SBP Dx and Dy matrix data
#include "fe_sbp.hpp"
// Create Dx and Dy matrixes
Dx = new DenseMatrix(Dof);
Dy = new DenseMatrix(Dof);
// Populate the Dx and Dy matrices and create the element's Nodes
switch (p)
{
case 0:
*Dx=p0Dx;
*Dy=p0Dy;
Nodes.IntPoint(0).Set2w(0.0, 0.0, 0.16666666666666666);
Nodes.IntPoint(1).Set2w(1.0, 0.0, 0.16666666666666666);
Nodes.IntPoint(2).Set2w(0.0, 1.0, 0.16666666666666666);
break;
case 1:
*Dx=p1Dx;
*Dy=p1Dy;
Nodes.IntPoint(0).Set2w(0.0, 0.0, 0.024999999999999998);
Nodes.IntPoint(1).Set2w(1.0, 0.0, 0.024999999999999998);
Nodes.IntPoint(2).Set2w(0.0, 1.0, 0.024999999999999998);
Nodes.IntPoint(3).Set2w(0.5, 0.0, 0.06666666666666667);
Nodes.IntPoint(4).Set2w(0.5, 0.5, 0.06666666666666667);
Nodes.IntPoint(5).Set2w(0.0, 0.5, 0.06666666666666667);
Nodes.IntPoint(6).Set2w(0.3333333333333333, 0.3333333333333333, 0.22500000000000006);
break;
case 2:
*Dx=p2Dx;
*Dy=p2Dy;
// vertices
Nodes.IntPoint(0).Set2w(0.0, 0.0, 0.006261126504899741);
Nodes.IntPoint(1).Set2w(1.0, 0.0, 0.006261126504899741);
Nodes.IntPoint(2).Set2w(0.0, 1.0, 0.006261126504899741);
// edges
Nodes.IntPoint(3).Set2w(0.27639320225002106, 0.0, 0.026823800250389242);
Nodes.IntPoint(4).Set2w(0.7236067977499789, 0.0, 0.026823800250389242);
Nodes.IntPoint(5).Set2w(0.7236067977499789, 0.27639320225002106, 0.026823800250389242);
Nodes.IntPoint(6).Set2w(0.27639320225002106, 0.7236067977499789, 0.026823800250389242);
Nodes.IntPoint(7).Set2w(0.0, 0.7236067977499789, 0.026823800250389242);
Nodes.IntPoint(8).Set2w(0.0, 0.27639320225002106, 0.026823800250389242);
// interior
Nodes.IntPoint(9).Set2w(0.21285435711180825, 0.5742912857763836, 0.10675793966098839);
Nodes.IntPoint(10).Set2w(0.21285435711180825, 0.21285435711180825, 0.10675793966098839);
Nodes.IntPoint(11).Set2w(0.5742912857763836, 0.21285435711180825, 0.10675793966098839);
break;
case 3:
*Dx=p3Dx;
*Dy=p3Dy;
// vertices
Nodes.IntPoint(0).Set2w(0.0, 0.0, 0.0022825661430496253);
Nodes.IntPoint(1).Set2w(1.0, 0.0, 0.0022825661430496253);
Nodes.IntPoint(2).Set2w(0.0, 1.0, 0.0022825661430496253);
// edges
Nodes.IntPoint(3).Set2w(0.5, 0.0, 0.015504052643022513);
Nodes.IntPoint(4).Set2w(0.17267316464601146, 0.0, 0.011342592592592586);
Nodes.IntPoint(5).Set2w(0.8273268353539885, 0.0, 0.011342592592592586);
Nodes.IntPoint(6).Set2w(0.5, 0.5, 0.015504052643022513);
Nodes.IntPoint(7).Set2w(0.8273268353539885, 0.17267316464601146, 0.011342592592592586);
Nodes.IntPoint(8).Set2w(0.17267316464601146, 0.8273268353539885, 0.011342592592592586);
Nodes.IntPoint(9).Set2w(0.0, 0.5, 0.015504052643022513);
Nodes.IntPoint(10).Set2w(0.0, 0.8273268353539885, 0.011342592592592586);
Nodes.IntPoint(11).Set2w(0.0, 0.17267316464601146, 0.011342592592592586);
// interior
Nodes.IntPoint(12).Set2w(0.4243860251718814, 0.1512279496562372, 0.07467669469983994);
Nodes.IntPoint(13).Set2w(0.4243860251718814, 0.4243860251718814, 0.07467669469983994);
Nodes.IntPoint(14).Set2w(0.1512279496562372, 0.4243860251718814, 0.07467669469983994);
Nodes.IntPoint(15).Set2w(0.14200508409677795, 0.7159898318064442, 0.051518167995569394);
Nodes.IntPoint(16).Set2w(0.14200508409677795, 0.14200508409677795, 0.051518167995569394);
Nodes.IntPoint(17).Set2w(0.7159898318064442, 0.14200508409677795, 0.051518167995569394);
break;
case 4:
*Dx=p4Dx;
*Dy=p4Dy;
// vertices
Nodes.IntPoint(0).Set2w(0.000000000000000000,0.000000000000000000,0.001090393904993471);
Nodes.IntPoint(1).Set2w(1.000000000000000000,0.000000000000000000,0.001090393904993471);
Nodes.IntPoint(2).Set2w(0.000000000000000000,1.000000000000000000,0.001090393904993471);
// edges
Nodes.IntPoint(3).Set2w(0.357384241759677534,0.000000000000000000,0.006966942871463700);
Nodes.IntPoint(4).Set2w(0.642615758240322466,0.000000000000000000,0.006966942871463700);
Nodes.IntPoint(5).Set2w(0.117472338035267576,0.000000000000000000,0.005519747637357106);
Nodes.IntPoint(6).Set2w(0.882527661964732424,0.000000000000000000,0.005519747637357106);
Nodes.IntPoint(7).Set2w(0.642615758240322466,0.357384241759677534,0.006966942871463700);
Nodes.IntPoint(8).Set2w(0.357384241759677534,0.642615758240322466,0.006966942871463700);
Nodes.IntPoint(9).Set2w(0.882527661964732424,0.117472338035267576,0.005519747637357106);
Nodes.IntPoint(10).Set2w(0.117472338035267576,0.882527661964732424,0.005519747637357106);
Nodes.IntPoint(11).Set2w(0.000000000000000000,0.642615758240322466,0.006966942871463700);
Nodes.IntPoint(12).Set2w(0.000000000000000000,0.357384241759677534,0.006966942871463700);
Nodes.IntPoint(13).Set2w(0.000000000000000000,0.882527661964732424,0.005519747637357106);
Nodes.IntPoint(14).Set2w(0.000000000000000000,0.117472338035267576,0.005519747637357106);
// interior
Nodes.IntPoint(15).Set2w(0.103677508142805172,0.792644983714389628,0.028397190663911491);
Nodes.IntPoint(16).Set2w(0.103677508142805172,0.103677508142805172,0.028397190663911491);
Nodes.IntPoint(17).Set2w(0.792644983714389628,0.103677508142805172,0.028397190663911491);
Nodes.IntPoint(18).Set2w(0.265331380484209678,0.469337239031580644,0.039960048027851809);
Nodes.IntPoint(19).Set2w(0.265331380484209678,0.265331380484209678,0.039960048027851809);
Nodes.IntPoint(20).Set2w(0.469337239031580644,0.265331380484209678,0.039960048027851809);
Nodes.IntPoint(21).Set2w(0.587085567133367348,0.088273960601581103,0.036122826526134168);
Nodes.IntPoint(22).Set2w(0.324640472265051494,0.088273960601581103,0.036122826526134168);
Nodes.IntPoint(23).Set2w(0.324640472265051494,0.587085567133367348,0.036122826526134168);
Nodes.IntPoint(24).Set2w(0.587085567133367348,0.324640472265051494,0.036122826526134168);
Nodes.IntPoint(25).Set2w(0.088273960601581103,0.324640472265051494,0.036122826526134168);
Nodes.IntPoint(26).Set2w(0.088273960601581103,0.587085567133367348,0.036122826526134168);
break;
default:
mfem_error("SBP elements are currently only supported for 0 <= order <= 4");
break;
}
// populate unordered_map with mapping from IntPoint address to index
for (int i = 0; i < Dof; i++)
{
ipIdxMap[&(Nodes.IntPoint(i))] = i;
}
}
/// CalcShape outputs ndofx1 vector shape based on Kronecker \delta_{i, ip}
/// where ip is the integration point CalcShape is evaluated at.
void SBP_TriangleElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
int ipIdx;
try
{
ipIdx = ipIdxMap.at(&ip);
}
catch (const std::out_of_range& oor)
// error handling code to handle cases where the pointer to ip is not
// in the map. Problems arise in GridFunction::SaveVTK() -> GridFunction::GetValues()
// which calls CalcShape() with an `IntegrationPoint` defined by a refined
// geometry type. Since the IntegrationPoint is not in Nodes, its address is
// not in the ipIdxMap, and an out_of_range error is thrown. This code catches
// the error and uses float comparisons to determine the IntegrationPoint
// index.
{
double tol = 1e-12;
for (int i = 0; i < Dof; i++)
{
double delta_x = ip.x - Nodes.IntPoint(i).x;
double delta_y = ip.y - Nodes.IntPoint(i).y;
if (delta_x*delta_x + delta_y*delta_y < tol)
{
ipIdx = i;
break;
}
}
}
shape = 0.0;
shape(ipIdx) = 1.0;
}
/// CalcDShape outputs ndof x ndim DenseMatrix dshape, where the first column
/// is the ith row of Dx, and the second column is the ith row of Dy, where i
/// is the integration point CalcDShape is evaluated at. Since DenseMatrices
/// are stored a column major we should store the transpose so accessing a row
/// is faster, but this is not done here.
void SBP_TriangleElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
int ipIdx;
try
{
ipIdx = ipIdxMap.at(&ip);
}
catch (const std::out_of_range& oor)
// error handling code to handle cases where the pointer to ip is not
// in the map. Problems arise in GridFunction::SaveVTK() -> GridFunction::GetValues()
// which calls CalcShape() with an `IntegrationPoint` defined by a refined
// geometry type. Since the IntegrationPoint is not in Nodes, its address is
// not in the ipIdxMap, and an out_of_range error is thrown. This code catches
// the error and uses float comparisons to determine the IntegrationPoint
// index.
{
double tol = 1e-12;
for (int i = 0; i < Dof; i++)
{
double delta_x = ip.x - Nodes.IntPoint(i).x;
double delta_y = ip.y - Nodes.IntPoint(i).y;
if (delta_x*delta_x + delta_y*delta_y < tol)
{
ipIdx = i;
break;
}
}
}
dshape = 0.0;
Vector tempVec(Dof);
// when we switch to storing Dx and Dy transpose so that access to the row we want
// is faster Dx->GetRow() will be replaced with Dx->GetColumnReference()
Dx->GetRow(ipIdx, tempVec);
dshape.SetCol(0, tempVec);
Dy->GetRow(ipIdx, tempVec);
dshape.SetCol(1, tempVec);
}
SBP_TriangleElement::~SBP_TriangleElement()
{
delete Dx;
delete Dy;
}
// Global object definitions
@@ -11966,10 +12230,6 @@ Linear3DFiniteElement TetrahedronFE;
// Object declared in mesh/wedge.hpp.
// Defined here to ensure it is constructed after 'poly1d' and before
// 'Geometries'.
// TODO: define as thread_local to prevent race conditions in GLVis, because
// there is no "LinearWedgeFiniteElement" and WedgeFE is in turn used from two
// different threads for different things in GLVis. We also don't want to turn
// MFEM_THREAD_SAFE on globally. (See PR #731)
H1_WedgeElement WedgeFE(1);
// Object declared in geom.hpp.
+43 -125
View File
@@ -19,6 +19,7 @@
#include "geom.hpp"
#include <map>
#include <unordered_map>
namespace mfem
{
@@ -116,92 +117,7 @@ public:
}
};
/** @brief Structure representing the matrices/tensors needed to evaluate (in
reference space) the values, gradients, divergences, or curls of a
FiniteElement at a the quadrature points of a given IntegrationRule. */
/** Object of this type are typically created and owned by the respective
FiniteElement object. */
class DofToQuad
{
public:
/// The FiniteElement that created and owns this object.
/** This pointer is not owned. */
const class FiniteElement *FE;
/** @brief IntegrationRule that defines the quadrature points at which the
basis functions of the #FE are evaluated. */
/** This pointer is not owned. */
const IntegrationRule *IntRule;
/// Type of data stored in the arrays #B, #Bt, #G, and #Gt.
enum Mode
{
/** @brief Full multidimensional representation which does not use tensor
product structure. The ordering of the degrees of freedom is as
defined by #FE */
FULL,
/** @brief Tensor product representation using 1D matrices/tensors with
dimensions using 1D number of quadrature points and degrees of
freedom. */
/** When representing a vector-valued FiniteElement, two DofToQuad objects
are used to describe the "closed" and "open" 1D basis functions
(TODO). */
TENSOR
};
/// Describes the contents of the #B, #Bt, #G, and #Gt arrays, see #Mode.
Mode mode;
/** @brief Number of degrees of freedom = number of basis functions. When
#mode is TENSOR, this is the 1D number. */
int ndof;
/** @brief Number of quadrature points. When #mode is TENSOR, this is the 1D
number. */
int nqpt;
/// Basis functions evaluated at quadrature points.
/** The storage layout is column-major with dimensions:
- #nqpt x #ndof, for scalar elements, or
- #nqpt x dim x #ndof, for vector elements, (TODO)
where
- dim = dimension of the finite element reference space when #mode is
FULL, and dim = 1 when #mode is TENSOR. */
Array<double> B;
/// Transpose of #B.
/** The storage layout is column-major with dimensions:
- #ndof x #nqpt, for scalar elements, or
- #ndof x #nqpt x dim, for vector elements (TODO). */
Array<double> Bt;
/** @brief Gradients/divergences/curls of basis functions evaluated at
quadrature points. */
/** The storage layout is column-major with dimensions:
- #nqpt x dim x #ndof, for scalar elements, or
- #nqpt x #ndof, for H(div) vector elements (TODO), or
- #nqpt x cdim x #ndof, for H(curl) vector elements (TODO),
where
- dim = dimension of the finite element reference space when #mode is
FULL, and 1 when #mode is TENSOR,
- cdim = 1/1/3 in 1D/2D/3D, respectively, when #mode is FULL, and cdim =
1 when #mode is TENSOR. */
Array<double> G;
/// Transpose of #G.
/** The storage layout is column-major with dimensions:
- #ndof x #nqpt x dim, for scalar elements, or
- #ndof x #nqpt, for H(div) vector elements (TODO), or
- #ndof x #nqpt x cdim, for H(curl) vector elements (TODO). */
Array<double> Gt;
};
// Base and derived classes for finite elements
/// Describes the space on each element
class FunctionSpace
@@ -211,7 +127,8 @@ public:
{
Pk, ///< Polynomials of order k
Qk, ///< Tensor products of polynomials of order k
rQk ///< Refined tensor products of polynomials of order k
rQk,///< Refined tensor products of polynomials of order k
SBPk///< Summation-by-parts operator of order k with no explicit basis
};
};
@@ -221,10 +138,6 @@ class VectorCoefficient;
class MatrixCoefficient;
class KnotVector;
// Base and derived classes for finite elements
/// Abstract class for Finite Elements
class FiniteElement
{
@@ -241,10 +154,6 @@ protected:
#ifndef MFEM_THREAD_SAFE
mutable DenseMatrix vshape; // Dof x Dim
#endif
/// Container for all DofToQuad objects created by the FiniteElement.
/** Multiple DofToQuad objects may be needed when different quadrature rules
or different DofToQuad::Mode are used. */
mutable Array<DofToQuad*> dof2quad_array;
public:
/// Enumeration for RangeType and DerivRangeType
@@ -510,13 +419,7 @@ public:
ElementTransformation &Trans,
DenseMatrix &div) const;
/** Return a DofToQuad structure corresponding to the given IntegrationRule
using the given DofToQuad::Mode. */
/** See the documentation for DofToQuad for more details. */
virtual const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const;
virtual ~FiniteElement();
virtual ~FiniteElement () { }
static bool IsClosedType(int b_type)
{
@@ -563,10 +466,6 @@ protected:
return static_cast<const ScalarFiniteElement &>(fe);
}
const DofToQuad &GetTensorDofToQuad(const class TensorBasisElement &tb,
const IntegrationRule &ir,
DofToQuad::Mode mode) const;
public:
ScalarFiniteElement(int D, Geometry::Type G, int Do, int O,
int F = FunctionSpace::Pk)
@@ -597,9 +496,6 @@ public:
void ScalarLocalInterpolation(ElementTransformation &Trans,
DenseMatrix &I,
const ScalarFiniteElement &fine_fe) const;
virtual const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const;
};
class NodalFiniteElement : public ScalarFiniteElement
@@ -1856,14 +1752,6 @@ class NodalTensorFiniteElement : public NodalFiniteElement,
public:
NodalTensorFiniteElement(const int dims, const int p, const int btype,
const DofMapType dmtype);
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
return (mode == DofToQuad::FULL) ?
ScalarFiniteElement::GetDofToQuad(ir, mode) :
ScalarFiniteElement::GetTensorDofToQuad(*this, ir, mode);
}
};
class PositiveTensorFiniteElement : public PositiveFiniteElement,
@@ -1872,14 +1760,6 @@ class PositiveTensorFiniteElement : public PositiveFiniteElement,
public:
PositiveTensorFiniteElement(const int dims, const int p,
const DofMapType dmtype);
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
return (mode == DofToQuad::FULL) ?
ScalarFiniteElement::GetDofToQuad(ir, mode) :
ScalarFiniteElement::GetTensorDofToQuad(*this, ir, mode);
}
};
class H1_SegmentElement : public NodalTensorFiniteElement
@@ -2941,6 +2821,44 @@ public:
DenseMatrix &dshape) const;
};
/// Class for summation-by-parts operator on interval
class SBP_SegmentElement : public NodalTensorFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, dshape_x;
#endif
public:
SBP_SegmentElement(const int p);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
// ProjectDelta is leftover function from H1_SegmentElement
// virtual void ProjectDelta(int vertex, Vector &dofs) const;
};
/// Class for (diagonal-norm) summation-by-parts operator on triangles
class SBP_TriangleElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_l, dshape_x, dshape_y, dshape_l, u;
mutable Vector ddshape_x, ddshape_y, ddshape_l;
mutable DenseMatrix du, ddu;
#endif
DenseMatrix *Dx, *Dy;
std::unordered_map<const IntegrationPoint*, int> ipIdxMap;
public:
SBP_TriangleElement(const int p, const int Do);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
virtual ~SBP_TriangleElement();
};
} // namespace mfem
#endif
+159
View File
@@ -274,6 +274,10 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
fec = new NURBSFECollection();
}
}
else if (!strncmp(name, "SBP_", 4))
{
fec = new C_SBPCollection(atoi(name+8), atoi(name+4));
}
else
{
MFEM_ABORT("unknown FiniteElementCollection: " << name);
@@ -2542,4 +2546,159 @@ FiniteElementCollection *NURBSFECollection::GetTraceCollection() const
return NULL;
}
C_SBPCollection::C_SBPCollection(const int p, const int dim)
{
MFEM_VERIFY(p >= 0 && p <= 4, "C_SBPCollection requires 0 <= order <= 4.");
MFEM_VERIFY(dim == 2, "C_SBPCollection requires dim == 2.");
snprintf(c_SBPname, 32, "SBP_%dD_P%d", dim, p);
for (int g = 0; g < Geometry::NumGeom; g++)
{
C_SBPdof[g] = 0;
C_SBPElements[g] = NULL;
}
for (int i = 0; i < 2; i++)
{
SegDofOrd[i] = NULL;
}
C_SBPdof[Geometry::POINT] = 1;
C_SBPElements[Geometry::POINT] = new PointFiniteElement;
if (dim >= 1)
{
C_SBPdof[Geometry::SEGMENT] = p;
C_SBPElements[Geometry::SEGMENT] = new SBP_SegmentElement(p);
int nodeOrder0[] = {};
int nodeOrder1[1] = {0};
int nodeOrder2[2] = {0, 1};
int nodeOrder3[3] = {0, 1, 2};
int nodeOrder4[4] = {0, 1, 2, 3};
int revNodeOrder0[] = {};
int revNodeOrder1[1] = {0};
int revNodeOrder2[2] = {1, 0};
int revNodeOrder3[3] = {0, 2, 1};
int revNodeOrder4[4] = {1, 0, 3, 2};
switch (p)
{
case 0:
SegDofOrd[0] = new int[p];
SegDofOrd[1] = new int[p];
for (int i = 0; i < p; i++)
{
SegDofOrd[0][i] = nodeOrder0[i];
SegDofOrd[1][i] = revNodeOrder0[i];
}
break;
case 1:
SegDofOrd[0] = new int[p];
SegDofOrd[1] = new int[p];
for (int i = 0; i < p; i++)
{
SegDofOrd[0][i] = nodeOrder1[i];
SegDofOrd[1][i] = revNodeOrder1[i];
}
break;
case 2:
SegDofOrd[0] = new int[p];
SegDofOrd[1] = new int[p];
for (int i = 0; i < p; i++)
{
SegDofOrd[0][i] = nodeOrder2[i];
SegDofOrd[1][i] = revNodeOrder2[i];
}
break;
case 3:
SegDofOrd[0] = new int[p];
SegDofOrd[1] = new int[p];
for (int i = 0; i < p; i++)
{
SegDofOrd[0][i] = nodeOrder3[i];
SegDofOrd[1][i] = revNodeOrder3[i];
}
break;
case 4:
SegDofOrd[0] = new int[p];
SegDofOrd[1] = new int[p];
for (int i = 0; i < p; i++)
{
SegDofOrd[0][i] = nodeOrder4[i];
SegDofOrd[1][i] = revNodeOrder4[i];
}
break;
default:
mfem_error("SBP elements are currently only supported for 0 <= order <= 4");
break;
}
}
if (dim >= 2)
{
switch (p)
{
case 0:
C_SBPdof[Geometry::TRIANGLE] = 3 - 3 - 3*p;
break;
case 1:
C_SBPdof[Geometry::TRIANGLE] = 7 - 3 - 3*p;
break;
case 2:
C_SBPdof[Geometry::TRIANGLE] = 12 - 3 - 3*p;
break;
case 3:
C_SBPdof[Geometry::TRIANGLE] = 18 - 3 - 3*p;
break;
case 4:
C_SBPdof[Geometry::TRIANGLE] = 27 - 3 - 3*p;
break;
default:
mfem_error("SBP elements are currently only supported for 0 <= order <= 4");
break;
}
const int &TriDof = C_SBPdof[Geometry::TRIANGLE] + 3*C_SBPdof[Geometry::POINT] + 3*C_SBPdof[Geometry::SEGMENT];
C_SBPElements[Geometry::TRIANGLE] = new SBP_TriangleElement(p, TriDof);
}
}
const FiniteElement *C_SBPCollection::FiniteElementForGeometry(
Geometry::Type GeomType) const
{
if (GeomType == Geometry::TRIANGLE || GeomType == Geometry::SEGMENT || GeomType == Geometry::POINT)
{
}
else
{
MFEM_ABORT("Unsupported geometry type " << GeomType);
}
return C_SBPElements[GeomType];
}
const int *C_SBPCollection::DofOrderForOrientation(Geometry::Type GeomType,
int Or) const
{
if (GeomType == Geometry::SEGMENT)
{
return (Or > 0) ? SegDofOrd[0] : SegDofOrd[1];
}
return NULL;
}
C_SBPCollection::~C_SBPCollection()
{
delete [] SegDofOrd[0];
for (int g = 0; g < Geometry::NumGeom; g++)
{
delete C_SBPElements[g];
}
}
}
+24
View File
@@ -884,6 +884,30 @@ public:
virtual ~Local_FECollection() { delete Local_Element; }
};
/// Arbitrary order H1-conforming (continuous) Summation By Parts
/// opperators.
class C_SBPCollection : public FiniteElementCollection
{
protected:
char c_SBPname[32];
FiniteElement *C_SBPElements[Geometry::NumGeom];
int C_SBPdof[Geometry::NumGeom];
int *SegDofOrd[2];
public:
explicit C_SBPCollection(const int p, const int dim = 2);
virtual const FiniteElement *FiniteElementForGeometry(
Geometry::Type GeomType) const;
virtual int DofForGeometry(Geometry::Type GeomType) const
{ return C_SBPdof[GeomType]; }
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const;
virtual const char *Name() const { return c_SBPname; }
virtual ~C_SBPCollection();
};
}
#endif
+141
View File
@@ -0,0 +1,141 @@
/// SBP Triangle Dx and Dy matrix data arrays, stored column major format
const double p0Dx[9] = {-0.9999999999999984,-1.000000000000001,-0.9999999999999988,
1.000000000000001,0.9999999999999974,0.9999999999999994,
1.3322676295501878e-15,-1.9984014443252818e-15,9.992007221626409e-16};
const double p0Dy[9] = {-0.9999999999999974,-0.9999999999999994,-1.0000000000000009,
1.9984014443252818e-15,-9.992007221626409e-16,-1.7486012637846216e-15,
1.0000000000000009,0.9999999999999991,0.9999999999999984};
const double p1Dx[49] = {-3.333333333333333, 0.21647921352995003, 0.10823960676497299, -0.8824293926518367, 0.7863909022744312, -0.051249687578105226, -0.362809297675581,
-0.21647921352995003, 3.333333333333333, -0.10823960676497392, 0.8824293926518368, 0.05124968757810527, -0.7863909022744306, 0.3628092976755813,
-0.10823960676497299, 0.10823960676497392, 0.0, 7.406681257404114e-16, -0.8311797050737315, 0.8311797050737307, -1.9769834191462048e-16,
2.3531450470715645, -2.353145047071565, -1.9751150019744302e-15, 0.0, -0.2351412146963257, 0.23514121469632399, 3.1540140465066577e-17,
-2.09704240606515, -0.13666583354161405, 2.2164792135299507, 0.2351412146963257, 5.0, 0.47028242939265, 1.2743814046488378,
0.13666583354161393, 2.0970424060651482, -2.2164792135299485, -0.23514121469632399, -0.47028242939265, -5.0, -1.2743814046488378,
3.2652836790802304, -3.2652836790802326, 1.7792850772315852e-15, -1.0644797406959973e-16, -4.301037240689829, 4.301037240689829, 0.0};
const double p1Dy[49] = {-3.333333333333333, 0.10823960676497733, 0.21647921352994814, -0.0512496875781027, 0.7863909022744318, -0.8824293926518388, -0.3628092976755811,
-0.10823960676497733, 0.0, 0.10823960676497837, 0.8311797050737281, -0.8311797050737287, 9.745633233426465e-17, -3.4329014409980615e-16,
-0.21647921352994814, -0.10823960676497837, 3.333333333333333, -0.7863909022744319, 0.051249687578102346, 0.8824293926518396, 0.3628092976755813,
0.1366658335416072, -2.216479213529942, 2.0970424060651514, -5.0, -0.4702824293926414, -0.23514121469633495, -1.274381404648838,
-2.0970424060651514, 2.216479213529943, -0.13666583354160625, 0.4702824293926414, 5.0, 0.235141214696336, 1.2743814046488384,
2.3531450470715702, -2.5988355289137243e-16, -2.3531450470715725, 0.23514121469633495, -0.235141214696336, 0.0, 3.7848168558079887e-16,
3.2652836790802313, 3.0896112968982564e-15, -3.2652836790802326, 4.30103724068983, -4.301037240689832, -1.2773756888351966e-15, 0.0};
const double p2Dx[144] = {-6.654819485608504, -0.2644800033235228, -0.13224000166175998, -1.4588736121976598, 0.16568196946098376, -0.18364010800322192, -0.49629408766969996, 0.18676496181247967, -0.32850861828832895, 0.17877251381228318, -0.18297783134208934, 0.16855922332582518,
0.2644800033235228, 6.654819485608504, 0.13224000166176486, -0.1656819694609847, 1.4588736121976598, 0.3285086182883251, -0.18676496181248323, 0.4962940876696997, 0.18364010800322153, -0.1787725138122836, -0.16855922332582482, 0.18297783134208834,
0.13224000166175998, -0.13224000166176486, 0.0, -0.31265397966647923, 0.3126539796664765, -0.021082992351498762, -1.1303649939093317, 1.1303649939093305, 0.0210829923514981, -3.1850260779507576e-16, 0.010213290486458549, -0.010213290486458991,
6.25007885298757, 0.7098115731146227, 1.3394662911697857, 0.0, -1.0987526276656132, 0.8391459038761706, -0.7493297423228898, -1.0608454694676046, 0.7982718911706455, 0.47711018331982885, -0.20709875867432972, -0.4372738402113571,
-0.7098115731146187, -6.25007885298757, -1.339466291169774, 1.0987526276656132, 0.0, -0.7982718911706481, 1.0608454694676077, 0.7493297423228901, -0.8391459038761684, -0.47711018331982796, 0.4372738402113582, 0.20709875867432867,
0.7867474920341369, -1.407390435347625, 0.09032335875573284, -0.8391459038761706, 0.7982718911706481, 7.766734444360106, 0.549376313832808, -1.8999913733437686, 1.4986594846457786, 0.7173852412251129, -0.7042577654543215, 1.9425023749691277,
2.126213783203917, 0.8001349318703544, 4.842688417639932, 0.7493297423228898, -1.0608454694676077, -0.549376313832808, 7.766734444360106, 1.5965437823412956, -1.8999913733437699, 1.7354036162948008, -0.2271475821344933, 0.2801114010137556,
-0.8001349318703392, -2.1262137832039163, -4.842688417639927, 1.0608454694676046, -0.7493297423228901, 1.8999913733437686, -1.5965437823412956, -7.766734444360106, 0.5493763138328077, -1.7354036162947999, -0.28011140101375603, 0.22714758213449346,
1.4073904353476412, -0.7867474920341352, -0.09032335875573, -0.7982718911706455, 0.8391459038761684, -1.4986594846457786, 1.8999913733437699, -0.5493763138328077, -7.766734444360106, -0.7173852412251128, -1.9425023749691277, 0.7042577654543228,
-3.0482350464695736, 3.0482350464695807, 5.4307610872012924e-15, -1.8988845609884182, 1.8988845609884146, -2.8551722567827746, -6.906855584460779, 6.906855584460776, 2.855172256782774, 0.0, 1.0883892687387082, -1.088389268738702,
3.11993956078517, 2.8740890922806273, -0.1741459541889496, 0.8242469961764219, -1.7403370817278376, 2.8029252875555524, 0.904040726567137, 1.114835175054942, 7.73110258063719, -1.0883892687387082, 0.0, -2.176778537477416,
-2.8740890922806335, -3.119939560785153, 0.17414595418895715, 1.7403370817278334, -0.8242469961764177, -7.73110258063719, -1.1148351750549401, -0.9040407265671375, -2.8029252875555577, 1.088389268738702, 2.176778537477416, 0.0};
const double p2Dy[144] = {-6.654819485608504, -0.13224000166176303, -0.26448000332352145, -0.3285086182883249, 0.1867649618124839, -0.49629408766970096, -0.18364010800322322, 0.16568196946098196, -1.4588736121976569, 0.16855922332582438, -0.1829778313420883, 0.17877251381228437,
0.13224000166176303, 0.0, -0.13224000166176358, 0.021082992351497333, 1.1303649939093325, -1.13036499390933, -0.0210829923514987, 0.31265397966647596, -0.3126539796664774, -0.010213290486459194, 0.010213290486458717, 1.7550143694830702e-16,
0.26448000332352145, 0.13224000166176358, 6.654819485608504, 0.18364010800321912, 0.49629408766969646, -0.18676496181248112, 0.32850861828832506, 1.4588736121976555, -0.16568196946098368, 0.18297783134208803, -0.16855922332582496, -0.17877251381228357,
1.4073904353476239, -0.09032335875572672, -0.7867474920341248, -7.766734444360106, -0.5493763138328086, 1.8999913733437714, -1.498659484645771, 0.8391459038761694, -0.7982718911706419, 0.7042577654543205, -1.9425023749691286, -0.7173852412251152,
-0.8001349318703573, -4.842688417639936, -2.1262137832039025, 0.5493763138328086, -7.766734444360106, -1.5965437823412931, 1.8999913733437763, -0.7493297423228881, 1.060845469467605, 0.22714758213449282, -0.28011140101375637, -1.7354036162948017,
2.1262137832039216, 4.842688417639926, 0.8001349318703455, -1.8999913733437714, 1.5965437823412931, 7.766734444360106, -0.5493763138328039, -1.0608454694675977, 0.7493297423228871, 0.280111401013755, -0.22714758213449193, 1.7354036162947997,
0.7867474920341424, 0.09032335875573258, -1.4073904353476248, 1.498659484645771, -1.8999913733437763, 0.5493763138328039, 7.766734444360106, 0.7982718911706495, -0.8391459038761694, 1.942502374969129, -0.7042577654543208, 0.7173852412251135,
-0.709811573114611, -1.3394662911697717, -6.250078852987552, -0.8391459038761694, 0.7493297423228881, 1.0608454694675977, -0.7982718911706495, 0.0, 1.0987526276656179, 0.20709875867432867, 0.43727384021135907, -0.47711018331982974,
6.250078852987558, 1.339466291169778, 0.7098115731146183, 0.7982718911706419, -1.060845469467605, -0.7493297423228871, 0.8391459038761694, -1.0987526276656179, 0.0, -0.4372738402113571, -0.20709875867432725, 0.4771101833198306,
-2.8740890922806197, 0.17414595418896062, -3.1199395607851477, -2.8029252875555484, -0.904040726567135, -1.114835175054938, -7.731102580637195, -0.8242469961764177, 1.7403370817278334, 0.0, 2.1767785374774147, 1.0883892687387102,
3.119939560785152, -0.17414595418895248, 2.8740890922806295, 7.731102580637194, 1.1148351750549435, 0.9040407265671315, 2.8029252875555497, -1.7403370817278412, 0.8242469961764122, -2.1767785374774147, 0.0, -1.0883892687387073,
-3.048235046469594, -2.9924601909068347e-15, 3.04823504646958, 2.8551722567827835, 6.906855584460783, -6.906855584460775, -2.855172256782777, 1.8988845609884217, -1.8988845609884253, -1.0883892687387102, 1.0883892687387073, 0.0};
const double p3Dx[324] = {-10.952585131486583, -0.1691685246371182, -0.08458426231856121, 0.2782043329268357, -2.305479077267717, -0.07465619884488185, 0.08251879096720546, 0.22849304912079002, 0.5302407771816927, 0.28327860677318784, -0.11951297527437506, -0.5003055437372067, 0.12448476066856992, -0.09239160861978224, 0.10362923976480765, -0.16947318256573993, -0.12955658105892584, -0.0720914264099959,
0.1691685246371182, 10.952585131486583, 0.08458426231855752, -0.2782043329268405, 0.0746561988448885, 2.3054790772677194, -0.2832786067731876, 0.5003055437372037, 0.11951297527437463, -0.08251879096721035, -0.5302407771816938, -0.2284930491207896, -0.12448476066856932, -0.10362923976480676, 0.09239160861978266, 0.16947318256574107, 0.07209142640999573, 0.12955658105892595,
0.08458426231856121, -0.08458426231855752, 0.0, 1.1683416656308464e-15, 0.3017477280608998, -0.3017477280609013, -0.005074273846349578, 0.04485677642949094, -1.8051735335305155, 0.0050742738463512614, 1.805173533530519, -0.04485677642949147, 4.205982075638532e-16, 0.020855520903763268, -0.020855520903761818, 7.395777588076851e-16, -0.0973817561557449, 0.09738175615574554,
-1.8896690623176595, 1.8896690623176922, -7.935818527815355e-15, 0.0, 2.0050512471695137, -2.0050512471695145, -0.8926105190374908, 1.6696076717446249, 0.020736328416563414, 0.8926105190375015, -0.020736328416544252, -1.669607671744625, -4.429107086712402e-16, 0.4633133883374836, -0.46331338833748054, 1.783666940029547e-15, 0.7834980551346994, -0.783498055134701,
11.456452196936608, -0.3709837064689789, -1.4994533917700335, -1.4668732071255282, 0.0, 0.43808614602067847, -0.03454227374421423, -0.36820417663301963, 0.6765153661826107, -1.2393619133905875, 1.2485385227460366, 1.2932580655616601, -0.24307113935772914, 0.052425055137118155, 0.44269118420721065, -0.48976278487794334, -0.3768721550070895, 0.17743932374896715,
0.3709837064689459, -11.45645219693662, 1.4994533917700408, 1.4668732071255288, -0.43808614602067847, 0.0, 1.2393619133905824, -1.2932580655616563, -1.2485385227460208, 0.034542273744204106, -0.6765153661826184, 0.36820417663302096, 0.24307113935772948, -0.44269118420720915, -0.052425055137118384, 0.4897627848779442, -0.1774393237489731, 0.3768721550070885,
-0.5604988416610844, 1.924135449667926, 0.03446638735025072, 0.8926105190374908, 0.04721541624352583, -1.6940688111740314, 11.466536000042892, -1.1244968786658345, 0.8805543685036823, 1.785221038074984, -3.363676482918665, 0.026479087826965113, -0.42363907421033664, 2.460011157605557, -0.8869524625478119, 1.3603474091530658, 0.13068371284607608, 0.5768493540183615,
-1.135434157870106, -2.486132536271146, -0.22290356912810128, -1.2214664156593342, 0.36820417663301963, 1.2932580655616563, 0.8226694181207354, 12.000000000000005, -0.21904307301033732, 0.01937180635024931, 1.6167426993790468, -1.3530307323652335, -0.6132774672199158, 0.43432201350751304, -0.11093602189671813, -0.6100947628964166, 0.7813784140227625, 2.7998077074825853,
-2.634887549640154, -0.5938872755970562, 8.97031966066547, -0.01517046739396432, -0.6765153661826107, 1.2485385227460208, -0.6442037890047951, 0.21904307301033732, 12.000000000000005, -2.460828329049918, 2.586516131123318, 1.6167426993790477, -0.0585109667596005, 0.19125087414978273, -0.17058628301270465, 2.4229355524754963, 0.29161562914482175, -0.43265543914744503,
-1.9241354496679275, 0.5604988416611175, -0.034466387350262154, -0.8926105190375015, 1.6940688111740383, -0.047215416243512, -1.785221038074984, -0.026479087826961595, 3.3636764829186583, -11.466536000042892, -0.8805543685036843, 1.1244968786658245, 0.42363907421033115, 0.8869524625478146, -2.4600111576055554, -1.3603474091530607, -0.5768493540183651, -0.13068371284607755,
0.5938872755970585, 2.634887549640159, -8.97031966066549, 0.0151704673939503, -1.2485385227460366, 0.6765153661826184, 2.460828329049923, -1.6167426993790468, -2.586516131123318, 0.6442037890047966, -12.000000000000005, -0.21904307301033812, 0.05851096675959981, 0.17058628301270434, -0.19125087414978267, -2.422935552475496, 0.43265543914744814, -0.29161562914482114,
2.486132536271161, 1.1354341578701037, 0.22290356912810394, 1.2214664156593344, -1.2932580655616601, -0.36820417663302096, -0.019371806350251885, 1.3530307323652335, -1.6167426993790477, -0.822669418120728, 0.21904307301033812, -12.000000000000005, 0.613277467219916, 0.11093602189671813, -0.43432201350751237, 0.6100947628964153, -2.7998077074825867, -0.7813784140227636,
-4.072657651361357, 4.072657651361338, -1.3760339008438063e-14, 2.133320141015979e-15, 1.6003174861462979, -1.6003174861463, 2.0404965421712293, 4.037660156795314, 0.38522106525739636, -2.0404965421712027, -0.38522106525739186, -4.037660156795315, 0.0, -1.4579314260766258, 1.4579314260766234, 2.0161871961592337e-15, 3.273923136675281, -3.2739231366752755,
3.0226944225627097, 3.3903460469074083, -0.6823116044539523, -2.2315915230588144, -0.34515299788282433, 2.914564209151067, -11.84886986676902, -2.859463754812241, -1.2591462686659354, -4.272088065230016, -1.1230959476442317, -0.7303740631402172, 1.4579314260766258, 0.0, 2.9158628521532504, 1.6703256795818853, -0.3883097004508776, -1.603597457093401,
-3.390346046907437, -3.022694422562723, 0.6823116044539049, 2.2315915230587997, -2.914564209151077, 0.34515299788282583, 4.272088065230004, 0.7303740631402172, 1.1230959476442337, 11.84886986676901, 1.2591462686659352, 2.8594637548122366, -1.4579314260766234, -2.9158628521532504, 0.0, -1.6703256795818826, 1.6035974570934062, 0.3883097004508795,
3.825058001824472, -3.8250580018244977, -1.669248067139698e-14, -5.926918282616925e-15, 2.2245074239726854, -2.2245074239726894, -4.520276599333233, 2.771056460992968, -11.004997298094699, 4.520276599333215, 11.004997298094697, -2.7710564609929618, -1.3909328887646931e-15, -1.1523289737701476, 1.1523289737701459, 0.0, -0.9410755641839665, 0.9410755641839751,
2.9241289363066816, -1.6271240279890726, 2.1979339738354153, -2.6034731278339045, 1.7117570640055604, 0.8059311674378296, -0.4342468145397358, -3.5490285023571575, -1.3245210783844836, 1.9168034714993238, -1.9651252935551593, 12.716754362100263, -2.258623293890646, 0.26788818737287284, -1.1062943201205055, 0.9410755641839665, 0.0, 1.8821511283679513,
1.6271240279890764, -2.9241289363066834, -2.1979339738354295, 2.60347312783391, -0.8059311674378026, -1.711757064005556, -1.9168034714993114, -12.716754362100257, 1.9651252935551453, 0.4342468145397406, 1.324521078384481, 3.5490285023571624, 2.258623293890642, 1.106294320120502, -0.2678881873728741, -0.9410755641839751, -1.8821511283679513, 0.0};
const double p3Dy[324] = {-10.952585131486583, -0.08458426231856092, -0.16916852463712426, 0.28327860677318756, -0.5003055437372038, -0.11951297527437327, 0.0825187909672079, 0.5302407771816892, 0.22849304912079044, 0.2782043329268377, -0.07465619884488649, -2.305479077267721, 0.1036292397648074, -0.09239160861978217, 0.12448476066856967, -0.07209142640999439, -0.12955658105892628, -0.16947318256574065,
0.08458426231856092, 0.0, -0.08458426231857115, 0.00507427384635043, -0.044856776429489494, 1.8051735335305168, -0.005074273846348987, -1.805173533530519, 0.04485677642949065, -2.536797215167323e-16, -0.30174772806090094, 0.3017477280609018, -0.020855520903761887, 0.020855520903762893, 3.895409306444457e-16, 0.09738175615574617, -0.09738175615574511, -2.518495690291951e-17,
0.16916852463712426, 0.08458426231857115, 10.952585131486583, -0.08251879096720664, -0.2284930491207953, -0.5302407771816934, -0.2832786067731896, 0.11951297527436967, 0.5003055437372073, -0.2782043329268386, 2.305479077267721, 0.07465619884488589, 0.09239160861978252, -0.10362923976480695, -0.12448476066856902, 0.12955658105892542, 0.07209142640999575, 0.1694731825657405,
-1.9241354496679255, -0.034466387350256505, 0.5604988416610924, -11.466536000042892, 1.124496878665824, -0.8805543685036857, -1.7852210380749862, 3.3636764829186614, -0.026479087826939977, -0.8926105190374863, -0.04721541624350879, 1.6940688111740283, -2.4600111576055577, 0.8869524625478143, 0.42363907421033403, -0.13068371284608143, -0.5768493540183609, -1.3603474091530663,
2.4861325362711466, 0.2229035691280941, 1.1354341578701321, -0.8226694181207276, -12.000000000000005, 0.21904307301034107, -0.01937180635025535, -1.6167426993790432, 1.3530307323652313, 1.2214664156593333, -0.3682041766330094, -1.2932580655616668, -0.4343220135075129, 0.11093602189671664, 0.6132774672199158, -0.7813784140227632, -2.799807707482588, 0.6100947628964168,
0.5938872755970495, -8.97031966066548, 2.634887549640157, 0.6442037890047976, -0.21904307301034107, -12.000000000000005, 2.4608283290499164, -2.586516131123313, -1.6167426993790395, 0.015170467393960869, 0.6765153661826211, -1.2485385227460257, -0.19125087414978525, 0.17058628301270676, 0.058510966759598895, -0.2916156291448204, 0.43265543914744725, -2.4229355524754985,
-0.560498841661101, 0.03446638735024671, 1.9241354496679397, 1.7852210380749862, 0.026479087826969852, -3.363676482918656, 11.466536000042892, 0.8805543685036855, -1.1244968786658387, 0.8926105190374908, -1.6940688111740407, 0.047215416243521366, -0.8869524625478177, 2.460011157605558, -0.4236390742103285, 0.5768493540183687, 0.13068371284607674, 1.360347409153062,
-2.634887549640136, 8.97031966066549, -0.5938872755970316, -2.4608283290499204, 1.6167426993790432, 2.586516131123313, -0.6442037890047975, 12.000000000000005, 0.2190430730103402, -0.015170467393955334, 1.2485385227460235, -0.6765153661826085, -0.17058628301270545, 0.19125087414978356, -0.05851096675960009, -0.432655439147446, 0.2916156291448159, 2.4229355524755003,
-1.135434157870108, -0.22290356912809986, -2.4861325362711635, 0.019371806350233497, -1.3530307323652313, 1.6167426993790395, 0.8226694181207385, -0.2190430730103402, 12.000000000000005, -1.221466415659336, 1.2932580655616515, 0.3682041766330148, -0.11093602189671982, 0.43432201350751376, -0.6132774672199166, 2.7998077074825862, 0.781378414022764, -0.6100947628964163,
-1.889669062317673, 1.72308862498413e-15, 1.8896690623176793, 0.8926105190374863, -1.6696076717446235, -0.0207363284165587, -0.8926105190374908, 0.020736328416551132, 1.669607671744627, 0.0, -2.005051247169507, 2.005051247169518, -0.46331338833748403, 0.4633133883374836, -7.923728999874815e-16, -0.7834980551347012, 0.7834980551346985, 2.487535508292328e-15,
0.37098370646896894, 1.499453391770039, -11.456452196936628, 0.03454227374420177, 0.3682041766330094, -0.6765153661826211, 1.2393619133905893, -1.2485385227460235, -1.2932580655616515, 1.4668732071255233, 0.0, -0.43808614602068285, -0.0524250551371181, -0.44269118420721065, 0.24307113935773086, 0.37687215500708976, -0.17743932374896845, 0.48976278487794267,
11.456452196936628, -1.4994533917700434, -0.37098370646896595, -1.2393619133905802, 1.2932580655616668, 1.2485385227460257, -0.03454227374421096, 0.6765153661826085, -0.3682041766330148, -1.4668732071255315, 0.43808614602068285, 0.0, 0.44269118420721193, 0.05242505513711802, -0.24307113935772984, 0.17743932374897087, -0.37687215500708626, -0.48976278487794556,
-3.3903460469074287, 0.682311604453907, -3.022694422562719, 11.848869866769022, 2.85946375481224, 1.2591462686659523, 4.272088065230031, 1.123095947644239, 0.7303740631402283, 2.2315915230588166, 0.34515299788282394, -2.9145642091510853, 0.0, -2.9158628521532504, -1.4579314260766258, 0.3883097004508778, 1.6035974570934002, -1.6703256795818757,
3.0226944225627075, -0.68231160445394, 3.3903460469074145, -4.272088065230015, -0.7303740631402075, -1.1230959476442475, -11.848869866769023, -1.259146268665941, -2.859463754812246, -2.2315915230588144, 2.914564209151077, -0.3451529978828235, 2.9158628521532504, 0.0, 1.4579314260766267, -1.6035974570933964, -0.3883097004508765, 1.6703256795818802,
-4.072657651361349, -1.2744265588712169e-14, 4.072657651361328, -2.040496542171217, -4.037660156795314, -0.3852210652573858, 2.04049654217119, 0.3852210652573937, 4.037660156795319, 3.816536908330339e-15, -1.6003174861463092, 1.6003174861463025, 1.4579314260766258, -1.4579314260766267, 0.0, -3.2739231366752795, 3.273923136675281, -1.5485517097979382e-15,
1.6271240279890422, -2.1979339738354438, -2.924128936306672, 0.43424681453975356, 3.5490285023571606, 1.3245210783844776, -1.9168034714993356, 1.9651252935551495, -12.71675436210026, 2.6034731278339103, -1.7117570640055617, -0.8059311674378195, -0.26788818737287295, 1.1062943201204989, 2.2586232938906448, 0.0, -1.8821511283679437, -0.941075564183975,
2.9241289363066914, 2.1979339738354198, -1.627124027989073, 1.9168034714993096, 12.71675436210027, -1.9651252935551553, -0.434246814539738, -1.324521078384457, -3.549028502357164, -2.6034731278339014, 0.8059311674378086, 1.7117570640055457, -1.1062943201205013, 0.26788818737287207, -2.258623293890646, 1.8821511283679437, 0.0, 0.9410755641839769,
3.825058001824488, 5.68431650770163e-16, -3.825058001824485, 4.520276599333235, -2.7710564609929693, 11.00499729809471, -4.5202765993332195, -11.004997298094716, 2.7710564609929667, -8.265791864994906e-15, -2.2245074239726823, 2.2245074239726956, 1.152328973770141, -1.152328973770144, 1.0683192052870465e-15, 0.941075564183975, -0.9410755641839769, 0.0};
const double p4Dx[729] = {-15.28499617463146, 0.1965429454017319, 0.09827147270087869, 0.4207579292099769, -0.03011871770800639, -3.318562255325795, -0.12660138709292057, -0.20489551789966803, -0.1525100012592784, -0.02465488205886147, -0.44232233201277743, -0.16408537644014787, 0.28709832395121765, 0.06989189152237547, -0.445234967991587, 0.1446267442307163, -0.13342014677484493, 0.04884580398085625, -0.06120785634410805, -0.0065053622920859784, -0.055536979099377465, -0.05310883929020609, 0.137613494649232, 0.09694858537476729, 0.05875484376287877, 0.11266078234486253, -0.10900916485152536,
-0.1965429454017319, 15.28499617463146, -0.09827147270085763, 0.030118717708007003, -0.42075792920997546, 0.12660138709291616, 3.318562255325798, -0.28709832395121665, 0.16408537644014565, 0.4452349679915819, -0.06989189152237295, 0.15251000125928654, 0.20489551789967583, 0.44232233201277626, 0.024654882058862064, -0.14462674423071573, -0.048845803980857666, 0.13342014677484318, 0.06120785634410922, 0.055536979099377264, 0.006505362292085202, -0.13761349464923325, 0.0531088392902052, 0.10900916485152572, -0.11266078234486292, -0.05875484376287771, -0.09694858537476635,
-0.09827147270087869, 0.09827147270085763, 0.0, 0.05238551664039212, -0.05238551664039154, -0.41766744995391064, 0.41766744995391425, 0.13396665873213762, 0.13365960525875686, -0.19649327861528906, -2.87332728733421, -0.1336596052587585, -0.13396665873213948, 2.873327287334207, 0.19649327861529092, 7.071416687303329e-16, 0.0957809402498582, -0.09578094024985911, 5.095198309682238e-16, -0.005670877244732207, 0.005670877244731516, -0.0381937416118884, 0.03819374161188797, 0.024952712304370763, 0.055900325561320505, -0.05590032556132075, -0.02495271230436985,
-2.688383016538256, -0.19243998400255485, -0.3347110618046863, 0.0, -1.8584179757445634, 2.9254109394468375, 0.5544784209743959, -0.09619204170147806, 0.9621335517719756, -0.7640240022365051, -0.17045575971749122, -0.32599503457469536, 1.5819839652391803, -0.1673484738314025, -2.1466117678065375, 0.20084022575022525, 0.8121668775566188, 0.28698839120282377, 0.22242408399761535, 0.1007642564284906, 0.1396610379722831, -0.4047350379804057, -0.19103788701331148, -0.4861845324334061, 0.0708248987102739, -0.5390429074476013, 0.02706866117436579,
0.1924399840025509, 2.688383016538247, 0.3347110618046826, 1.8584179757445634, 0.0, -0.5544784209744025, -2.9254109394468055, -1.5819839652391763, 0.32599503457469153, 2.1466117678065477, 0.1673484738314174, -0.9621335517719789, 0.09619204170146632, 0.1704557597174755, 0.7640240022365011, -0.20084022575022814, -0.28698839120282255, -0.8121668775566167, -0.22242408399761296, -0.13966103797228493, -0.10076425642849256, 0.19103788701331245, 0.40473503798040156, -0.027068661174367476, 0.5390429074475989, -0.07082489871027405, 0.48618453243340803,
16.79909075460827, -0.6408763879659072, 2.114299162464476, -2.3177353997619106, 0.43930042352937404, 0.0, 0.23758827371798064, 0.14587872928125803, -0.026683834684955113, 0.27594927228434313, -0.8508769996321647, 1.733847812899358, -1.9338802842578322, -1.4125593711637086, 1.7702761450361215, 0.49385180688379604, -0.48895648790844665, -0.15872007418498266, -0.09920419307861998, 0.12317929238116793, 0.08585557349611746, 0.09370614907316197, -0.2607460957437406, -0.00372051314937977, -0.04020180712625255, 0.5265453421646704, -0.4929563256972127,
0.6408763879659295, -16.79909075460828, -2.1142991624644942, -0.4393004235293688, 2.3177353997618853, -0.23758827371798064, 0.0, 1.9338802842578542, -1.733847812899381, -1.7702761450361162, 1.4125593711636986, 0.026683834684962007, -0.14587872928127077, 0.8508769996321606, -0.27594927228435256, -0.4938518068837918, 0.15872007418498413, 0.4889564879084464, 0.0992041930786239, -0.08585557349611386, -0.1231792923811642, 0.26074609574374336, -0.09370614907315725, 0.49295632569721404, -0.5265453421646692, 0.040201807126253306, 0.0037205131493783417,
1.3091556741914314, 1.834380779552396, -0.8559641188321434, 0.09619204170147806, 1.5819839652391763, -0.1841259491984436, -2.440914756589403, 19.910396398833793, 0.9292089878722896, -0.6009288150226979, -0.17768908386483087, -0.22980299287322684, -1.9242671035439767, 2.9524607667234655, 0.2041356965652301, -1.1190995177139584, -0.24116380960165718, 0.7245564881296083, -0.49029775323051117, -0.04870816987800388, -0.7132041988172136, -0.454608663355292, 0.08758856822375095, 1.0907550537346067, 4.030854005025503, 0.7748473349352286, 0.07689148379455818,
0.9744446123867609, -1.0484041028347049, -0.8540022369858385, -0.9621335517719756, -0.32599503457469153, 0.03367993684774262, 2.18843676448695, -0.9292089878722896, 19.910396398833793, 0.3767893371095702, 2.3244821244241383, 3.163967930478371, -0.22980299287321784, -4.587526524395932, -0.3514744230298633, 1.53672336568623, -0.04032358385143443, -0.832111126511136, -0.6124399423887177, 0.1737159141196091, -0.3506367152582221, 0.14771638250483257, 0.2886628025018225, 3.839816118012189, 0.6860200157542047, 0.11465722939811557, -0.9936515708028879,
0.12480694029056734, -2.253850330037397, 0.9946802760235417, 0.6053185391404056, -1.7007108357677436, -0.27594927228434313, 1.7702761450361162, 0.47610199597409236, -0.29852147371705867, 17.141859608597226, -0.11879413685898123, -0.27846505302304264, 0.16173198913849687, -1.6885086434480685, 1.7017539992643287, 0.5994748509692391, -0.6941668393814941, 3.674640424508895, -0.09744008746280787, 0.20279671720873968, -0.3963039467276607, -0.7855375215315802, 0.6600584756782935, -0.4640248521828324, 0.7038226731025359, -0.1687199363657259, 0.20287631854527582,
2.2391061027550703, 0.35380388805763474, 14.545240424570853, 0.13504815445355628, -0.13258632374176726, 0.8508769996321647, -1.4125593711636986, 0.14077894981231426, -1.8416334037878173, 0.11879413685898123, 17.141859608597226, -3.634591120025567, 2.339166352039784, 3.5405522900722497, -1.6885086434480694, 3.1856839366004506, -0.20031503249770058, 0.44075477678425123, -0.2731246543464956, 0.10359252413012018, -0.0115845139666891, 0.16267451141902461, -0.1724404495151067, 0.4430765773587932, -0.3703187031096739, 0.1671021499810791, -0.2589921793669097,
1.048404102834719, -0.9744446123868129, 0.8540022369858489, 0.32599503457469536, 0.9621335517719789, -2.1884367644869207, -0.03367993684775132, 0.22980299287322684, -3.163967930478371, 0.3514744230298829, 4.587526524395912, -19.910396398833793, 0.9292089878722776, -2.324482124424116, -0.37678933710957857, -1.536723365686234, 0.8321111265111332, 0.04032358385143004, 0.612439942388719, 0.35063671525822543, -0.17371591411960696, -0.28866280250182014, -0.14771638250483188, 0.9936515708028864, -0.11465722939811639, -0.6860200157542059, -3.8398161180121906,
-1.8343807795524023, -1.3091556741914812, 0.8559641188321553, -1.5819839652391803, -0.09619204170146632, 2.440914756589375, 0.18412594919845968, 1.9242671035439767, 0.22980299287321784, -0.2041356965652155, -2.9524607667234513, -0.9292089878722776, -19.910396398833793, 0.17768908386480717, 0.6009288150227365, 1.1190995177139624, -0.7245564881296058, 0.24116380960166542, 0.49029775323050956, 0.7132041988172101, 0.04870816987800567, -0.08758856822375086, 0.4546086633552912, -0.07689148379455372, -0.7748473349352273, -4.030854005025498, -1.0907550537346087,
-0.35380388805764745, -2.2391061027550645, -14.545240424570837, 0.13258632374175544, -0.13504815445354384, 1.4125593711637086, -0.8508769996321606, -2.3391663520397947, 3.6345911200255827, 1.6885086434480685, -3.5405522900722497, 1.8416334037877997, -0.1407789498122955, -17.141859608597226, -0.11879413685897054, -3.1856839366004435, -0.44075477678425035, 0.20031503249770535, 0.2731246543464969, 0.01158451396669102, -0.10359252413011845, 0.17244044951510712, -0.16267451141902445, 0.25899217936691005, -0.16710214998107684, 0.37031870310967413, -0.44307657735879563,
2.2538503300374226, -0.12480694029057034, -0.994680276023551, 1.7007108357677354, -0.6053185391404025, -1.7702761450361215, 0.27594927228435256, -0.16173198913850842, 0.2784650530230271, -1.7017539992643287, 1.6885086434480694, 0.29852147371706533, -0.476101995974123, 0.11879413685897054, -17.141859608597226, -0.5994748509692417, -3.6746404245088935, 0.6941668393814934, 0.09744008746280555, 0.39630394672766056, -0.2027967172087368, -0.6600584756782925, 0.7855375215315809, -0.20287631854527904, 0.16871993636572646, -0.7038226731025339, 0.4640248521828319,
-3.766522549523058, 3.7665225495230428, -1.8416130814168517e-14, -0.8186227860390011, 0.818622786039013, -2.5406965754889375, 2.5406965754889157, 4.561438634236725, -6.263669333621437, -3.0840905707320223, -16.389240973690264, 6.263669333621453, -4.561438634236742, 16.38924097369023, 3.0840905707320356, 0.0, 0.8064071592627126, -0.8064071592627154, -3.27587819047868e-15, -0.2376302079323201, 0.23763020793230094, -0.5181032452374912, 0.5181032452374866, -2.3969263418417017, 0.8425636809391238, -0.8425636809391335, 2.396926341841694,
3.474668492754472, 1.2720940592425334, -2.494428490280513, -3.3103842098885403, 1.169761861647588, 2.5155118541145702, -0.8165598329744024, 0.9829813174648175, 0.1643585314258267, 3.571248068855471, 1.030551493366546, -3.3916767725891264, 2.953285123732874, 2.267530737757596, 18.904752827804817, -0.8064071592627126, 0.0, -1.6128143185254504, 0.281848015316382, -1.069973391943026, 0.5194782232486872, 1.842445782443736, -3.2355644271100483, -0.020449207033394267, -0.5385524522708879, -0.8386380852683435, 0.9998821015046064,
-1.2720940592424965, -3.474668492754426, 2.4944284902805367, -1.169761861647593, 3.310384209888532, 0.8165598329743948, -2.515511854114569, -2.953285123732884, 3.391676772589138, -18.904752827804824, -2.2675307377576006, -0.1643585314258088, -0.9829813174648512, -1.0305514933665705, -3.571248068855467, 0.8064071592627154, 1.6128143185254504, 0.0, -0.2818480153163751, -0.5194782232487007, 1.0699733919430288, 3.235564427110037, -1.8424457824437297, -0.9998821015046048, 0.8386380852683486, 0.5385524522708905, 0.020449207033400366,
2.2431057877263694, -2.243105787726412, -1.867255202307378e-14, -1.275749958493393, 1.2757499584933791, 0.7181857904438594, -0.7181857904438878, 2.8121834969091495, 3.512750134965057, 0.7054146005697136, 1.977277771072542, -3.5127501349650645, -2.8121834969091406, -1.9772777710725515, -0.705414600569697, 4.609760570128499e-15, -0.39661177621033, 0.3966117762103203, 0.0, 1.473286325955928, -1.4732863259559217, 0.03294149361242836, -0.0329414936124289, -3.953179136944931, -1.408551582879458, 1.4085515828794593, 3.9531791369449287,
0.23840429448465078, -2.035283159571775, 0.20782262815464633, -0.5779499847581375, 0.8010488801122674, -0.8917528051961954, 0.6215488579847753, 0.27937372870475113, -0.996376229788628, -1.468140772361843, -0.749954981917987, -2.0111346167968778, -4.090700119723205, -0.08386574258491672, -2.869030576268716, 0.3343892230125261, 1.5056485212427524, 0.7310009992228542, -1.473286325955928, 0.0, -2.9465726519118554, -0.8795833962368981, 0.19919319766269372, 0.49291247512221925, 0.5258539687346377, 4.152372334607633, 0.5289681866425602,
2.035283159571782, -0.23840429448462236, -0.20782262815462102, -0.801048880112257, 0.5779499847581487, -0.6215488579848013, 0.8917528051961683, 4.090700119723225, 2.0111346167968587, 2.869030576268717, 0.0838657425849028, 0.9963762297886157, -0.27937372870476146, 0.7499549819179745, 1.468140772361822, -0.3343892230124991, -0.7310009992228351, -1.5056485212427564, 1.4732863259559217, 2.9465726519118554, 0.0, -0.19919319766269705, 0.8795833962369017, -0.5289681866425588, -4.152372334607625, -0.5258539687346414, -0.49291247512221426,
1.759402157237792, 4.558892315982919, 1.2652912826383578, 2.0985063083979236, -0.9905102682507421, -0.6132401678100317, -1.706397937502056, 2.357095527641503, -0.7658930694187117, 5.140784956908139, -1.0645890975962542, 1.4966846336620354, 0.4541370173391847, -1.1285002237720632, 4.319613754198333, 0.6590565197748737, -2.3436948454060653, -4.115820254824023, -0.029778238966182636, 0.795120126366912, 0.1800653822873389, 0.0, 3.1163752731045724, 0.15960718397414111, -0.7346658981606436, -0.1973888096528869, -0.7116415547893532,
-4.558892315982878, -1.7594021572377623, -1.2652912826383433, 0.9905102682507371, -2.0985063083979023, 1.7063979375020377, 0.6132401678100009, -0.4541370173391852, -1.4966846336620476, -4.319613754198339, 1.1285002237720605, 0.7658930694187083, -2.357095527641499, 1.064589097596253, -5.140784956908144, -0.6590565197748679, 4.115820254824037, 2.3436948454060573, 0.029778238966183125, -0.1800653822873359, -0.7951201263669153, -3.1163752731045724, 0.0, 0.71164155478935, 0.1973888096528901, 0.7346658981606372, -0.15960718397413862,
-3.2117356080303803, -3.6112813302216713, -0.8266393399665194, 2.520812908731641, 0.14034800771819048, 0.024348115151799948, -3.2260489080343286, -5.655444047050364, -19.909020941069866, 3.0367129695880832, -2.8996213945368607, -5.151973251646951, 0.3986738203099664, -1.6949198009668083, 1.3276813620016823, 3.049025358424062, 0.026012543529633645, 1.271906370130427, 3.57356938337672, -0.4455798406197396, 0.47817325020768053, -0.15960718397414111, -0.71164155478935, 0.0, -1.5581876365522882, -0.3569959936270113, 1.46933179632127,
-1.9464443253920187, 3.7322529760164143, -1.8518791729839088, -0.3672192491087197, -2.794877724005471, 0.26309226440541583, 3.4458651559413247, -20.899531209320617, -3.556937738652459, -4.606019332038912, 2.4234728017780744, 0.594485025057365, 4.017497542393682, 1.0935648461639937, -1.1041521086202575, -1.071788475275629, 0.6850690633045105, -1.0667948963999776, 1.2732933765745933, -0.4753580795859133, 3.753634765664056, 0.7346658981606436, -0.1973888096528901, 1.5581876365522882, 0.0, -1.4232831095787026, -0.35699599362703105,
-3.7322529760164014, 1.9464443253919836, 1.8518791729839168, 2.794877724005484, 0.36721924910872045, -3.4458651559413327, -0.26309226440542083, -4.017497542393689, -0.5944850250573608, 1.1041521086202537, -1.0935648461640088, 3.5569377386524654, 20.89953120932059, -2.423472801778076, 4.6060193320388985, 1.0717884752756415, 1.0667948963999712, -0.6850690633045139, -1.2732933765745946, -3.753634765664064, 0.47535807958591664, 0.1973888096528869, -0.7346658981606372, 0.3569959936270113, 1.4232831095787026, 0.0, -1.5581876365522849,
3.6112813302216593, 3.2117356080303487, 0.8266393399664891, -0.14034800771818173, -2.5208129087316506, 3.2260489080343198, -0.024348115151790604, -0.3986738203099895, 5.151973251646959, -1.3276813620016612, 1.694919800966806, 19.909020941069873, 5.6554440470503735, 2.8996213945368763, -3.03671296958808, -3.0490253584240525, -1.2719063701304292, -0.026012543529641403, -3.5735693833767184, -0.47817325020768164, 0.4455798406197351, 0.7116415547893532, 0.15960718397413862, -1.46933179632127, 0.35699599362703105, 1.5581876365522849, 0.0};
const double p4Dy[729] = {-15.28499617463146, 0.09827147270086124, 0.19654294540174796, 0.2870983239512125, -0.164085376440144, -0.4452349679915834, 0.06989189152237135, -0.15251000125927563, -0.2048955178996692, -0.44232233201278237, -0.024654882058863847, -0.03011871770800668, 0.42075792920998006, -0.12660138709291216, -3.3185622553257907, 0.048845803980856306, -0.13342014677484385, 0.14462674423071498, -0.05553697909937705, -0.006505362292085907, -0.06120785634410891, -0.10900916485152638, 0.11266078234486171, 0.05875484376287875, 0.09694858537476714, 0.1376134946492328, -0.05310883929020566,
-0.09827147270086124, 0.0, 0.09827147270087347, -0.13396665873213587, -0.13365960525875817, 0.19649327861528584, 2.8733272873342135, 0.1336596052587607, 0.13396665873214064, -2.873327287334207, -0.19649327861528823, -0.052385516640395494, 0.05238551664038741, 0.4176674499539139, -0.41766744995391747, -0.095780940249859, 0.09578094024985785, -9.030965889809071e-16, 0.00567087724473144, -0.0056708772447323225, -2.468104976161084e-16, -0.024952712304370517, -0.05590032556132032, 0.05590032556132042, 0.02495271230436975, 0.03819374161188785, -0.03819374161188903,
-0.19654294540174796, -0.09827147270087347, 15.28499617463146, 0.20489551789967816, 0.15251000125928516, 0.024654882058868614, 0.44232233201278004, 0.16408537644014445, -0.2870983239512186, -0.06989189152236565, 0.445234967991583, -0.42075792920997346, 0.03011871770800419, 3.3185622553257907, 0.12660138709291832, 0.13342014677484448, -0.04884580398085644, -0.14462674423071606, 0.0065053622920860695, 0.055536979099378284, 0.06120785634410899, -0.0969485853747657, -0.058754843762877665, -0.11266078234486313, 0.1090091648515255, 0.05310883929020579, -0.1376134946492322,
-1.8343807795523694, 0.8559641188321322, -1.309155674191496, -19.910396398833793, -0.9292089878722894, 0.6009288150227082, 0.1776890838648202, 0.22980299287321443, 1.9242671035439682, -2.9524607667234357, -0.20413569656523223, -0.09619204170148214, -1.581983965239188, 0.18412594919846403, 2.4409147565893874, 0.24116380960165718, -0.7245564881296103, 1.1190995177139629, 0.048708169878006975, 0.7132041988172096, 0.49029775323050717, -1.0907550537346093, -4.0308540050255, -0.774847334935228, -0.07689148379455714, 0.45460866335528777, -0.0875885682237497,
1.0484041028346944, 0.8540022369858469, -0.9744446123868041, 0.9292089878722894, -19.910396398833793, -0.376789337109576, -2.3244821244241085, -3.163967930478372, 0.22980299287322126, 4.587526524395933, 0.35147442302985465, 0.9621335517719841, 0.32599503457468426, -0.03367993684773616, -2.1884367644869362, 0.0403235838514332, 0.8321111265111371, -1.5367233656862271, -0.17371591411960613, 0.3506367152582226, 0.6124399423887191, -3.8398161180121875, -0.686020015754205, -0.11465722939811551, 0.99365157080289, -0.14771638250483227, -0.2886628025018202,
2.2538503300374044, -0.9946802760235254, -0.1248069402906035, -0.4761019959741005, 0.2985214737170633, -17.141859608597226, 0.1187941368589855, 0.27846505302302493, -0.1617319891384971, 1.6885086434480594, -1.7017539992643365, -0.605318539140416, 1.700710835767743, 0.2759492722843605, -1.7702761450361306, 0.6941668393814986, -3.674640424508889, -0.5994748509692356, -0.20279671720873876, 0.3963039467276609, 0.097440087462807, 0.4640248521828354, -0.703822673102532, 0.16871993636572868, -0.20287631854527582, 0.7855375215315806, -0.6600584756782898,
-0.35380388805762664, -14.545240424570872, -2.2391061027550836, -0.14077894981230582, 1.8416334037877937, -0.1187941368589855, -17.141859608597226, 3.6345911200255987, -2.339166352039789, -3.540552290072251, 1.6885086434480605, -0.135048154453527, 0.13258632374176318, -0.8508769996321724, 1.4125593711637054, 0.20031503249770397, -0.44075477678425157, -3.1856839366004435, -0.103592524130115, 0.01158451396669216, 0.2731246543464982, -0.44307657735879197, 0.37031870310967574, -0.16710214998107722, 0.2589921793669143, -0.16267451141902012, 0.17244044951510798,
0.9744446123867433, -0.854002236985863, -1.048404102834697, -0.22980299287321443, 3.163967930478372, -0.35147442302986054, -4.587526524395952, 19.910396398833793, -0.9292089878722678, 2.324482124424122, 0.37678933710956625, -0.32599503457469653, -0.9621335517719883, 2.188436764486939, 0.03367993684775833, -0.8321111265111345, -0.0403235838514329, 1.5367233656862256, -0.3506367152582263, 0.17371591411960866, -0.612439942388722, -0.9936515708028895, 0.11465722939811439, 0.686020015754204, 3.839816118012191, 0.28866280250181964, 0.1477163825048354,
1.3091556741914387, -0.8559641188321627, 1.8343807795524083, -1.9242671035439682, -0.22980299287322126, 0.20413569656521577, 2.952460766723458, 0.9292089878722678, 19.910396398833793, -0.17768908386481014, -0.6009288150226963, 1.5819839652391892, 0.09619204170147512, -2.440914756589397, -0.18412594919844522, 0.7245564881296075, -0.24116380960166175, -1.119099517713959, -0.7132041988172084, -0.04870816987800363, -0.4902977532305103, 0.0768914837945529, 0.774847334935229, 4.030854005025501, 1.090755053734602, 0.08758856822374894, -0.4546086633552895,
2.2391061027550956, 14.545240424570837, 0.3538038880575977, 2.3391663520397716, -3.634591120025584, -1.6885086434480594, 3.540552290072251, -1.8416334037878042, 0.14077894981229783, 17.141859608597226, 0.1187941368589982, -0.13258632374177015, 0.13504815445355095, -1.4125593711636983, 0.8508769996321621, 0.4407547767842533, -0.200315032497699, 3.1856839366004475, -0.011584513966690685, 0.10359252413011888, -0.27312465434649635, -0.2589921793669082, 0.16710214998107692, -0.37031870310967424, 0.4430765773587957, -0.17244044951510495, 0.16267451141902212,
0.12480694029057937, 0.9946802760235375, -2.253850330037402, 0.16173198913851014, -0.27846505302302027, 1.7017539992643365, -1.6885086434480605, -0.29852147371705556, 0.47610199597409114, -0.1187941368589982, 17.141859608597226, -1.7007108357677325, 0.6053185391404096, 1.7702761450361277, -0.27594927228436483, 3.674640424508897, -0.694166839381494, 0.5994748509692353, -0.39630394672765873, 0.2027967172087418, -0.09744008746280586, 0.2028763185452754, -0.1687199363657282, 0.7038226731025327, -0.4640248521828344, 0.6600584756782918, -0.7855375215315795,
0.1924399840025528, 0.33471106180470783, 2.6883830165382343, 0.09619204170148214, -0.9621335517719841, 0.7640240022365183, 0.17045575971745425, 0.32599503457469653, -1.5819839652391892, 0.16734847383142104, 2.1466117678065335, 0.0, 1.8584179757445463, -2.9254109394468206, -0.5544784209744086, -0.8121668775566203, -0.286988391202826, -0.20084022575023078, -0.10076425642849045, -0.13966103797228321, -0.2224240839976154, 0.4861845324334064, -0.07082489871027524, 0.5390429074475949, -0.027068661174365027, 0.4047350379803992, 0.1910378870133102,
-2.6883830165382765, -0.33471106180465615, -0.19243998400253687, 1.581983965239188, -0.32599503457468426, -2.146611767806547, -0.16734847383141227, 0.9621335517719883, -0.09619204170147512, -0.1704557597174845, -0.7640240022365101, -1.8584179757445463, 0.0, 0.554478420974375, 2.925410939446845, 0.2869883912028199, 0.8121668775566261, 0.20084022575022775, 0.1396610379722875, 0.10076425642848952, 0.22242408399761332, 0.027068661174365592, -0.5390429074475985, 0.07082489871027872, -0.4861845324334045, -0.191037887013308, -0.40473503798040444,
0.640876387965887, -2.114299162464493, -16.799090754608244, -0.14587872928127424, 0.026683834684949993, -0.2759492722843605, 0.8508769996321724, -1.7338478128993726, 1.9338802842578497, 1.4125593711636983, -1.7702761450361277, 2.3177353997618972, -0.43930042352935234, 0.0, -0.23758827371795765, 0.4889564879084486, 0.15872007418498785, -0.4938518068837892, -0.12317929238116428, -0.08585557349611499, 0.09920419307862376, 0.0037205131493781296, 0.04020180712624783, -0.5265453421646701, 0.49295632569721504, -0.09370614907316001, 0.26074609574373936,
16.799090754608244, 2.1142991624645107, -0.6408763879659182, -1.933880284257842, 1.7338478128993704, 1.7702761450361306, -1.4125593711637054, -0.02668383468496756, 0.1458787292812593, -0.8508769996321621, 0.27594927228436483, 0.4393004235293789, -2.317735399761917, 0.23758827371795765, 0.0, -0.15872007418498993, -0.4889564879084488, 0.4938518068837932, 0.08585557349611653, 0.12317929238116535, -0.0992041930786226, -0.4929563256972161, 0.52654534216467, -0.04020180712625487, -0.0037205131493803054, -0.2607460957437367, 0.09370614907315984,
-1.2720940592424979, 2.494428490280534, -3.4746684927544607, -0.9829813174648175, -0.16435853142582169, -3.5712480688554935, -1.0305514933665634, 3.3916767725891317, -2.953285123732881, -2.2675307377576113, -18.904752827804835, 3.310384209888547, -1.1697618616475773, -2.5155118541145804, 0.8165598329744322, 0.0, 1.6128143185254429, 0.806407159262733, 1.0699733919430285, -0.5194782232486954, -0.2818480153163862, 0.020449207033393636, 0.538552452270888, 0.8386380852683486, -0.9998821015046098, -1.842445782443737, 3.2355644271100434,
3.4746684927544433, -2.4944284902805043, 1.2720940592425014, 2.9532851237328925, -3.3916767725891424, 18.904752827804796, 2.2675307377576024, 0.1643585314258205, 0.9829813174648363, 1.0305514933665378, 3.5712480688554704, 1.1697618616476022, -3.3103842098885705, -0.8165598329744215, 2.5155118541145813, -1.6128143185254429, 0.0, -0.8064071592627268, 0.5194782232486914, -1.069973391943029, 0.28184801531637504, 0.9998821015046067, -0.838638085268352, -0.5385524522708939, -0.020449207033397976, -3.2355644271100372, 1.8424457824437361,
-3.7665225495230237, 2.3519395979540512e-14, 3.7665225495230517, -4.561438634236743, 6.263669333621425, 3.084090570732004, 16.38924097369023, -6.263669333621419, 4.561438634236729, -16.38924097369025, -3.0840905707320028, 0.8186227860390236, -0.8186227860390113, 2.5406965754889024, -2.540696575488923, -0.806407159262733, 0.8064071592627268, 0.0, 0.23763020793230993, -0.23763020793231787, 4.300846213266488e-15, 2.39692634184169, -0.8425636809391248, 0.8425636809391247, -2.3969263418416866, 0.5181032452374869, -0.5181032452374932,
2.035283159571767, -0.20782262815461824, -0.2384042944846541, -0.27937372870476895, 0.9963762297886111, 1.4681407723618363, 0.7499549819179495, 2.011134616796883, 4.090700119723195, 0.08386574258491429, 2.8690305762687025, 0.5779499847581366, -0.801048880112282, 0.891752805196169, -0.6215488579847945, -1.5056485212427562, -0.7310009992228411, -0.3343892230125118, 0.0, 2.9465726519118554, 1.4732863259559286, -0.4929124751222108, -0.5258539687346461, -4.1523723346076205, -0.5289681866425575, 0.8795833962369044, -0.1991931976626978,
0.2384042944846482, 0.2078226281546506, -2.0352831595718124, -4.090700119723202, -2.0111346167968613, -2.8690305762687185, -0.08386574258492496, -0.9963762297886256, 0.27937372870474975, -0.7499549819179776, -1.4681407723618582, 0.8010488801122575, -0.5779499847581313, 0.6215488579847835, -0.8917528051961766, 0.7310009992228468, 1.5056485212427566, 0.334389223012523, -2.9465726519118554, 0.0, -1.47328632595593, 0.5289681866425614, 4.152372334607627, 0.5258539687346433, 0.4929124751222133, 0.19919319766269775, -0.8795833962368973,
2.243105787726401, 9.044950905679127e-15, -2.2431057877264036, -2.812183496909127, -3.512750134965065, -0.7054146005697074, -1.9772777710725609, 3.512750134965082, 2.8121834969091446, 1.9772777710725475, 0.7054146005696991, 1.2757499584933931, -1.2757499584933811, -0.7181857904438868, 0.7181857904438784, 0.39661177621033594, -0.39661177621032023, -6.052078294524533e-15, -1.4732863259559286, 1.47328632595593, 0.0, 3.9531791369449296, 1.4085515828794586, -1.4085515828794646, -3.953179136944927, -0.03294149361242823, 0.032941493612420415,
3.6112813302216935, 0.8266393399665112, 3.2117356080303274, 5.655444047050377, 19.909020941069855, -3.0367129695881028, 2.8996213945368523, 5.151973251646967, -0.3986738203099622, 1.6949198009667963, -1.3276813620016585, -2.520812908731642, -0.1403480077181807, -0.024348115151789213, 3.226048908034342, -0.026012543529632844, -1.2719063701304294, -3.049025358424047, 0.445579840619732, -0.4781732502076828, -3.573569383376719, 0.0, 1.5581876365522862, 0.35699599362702117, -1.4693317963212784, 0.159607183974139, 0.7116415547893488,
-3.7322529760163743, 1.8518791729839026, 1.946444325391982, 20.899531209320596, 3.556937738652461, 4.606019332038886, -2.4234728017780864, -0.5944850250573547, -4.01749754239369, -1.0935648461639944, 1.1041521086202688, 0.3672192491087266, 2.7948777240054694, -0.26309226440538497, -3.4458651559413305, -0.6850690633045106, 1.066794896399982, 1.0717884752756304, 0.4753580795859208, -3.7536347656640583, -1.273293376574594, -1.5581876365522862, 0.0, 1.4232831095787029, 0.3569959936270241, -0.7346658981606364, 0.19738880965288536,
-1.9464443253920178, -1.8518791729839061, 3.7322529760164214, 4.017497542393685, 0.5944850250573606, -1.104152108620272, 1.0935648461639964, -3.5569377386524557, -20.8995312093206, 2.4234728017780762, -4.6060193320388905, -2.7948777240054508, -0.36721924910874476, 3.445865155941331, 0.26309226440543104, -1.0667948963999776, 0.685069063304518, -1.0717884752756304, 3.753634765664053, -0.47535807958591836, 1.2732933765745995, -0.35699599362702117, -1.4232831095787029, 0.0, 1.5581876365522802, -0.19738880965288783, 0.73466589816063,
-3.211735608030375, -0.8266393399664858, -3.611281330221664, 0.39867382030998416, -5.15197325164697, 1.3276813620016612, -1.6949198009668363, -19.909020941069876, -5.65544404705034, -2.8996213945368767, 3.0367129695880966, 0.14034800771817776, 2.5208129087316324, -3.226048908034335, 0.024348115151803455, 1.2719063701304334, 0.026012543529638363, 3.0490253584240428, 0.4781732502076793, -0.4455798406197342, 3.573569383376717, 1.4693317963212784, -0.3569959936270241, -1.5581876365522802, 0.0, -0.7116415547893499, -0.1596071839741321,
-4.558892315982905, -1.2652912826383393, -1.7594021572377818, -2.3570955276414813, 0.7658930694187103, -5.140784956908142, 1.0645890975962247, -1.496684633662033, -0.4541370173391748, 1.1285002237720492, -4.319613754198328, -2.09850630839789, 0.9905102682507191, 0.6132401678100189, 1.7063979375020122, 2.3436948454060667, 4.115820254824023, -0.6590565197748682, -0.7951201263669176, -0.18006538228733954, 0.029778238966182518, -0.159607183974139, 0.7346658981606364, 0.19738880965288783, 0.7116415547893499, 0.0, -3.11637527310457,
1.7594021572377778, 1.2652912826383786, 4.5588923159828845, 0.4541370173391787, 1.4966846336620356, 4.319613754198315, -1.1285002237720687, -0.7658930694187266, 2.35709552764149, -1.064589097596238, 5.140784956908135, -0.9905102682507304, 2.098506308397917, -1.7063979375020297, -0.6132401678100178, -4.115820254824031, -2.3436948454060658, 0.6590565197748763, 0.1800653822873396, 0.7951201263669113, -0.029778238966175454, -0.7116415547893488, -0.19738880965288536, -0.73466589816063, 0.1596071839741321, 3.11637527310457, 0.0};
-1
View File
@@ -31,7 +31,6 @@
#include "estimators.hpp"
#include "staticcond.hpp"
#include "tmop.hpp"
#include "tmop_tools.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
+43 -700
View File
@@ -12,7 +12,6 @@
// Implementation of FiniteElementSpace
#include "../general/text.hpp"
#include "../general/forall.hpp"
#include "../mesh/mesh_headers.hpp"
#include "fem.hpp"
@@ -386,7 +385,6 @@ void FiniteElementSpace::MarkerToList(const Array<int> &marker,
Array<int> &list)
{
int num_marked = 0;
marker.HostRead(); // make sure we can read the array on host
for (int i = 0; i < marker.Size(); i++)
{
if (marker[i]) { num_marked++; }
@@ -567,40 +565,6 @@ bool FiniteElementSpace::DofFinalizable(int dof, const Array<bool>& finalized,
return true;
}
void FiniteElementSpace::GetDegenerateFaceDofs(int index,
Array<int> &dofs) const
{
// In NC meshes with prisms, a special constraint occurs where a prism edge
// is slave to a quadrilateral face. Rather than introduce a new edge-face
// constraint type, we handle such cases as degenerate face-face constraints,
// where the point-matrix rectangle has zero height. This method returns
// DOFs for the first edge of the rectangle, duplicated in the orthogonal
// direction, to resemble DOFs for a quadrilateral face. The extra DOFs are
// ignored by FiniteElementSpace::AddDependencies.
Array<int> edof;
GetEdgeDofs(-1 - index, edof);
int nv = fec->DofForGeometry(Geometry::POINT);
int ne = fec->DofForGeometry(Geometry::SEGMENT);
int nn = 2*nv + ne;
dofs.SetSize(nn*nn);
dofs = edof[0];
// copy first two vertex DOFs
for (int i = 0; i < nv; i++)
{
dofs[i] = edof[i];
dofs[nv+i] = edof[nv+i];
}
// copy first edge DOFs
for (int i = 0; i < ne; i++)
{
dofs[4*nv + i] = edof[2*nv + i];
}
}
void
FiniteElementSpace::GetEntityDofs(int entity, int index, Array<int> &dofs) const
{
@@ -608,8 +572,7 @@ FiniteElementSpace::GetEntityDofs(int entity, int index, Array<int> &dofs) const
{
case 0: GetVertexDofs(index, dofs); break;
case 1: GetEdgeDofs(index, dofs); break;
case 2: (index >= 0) ? GetFaceDofs(index, dofs)
/* */ : GetDegenerateFaceDofs(index, dofs);
case 2: GetFaceDofs(index, dofs); break;
}
}
@@ -632,33 +595,28 @@ void FiniteElementSpace::BuildConformingInterpolation() const
// collect local edge/face dependencies
for (int entity = 1; entity <= 2; entity++)
{
const NCMesh::NCList &list = mesh->ncmesh->GetNCList(entity);
const NCMesh::NCList &list = (entity > 1) ? mesh->ncmesh->GetFaceList()
/* */ : mesh->ncmesh->GetEdgeList();
if (!list.masters.size()) { continue; }
Array<int> master_dofs, slave_dofs;
IsoparametricTransformation T;
DenseMatrix I;
if (entity > 1) { T.SetFE(&QuadrilateralFE); }
else { T.SetFE(&SegmentFE); }
Geometry::Type geom = (entity > 1) ? Geometry::SQUARE : Geometry::SEGMENT;
const FiniteElement* fe = fec->FiniteElementForGeometry(geom);
if (!fe) { continue; }
Array<int> master_dofs, slave_dofs;
DenseMatrix I(fe->GetDof());
// loop through all master edges/faces, constrain their slave edges/faces
for (unsigned mi = 0; mi < list.masters.size(); mi++)
{
const NCMesh::Master &master = list.masters[mi];
GetEntityDofs(entity, master.index, master_dofs);
if (!master_dofs.Size()) { continue; }
const FiniteElement* fe = fec->FiniteElementForGeometry(master.Geom());
if (!fe) { continue; }
switch (master.geom)
{
case Geometry::SQUARE: T.SetFE(&QuadrilateralFE); break;
case Geometry::TRIANGLE: T.SetFE(&TriangleFE); break;
case Geometry::SEGMENT: T.SetFE(&SegmentFE); break;
default: MFEM_ABORT("unsupported geometry");
}
for (int si = master.slaves_begin; si < master.slaves_end; si++)
{
const NCMesh::Slave &slave = list.slaves[si];
@@ -694,9 +652,9 @@ void FiniteElementSpace::BuildConformingInterpolation() const
// create the conforming restriction matrix cR
int *cR_J;
{
int *cR_I = new int[n_true_dofs+1];
double *cR_A = new double[n_true_dofs];
cR_J = new int[n_true_dofs];
int *cR_I = mfem::New<int>(n_true_dofs+1);
double *cR_A = mfem::New<double>(n_true_dofs);
cR_J = mfem::New<int>(n_true_dofs);
for (int i = 0; i < n_true_dofs; i++)
{
cR_I[i] = i;
@@ -774,8 +732,6 @@ void FiniteElementSpace::BuildConformingInterpolation() const
MakeVDimMatrix(*cP);
MakeVDimMatrix(*cR);
}
if (Device::IsEnabled()) { cP->BuildTranspose(); }
}
void FiniteElementSpace::MakeVDimMatrix(SparseMatrix &mat) const
@@ -826,63 +782,6 @@ int FiniteElementSpace::GetNConformingDofs() const
return P ? (P->Width() / vdim) : ndofs;
}
const Operator *FiniteElementSpace::GetElementRestriction(
ElementDofOrdering e_ordering) const
{
// Check if we have a discontinuous space using the FE collection:
const L2_FECollection *dg_space = dynamic_cast<const L2_FECollection*>(fec);
if (dg_space)
{
if (L2E_nat.Ptr() == NULL)
{
L2E_nat.Reset(new L2ElementRestriction(*this));
}
return L2E_nat.Ptr();
}
if (e_ordering == ElementDofOrdering::LEXICOGRAPHIC)
{
if (L2E_lex.Ptr() == NULL)
{
L2E_lex.Reset(new ElementRestriction(*this, e_ordering));
}
return L2E_lex.Ptr();
}
// e_ordering == ElementDofOrdering::NATIVE
if (L2E_nat.Ptr() == NULL)
{
L2E_nat.Reset(new ElementRestriction(*this, e_ordering));
}
return L2E_nat.Ptr();
}
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
const IntegrationRule &ir) const
{
for (int i = 0; i < E2Q_array.Size(); i++)
{
const QuadratureInterpolator *qi = E2Q_array[i];
if (qi->IntRule == &ir) { return qi; }
}
QuadratureInterpolator *qi = new QuadratureInterpolator(*this, ir);
E2Q_array.Append(qi);
return qi;
}
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
const QuadratureSpace &qs) const
{
for (int i = 0; i < E2Q_array.Size(); i++)
{
const QuadratureInterpolator *qi = E2Q_array[i];
if (qi->qspace == &qs) { return qi; }
}
QuadratureInterpolator *qi = new QuadratureInterpolator(*this, qs);
E2Q_array.Append(qi);
return qi;
}
SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
const int coarse_ndofs, const Table &coarse_elem_dof,
const DenseTensor localP[]) const
@@ -951,7 +850,7 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
const DenseTensor &pmats = rtrans.point_matrices[geom];
const DenseTensor &pmats = rtrans.GetPointMatrices(geom);
int nmat = pmats.SizeK();
int ldof = fe->GetDof(); // assuming the same FE everywhere
@@ -990,9 +889,7 @@ FiniteElementSpace::RefinementOperator::RefinementOperator
: fespace(fespace)
, old_elem_dof(old_elem_dof)
{
const Mesh* mesh = fespace->GetMesh();
MFEM_VERIFY(mesh->ReduceInt(fespace->GetNDofs()) >=
mesh->ReduceInt(old_ndofs),
MFEM_VERIFY(fespace->GetNDofs() >= old_ndofs,
"Previous space is not coarser.");
width = old_ndofs * fespace->GetVDim();
@@ -1102,7 +999,7 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator(
f_fes->fec->FiniteElementForGeometry(geom);
const FiniteElement *coarse_fe =
c_fes->fec->FiniteElementForGeometry(geom);
const DenseTensor &pmats = rtrans.point_matrices[geom];
const DenseTensor &pmats = rtrans.GetPointMatrices(geom);
lP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), pmats.SizeK());
lM.SetSize(fine_fe->GetDof(), fine_fe->GetDof(), pmats.SizeK());
@@ -1218,7 +1115,7 @@ void FiniteElementSpace::GetLocalDerefinementMatrices(Geometry::Type geom,
const CoarseFineTransformations &dtrans =
mesh->ncmesh->GetDerefinementTransforms();
const DenseTensor &pmats = dtrans.point_matrices[geom];
const DenseTensor &pmats = dtrans.GetPointMatrices(geom);
const int nmat = pmats.SizeK();
const int ldof = fe->GetDof();
@@ -1323,7 +1220,7 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
coarse_fes.fec->FiniteElementForGeometry(geom);
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
const DenseTensor &pmats = rtrans.point_matrices[geom];
const DenseTensor &pmats = rtrans.GetPointMatrices(geom);
int nmat = pmats.SizeK();
@@ -1416,26 +1313,31 @@ void FiniteElementSpace::UpdateNURBS()
void FiniteElementSpace::Construct()
{
// This method should be used only for non-NURBS spaces.
MFEM_VERIFY(!NURBSext, "internal error");
MFEM_ASSERT(!NURBSext, "internal error");
elem_dof = NULL;
bdrElem_dof = NULL;
nvdofs = mesh->GetNV() * fec->DofForGeometry(Geometry::POINT);
if ( mesh->Dimension() > 1 )
{
nedofs = mesh->GetNEdges() * fec->DofForGeometry(Geometry::SEGMENT);
}
else
{
nedofs = 0;
}
ndofs = 0;
nedofs = nfdofs = nbdofs = 0;
nfdofs = 0;
nbdofs = 0;
bdofs = NULL;
fdofs = NULL;
cP = NULL;
cR = NULL;
cP_is_set = false;
// 'Th' is initialized/destroyed before this method is called.
nvdofs = mesh->GetNV() * fec->DofForGeometry(Geometry::POINT);
if (mesh->Dimension() > 1)
{
nedofs = mesh->GetNEdges() * fec->DofForGeometry(Geometry::SEGMENT);
}
// Th is initialized/destroyed before this method is called.
if (mesh->GetNFaces() > 0)
{
@@ -1467,7 +1369,8 @@ void FiniteElementSpace::Construct()
bdofs[0] = 0;
for (int i = 0; i < mesh->GetNE(); i++)
{
nbdofs += fec->DofForGeometry(mesh->GetElementBaseGeometry(i));
Geometry::Type geom = mesh->GetElementBaseGeometry(i);
nbdofs += fec->DofForGeometry(geom);
bdofs[i+1] = nbdofs;
}
}
@@ -1478,7 +1381,7 @@ void FiniteElementSpace::Construct()
// later.
}
void FiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
void FiniteElementSpace::GetElementDofs (int i, Array<int> &dofs) const
{
if (elem_dof)
{
@@ -1582,10 +1485,6 @@ void FiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
const FiniteElement *FiniteElementSpace::GetFE(int i) const
{
if (i < 0 || !mesh->GetNE()) { return NULL; }
MFEM_VERIFY(i < mesh->GetNE(),
"Invalid element id " << i << ", maximum allowed " << mesh->GetNE()-1);
const FiniteElement *FE =
fec->FiniteElementForGeometry(mesh->GetElementBaseGeometry(i));
@@ -1890,13 +1789,6 @@ void FiniteElementSpace::Destroy()
delete cR;
delete cP;
Th.Clear();
L2E_nat.Clear();
L2E_lex.Clear();
for (int i = 0; i < E2Q_array.Size(); i++)
{
delete E2Q_array[i];
}
E2Q_array.SetSize(0);
dof_elem_array.DeleteAll();
dof_ldof_array.DeleteAll();
@@ -2458,7 +2350,8 @@ const Operator &InterpolationGridTransfer::BackwardOperator()
return *B.Ptr();
}
// Construct B, if not set, define a suitable mass_integ
// Construct B
// If not set, define a suitable mass_integ
if (!mass_integ && ran_fes.GetNE() > 0)
{
const FiniteElement *f_fe_0 = ran_fes.GetFE(0);
@@ -2555,7 +2448,7 @@ L2ProjectionGridTransfer::L2Projection::L2Projection(
Vector shape_lor(ndof_lor);
const Geometry::Type geom = fe_ho->GetGeomType();
const DenseTensor &pmats = cf_tr.point_matrices[geom];
const DenseTensor &pmats = cf_tr.GetPointMatrices(geom);
emb_tr.SetIdentityTransformation(geom);
for (int iho=0; iho<nel_ho; ++iho)
@@ -2578,7 +2471,7 @@ L2ProjectionGridTransfer::L2Projection::L2Projection(
// Create the transformation that embeds the fine low-order element
// within the coarse high-order element in reference space
emb_tr.GetPointMat() = pmats(cf_tr.embeddings[ilor].matrix);
emb_tr.GetPointMat() = pmats(iref);
emb_tr.FinalizeTransformation();
int order = fe_lor->GetOrder() + fe_ho->GetOrder() + el_tr->OrderW();
@@ -2621,7 +2514,7 @@ void L2ProjectionGridTransfer::L2Projection::Mult(
fes_ho.GetElementVDofs(iho, vdofs);
x.GetSubVector(vdofs, xel_mat.GetData());
mfem::Mult(R(iho), xel_mat, yel_mat);
// Place result correctly into the low-order vector
// Place result correctly into low-order vector
for (int iref=0; iref<nref; ++iref)
{
int ilor = ho2lor.GetRow(iho)[iref];
@@ -2679,554 +2572,4 @@ const Operator &L2ProjectionGridTransfer::BackwardOperator()
return *B;
}
L2ElementRestriction::L2ElementRestriction(const FiniteElementSpace &fes)
: ne(fes.GetNE()),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
ndof(ne > 0 ? fes.GetFE(0)->GetDof() : 0)
{
height = vdim*ne*ndof;
width = vdim*ne*ndof;
}
void L2ElementRestriction::Mult(const Vector &x, Vector &y) const
{
for (int iel=0; iel<ne; ++iel)
{
for (int vd=0; vd<vdim; ++vd)
{
for (int idof=0; idof<ndof; ++idof)
{
// E-vector dimensions (dofs, vdim, elements)
// L-vector dimensions: byVDIM: (vdim, dofs, element)
// byNODES: (dofs, elements, vdim)
int yidx = iel*vdim*ndof + vd*ndof + idof;
int xidx;
if (byvdim)
{
xidx = iel*ndof*vdim + idof*vdim + vd;
}
else
{
xidx = vd*ne*ndof + iel*ndof + idof;
}
y[yidx] = x[xidx];
}
}
}
}
void L2ElementRestriction::MultTranspose(const Vector &x, Vector &y) const
{
// Since this restriction is a permutation, the transpose is the inverse
for (int iel=0; iel<ne; ++iel)
{
for (int vd=0; vd<vdim; ++vd)
{
for (int idof=0; idof<ndof; ++idof)
{
// E-vector dimensions (dofs, vdim, elements)
// L-vector dimensions: byVDIM: (vdim, dofs, element)
// byNODES: (dofs, elements, vdim)
int xidx = iel*vdim*ndof + vd*ndof + idof;
int yidx;
if (byvdim)
{
yidx = iel*ndof*vdim + idof*vdim + vd;
}
else
{
yidx = vd*ne*ndof + iel*ndof + idof;
}
y[yidx] = x[xidx];
}
}
}
}
ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
ElementDofOrdering e_ordering)
: fes(f),
ne(fes.GetNE()),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
ndofs(fes.GetNDofs()),
dof(ne > 0 ? fes.GetFE(0)->GetDof() : 0),
nedofs(ne*dof),
offsets(ndofs+1),
indices(ne*dof)
{
// Assuming all finite elements are the same.
height = vdim*ne*dof;
width = fes.GetVSize();
const bool dof_reorder = (e_ordering == ElementDofOrdering::LEXICOGRAPHIC);
const int *dof_map = NULL;
if (dof_reorder && ne > 0)
{
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 suitable for lexicographic ordering");
}
const FiniteElement *fe = fes.GetFE(0);
const TensorBasisElement* el =
dynamic_cast<const TensorBasisElement*>(fe);
const Array<int> &fe_dof_map = el->GetDofMap();
MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
dof_map = fe_dof_map.GetData();
}
const Table& e2dTable = fes.GetElementToDofTable();
const int* elementMap = e2dTable.GetJ();
// We will 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_reorder)?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 ElementRestriction::Mult(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
const bool t = byvdim;
auto d_offsets = offsets.Read();
auto d_indices = indices.Read();
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.Write(), nd, vd, ne);
MFEM_FORALL(i, ndofs,
{
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(idx_j % nd, c, idx_j / nd) = dofValue;
}
}
});
}
void ElementRestriction::MultTranspose(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
const bool t = byvdim;
auto d_offsets = offsets.Read();
auto d_indices = indices.Read();
auto d_x = Reshape(x.Read(), nd, vd, ne);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
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(idx_j % nd, c, idx_j / nd);
}
d_y(t?c:i,t?i:c) = dofValue;
}
});
}
QuadratureInterpolator::QuadratureInterpolator(const FiniteElementSpace &fes,
const IntegrationRule &ir)
{
fespace = &fes;
qspace = NULL;
IntRule = &ir;
use_tensor_products = true; // not implemented yet (not used)
if (fespace->GetNE() == 0) { return; }
const FiniteElement *fe = fespace->GetFE(0);
MFEM_VERIFY(dynamic_cast<const ScalarFiniteElement*>(fe) != NULL,
"Only scalar finite elements are supported");
}
QuadratureInterpolator::QuadratureInterpolator(const FiniteElementSpace &fes,
const QuadratureSpace &qs)
{
fespace = &fes;
qspace = &qs;
IntRule = NULL;
use_tensor_products = true; // not implemented yet (not used)
if (fespace->GetNE() == 0) { return; }
const FiniteElement *fe = fespace->GetFE(0);
MFEM_VERIFY(dynamic_cast<const ScalarFiniteElement*>(fe) != NULL,
"Only scalar finite elements are supported");
}
template<const int T_VDIM, const int T_ND, const int T_NQ>
void QuadratureInterpolator::Eval2D(
const int NE,
const int vdim,
const DofToQuad &maps,
const Vector &e_vec,
Vector &q_val,
Vector &q_der,
Vector &q_det,
const int eval_flags)
{
const int nd = maps.ndof;
const int nq = maps.nqpt;
const int ND = T_ND ? T_ND : nd;
const int NQ = T_NQ ? T_NQ : nq;
const int VDIM = T_VDIM ? T_VDIM : vdim;
MFEM_VERIFY(ND <= MAX_ND2D, "");
MFEM_VERIFY(NQ <= MAX_NQ2D, "");
MFEM_VERIFY(VDIM == 2 || !(eval_flags & DETERMINANTS), "");
auto B = Reshape(maps.B.Read(), NQ, ND);
auto G = Reshape(maps.G.Read(), NQ, 2, ND);
auto E = Reshape(e_vec.Read(), ND, VDIM, NE);
auto val = Reshape(q_val.Write(), NQ, VDIM, NE);
auto der = Reshape(q_der.Write(), NQ, VDIM, 2, NE);
auto det = Reshape(q_det.Write(), NQ, NE);
MFEM_FORALL(e, NE,
{
const int ND = T_ND ? T_ND : nd;
const int NQ = T_NQ ? T_NQ : nq;
const int VDIM = T_VDIM ? T_VDIM : vdim;
constexpr int max_ND = T_ND ? T_ND : MAX_ND2D;
constexpr int max_VDIM = T_VDIM ? T_VDIM : MAX_VDIM2D;
double s_E[max_VDIM*max_ND];
for (int d = 0; d < ND; d++)
{
for (int c = 0; c < VDIM; c++)
{
s_E[c+d*VDIM] = E(d,c,e);
}
}
for (int q = 0; q < NQ; ++q)
{
if (eval_flags & VALUES)
{
double ed[max_VDIM];
for (int c = 0; c < VDIM; c++) { ed[c] = 0.0; }
for (int d = 0; d < ND; ++d)
{
const double b = B(q,d);
for (int c = 0; c < VDIM; c++) { ed[c] += b*s_E[c+d*VDIM]; }
}
for (int c = 0; c < VDIM; c++) { val(q,c,e) = ed[c]; }
}
if ((eval_flags & DERIVATIVES) || (eval_flags & DETERMINANTS))
{
// use MAX_VDIM2D to avoid "subscript out of range" warnings
double D[MAX_VDIM2D*2];
for (int i = 0; i < 2*VDIM; i++) { D[i] = 0.0; }
for (int d = 0; d < ND; ++d)
{
const double wx = G(q,0,d);
const double wy = G(q,1,d);
for (int c = 0; c < VDIM; c++)
{
double s_e = s_E[c+d*VDIM];
D[c+VDIM*0] += s_e * wx;
D[c+VDIM*1] += s_e * wy;
}
}
if (eval_flags & DERIVATIVES)
{
for (int c = 0; c < VDIM; c++)
{
der(q,c,0,e) = D[c+VDIM*0];
der(q,c,1,e) = D[c+VDIM*1];
}
}
if (VDIM == 2 && (eval_flags & DETERMINANTS))
{
// The check (VDIM == 2) should eliminate this block when VDIM is
// known at compile time and (VDIM != 2).
det(q,e) = D[0]*D[3] - D[1]*D[2];
}
}
}
});
}
template<const int T_VDIM, const int T_ND, const int T_NQ>
void QuadratureInterpolator::Eval3D(
const int NE,
const int vdim,
const DofToQuad &maps,
const Vector &e_vec,
Vector &q_val,
Vector &q_der,
Vector &q_det,
const int eval_flags)
{
const int nd = maps.ndof;
const int nq = maps.nqpt;
const int ND = T_ND ? T_ND : nd;
const int NQ = T_NQ ? T_NQ : nq;
const int VDIM = T_VDIM ? T_VDIM : vdim;
MFEM_VERIFY(ND <= MAX_ND3D, "");
MFEM_VERIFY(NQ <= MAX_NQ3D, "");
MFEM_VERIFY(VDIM == 3 || !(eval_flags & DETERMINANTS), "");
auto B = Reshape(maps.B.Read(), NQ, ND);
auto G = Reshape(maps.G.Read(), NQ, 3, ND);
auto E = Reshape(e_vec.Read(), ND, VDIM, NE);
auto val = Reshape(q_val.Write(), NQ, VDIM, NE);
auto der = Reshape(q_der.Write(), NQ, VDIM, 3, NE);
auto det = Reshape(q_det.Write(), NQ, NE);
MFEM_FORALL(e, NE,
{
const int ND = T_ND ? T_ND : nd;
const int NQ = T_NQ ? T_NQ : nq;
const int VDIM = T_VDIM ? T_VDIM : vdim;
constexpr int max_ND = T_ND ? T_ND : MAX_ND3D;
constexpr int max_VDIM = T_VDIM ? T_VDIM : MAX_VDIM3D;
double s_E[max_VDIM*max_ND];
for (int d = 0; d < ND; d++)
{
for (int c = 0; c < VDIM; c++)
{
s_E[c+d*VDIM] = E(d,c,e);
}
}
for (int q = 0; q < NQ; ++q)
{
if (eval_flags & VALUES)
{
double ed[max_VDIM];
for (int c = 0; c < VDIM; c++) { ed[c] = 0.0; }
for (int d = 0; d < ND; ++d)
{
const double b = B(q,d);
for (int c = 0; c < VDIM; c++) { ed[c] += b*s_E[c+d*VDIM]; }
}
for (int c = 0; c < VDIM; c++) { val(q,c,e) = ed[c]; }
}
if ((eval_flags & DERIVATIVES) || (eval_flags & DETERMINANTS))
{
// use MAX_VDIM3D to avoid "subscript out of range" warnings
double D[MAX_VDIM3D*3];
for (int i = 0; i < 3*VDIM; i++) { D[i] = 0.0; }
for (int d = 0; d < ND; ++d)
{
const double wx = G(q,0,d);
const double wy = G(q,1,d);
const double wz = G(q,2,d);
for (int c = 0; c < VDIM; c++)
{
double s_e = s_E[c+d*VDIM];
D[c+VDIM*0] += s_e * wx;
D[c+VDIM*1] += s_e * wy;
D[c+VDIM*2] += s_e * wz;
}
}
if (eval_flags & DERIVATIVES)
{
for (int c = 0; c < VDIM; c++)
{
der(q,c,0,e) = D[c+VDIM*0];
der(q,c,1,e) = D[c+VDIM*1];
der(q,c,2,e) = D[c+VDIM*2];
}
}
if (VDIM == 3 && (eval_flags & DETERMINANTS))
{
// The check (VDIM == 3) should eliminate this block when VDIM is
// known at compile time and (VDIM != 3).
det(q,e) = D[0] * (D[4] * D[8] - D[5] * D[7]) +
D[3] * (D[2] * D[7] - D[1] * D[8]) +
D[6] * (D[1] * D[5] - D[2] * D[4]);
}
}
}
});
}
void QuadratureInterpolator::Mult(
const Vector &e_vec, unsigned eval_flags,
Vector &q_val, Vector &q_der, Vector &q_det) const
{
const int ne = fespace->GetNE();
if (ne == 0) { return; }
const int vdim = fespace->GetVDim();
const int dim = fespace->GetMesh()->Dimension();
const FiniteElement *fe = fespace->GetFE(0);
const IntegrationRule *ir =
IntRule ? IntRule : &qspace->GetElementIntRule(0);
const DofToQuad &maps = fe->GetDofToQuad(*ir, DofToQuad::FULL);
const int nd = maps.ndof;
const int nq = maps.nqpt;
void (*eval_func)(
const int NE,
const int vdim,
const DofToQuad &maps,
const Vector &e_vec,
Vector &q_val,
Vector &q_der,
Vector &q_det,
const int eval_flags) = NULL;
if (vdim == 1)
{
if (dim == 2)
{
switch (100*nd + nq)
{
// Q0
case 101: eval_func = &Eval2D<1,1,1>; break;
case 104: eval_func = &Eval2D<1,1,4>; break;
// Q1
case 404: eval_func = &Eval2D<1,4,4>; break;
case 409: eval_func = &Eval2D<1,4,9>; break;
// Q2
case 909: eval_func = &Eval2D<1,9,9>; break;
case 916: eval_func = &Eval2D<1,9,16>; break;
// Q3
case 1616: eval_func = &Eval2D<1,16,16>; break;
case 1625: eval_func = &Eval2D<1,16,25>; break;
case 1636: eval_func = &Eval2D<1,16,36>; break;
// Q4
case 2525: eval_func = &Eval2D<1,25,25>; break;
case 2536: eval_func = &Eval2D<1,25,36>; break;
case 2549: eval_func = &Eval2D<1,25,49>; break;
case 2564: eval_func = &Eval2D<1,25,64>; break;
}
if (nq >= 100 || !eval_func)
{
eval_func = &Eval2D<1>;
}
}
else if (dim == 3)
{
switch (1000*nd + nq)
{
// Q0
case 1001: eval_func = &Eval3D<1,1,1>; break;
case 1008: eval_func = &Eval3D<1,1,8>; break;
// Q1
case 8008: eval_func = &Eval3D<1,8,8>; break;
case 8027: eval_func = &Eval3D<1,8,27>; break;
// Q2
case 27027: eval_func = &Eval3D<1,27,27>; break;
case 27064: eval_func = &Eval3D<1,27,64>; break;
// Q3
case 64064: eval_func = &Eval3D<1,64,64>; break;
case 64125: eval_func = &Eval3D<1,64,125>; break;
case 64216: eval_func = &Eval3D<1,64,216>; break;
// Q4
case 125125: eval_func = &Eval3D<1,125,125>; break;
case 125216: eval_func = &Eval3D<1,125,216>; break;
}
if (nq >= 1000 || !eval_func)
{
eval_func = &Eval3D<1>;
}
}
}
else if (vdim == dim)
{
if (dim == 2)
{
switch (100*nd + nq)
{
// Q1
case 404: eval_func = &Eval2D<2,4,4>; break;
case 409: eval_func = &Eval2D<2,4,9>; break;
// Q2
case 909: eval_func = &Eval2D<2,9,9>; break;
case 916: eval_func = &Eval2D<2,9,16>; break;
// Q3
case 1616: eval_func = &Eval2D<2,16,16>; break;
case 1625: eval_func = &Eval2D<2,16,25>; break;
case 1636: eval_func = &Eval2D<2,16,36>; break;
// Q4
case 2525: eval_func = &Eval2D<2,25,25>; break;
case 2536: eval_func = &Eval2D<2,25,36>; break;
case 2549: eval_func = &Eval2D<2,25,49>; break;
case 2564: eval_func = &Eval2D<2,25,64>; break;
}
if (nq >= 100 || !eval_func)
{
eval_func = &Eval2D<2>;
}
}
else if (dim == 3)
{
switch (1000*nd + nq)
{
// Q1
case 8008: eval_func = &Eval3D<3,8,8>; break;
case 8027: eval_func = &Eval3D<3,8,27>; break;
// Q2
case 27027: eval_func = &Eval3D<3,27,27>; break;
case 27064: eval_func = &Eval3D<3,27,64>; break;
// Q3
case 64064: eval_func = &Eval3D<3,64,64>; break;
case 64125: eval_func = &Eval3D<3,64,125>; break;
case 64216: eval_func = &Eval3D<3,64,216>; break;
// Q4
case 125125: eval_func = &Eval3D<3,125,125>; break;
case 125216: eval_func = &Eval3D<3,125,216>; break;
}
if (nq >= 1000 || !eval_func)
{
eval_func = &Eval3D<3>;
}
}
}
if (eval_func)
{
eval_func(ne, vdim, maps, e_vec, q_val, q_der, q_det, eval_flags);
}
else
{
MFEM_ABORT("case not supported yet");
}
}
void QuadratureInterpolator::MultTranspose(
unsigned eval_flags, const Vector &q_val, const Vector &q_der,
Vector &e_vec) const
{
MFEM_ABORT("this method is not implemented yet");
}
} // namespace mfem
-204
View File
@@ -59,25 +59,9 @@ Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
}
/// Constants describing the possible orderings of the DOFs in one element.
enum class ElementDofOrdering
{
/// Native ordering as defined by the FiniteElement.
/** This ordering can be used by tensor-product elements when the
interpolation from the DOFs to quadrature points does not use the
tensor-product structure. */
NATIVE,
/// Lexicographic ordering for tensor-product FiniteElements.
/** This ordering can be used only with tensor-product elements. */
LEXICOGRAPHIC
};
// Forward declarations
class NURBSExtension;
class BilinearFormIntegrator;
class QuadratureSpace;
class QuadratureInterpolator;
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
@@ -126,11 +110,6 @@ protected:
/// Transformation to apply to GridFunctions after space Update().
OperatorHandle Th;
/// The element restriction operators, see GetElementRestriction().
mutable OperatorHandle L2E_nat, L2E_lex;
mutable Array<QuadratureInterpolator*> E2Q_array;
long sequence; // should match Mesh::GetSequence
void UpdateNURBS();
@@ -146,8 +125,6 @@ protected:
/// Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
void GetEntityDofs(int entity, int index, Array<int> &dofs) const;
// Get degenerate face DOFs: see explanation in method implementation.
void GetDegenerateFaceDofs(int index, Array<int> &dofs) const;
/// Calculate the cP and cR matrices for a nonconforming mesh.
void BuildConformingInterpolation() const;
@@ -158,7 +135,6 @@ protected:
static bool DofFinalizable(int dof, const Array<bool>& finalized,
const SparseMatrix& deps);
/// Replicate 'mat' in the vector dimension, according to vdim ordering mode.
void MakeVDimMatrix(SparseMatrix &mat) const;
/// GridFunction interpolation operator applicable after mesh refinement.
@@ -281,61 +257,14 @@ public:
bool Conforming() const { return mesh->Conforming(); }
bool Nonconforming() const { return mesh->Nonconforming(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
const SparseMatrix *GetConformingProlongation() const;
/// The returned SparseMatrix is owned by the FiniteElementSpace.
const SparseMatrix *GetConformingRestriction() const;
/// The returned Operator is owned by the FiniteElementSpace.
virtual const Operator *GetProlongationMatrix() const
{ return GetConformingProlongation(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ return GetConformingRestriction(); }
/// Return an Operator that converts L-vectors to E-vectors.
/** An L-vector is a vector of size GetVSize() which is the same size as a
GridFunction. An E-vector represents the element-wise discontinuous
version of the FE space.
The layout of the E-vector is: ND x VDIM x NE, where ND is the number of
degrees of freedom, VDIM is the vector dimension of the FE space, and NE
is the number of the mesh elements.
The parameter @a e_ordering describes how the local DOFs in each element
should be ordered, see ElementDofOrdering.
For discontinuous spaces, the element restriction corresponds to a
permutation of the degrees of freedom, implemented by the
L2ElementRestriction class.
The returned Operator is owned by the FiniteElementSpace. */
const Operator *GetElementRestriction(ElementDofOrdering e_ordering) const;
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
quadrature point values and/or derivatives (Q-vectors). */
/** An E-vector represents the element-wise discontinuous version of the FE
space and can be obtained, for example, from a GridFunction using the
Operator returned by GetElementRestriction().
All elements will use the same IntegrationRule, @a ir as the target
quadrature points. */
const QuadratureInterpolator *GetQuadratureInterpolator(
const IntegrationRule &ir) const;
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
quadrature point values and/or derivatives (Q-vectors). */
/** An E-vector represents the element-wise discontinuous version of the FE
space and can be obtained, for example, from a GridFunction using the
Operator returned by GetElementRestriction().
The target quadrature points in the elements are described by the given
QuadratureSpace, @a qs. */
const QuadratureInterpolator *GetQuadratureInterpolator(
const QuadratureSpace &qs) const;
/// Returns vector dimension.
inline int GetVDim() const { return vdim; }
@@ -877,139 +806,6 @@ public:
virtual const Operator &BackwardOperator();
};
/// Operator that converts FiniteElementSpace L-vectors to E-vectors.
/** Objects of this type are typically created and owned by FiniteElementSpace
objects, see FiniteElementSpace::GetElementRestriction(). */
class ElementRestriction : public Operator
{
protected:
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:
ElementRestriction(const FiniteElementSpace&, ElementDofOrdering);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
};
/// Operator that converts L2 FiniteElementSpace L-vectors to E-vectors.
/** Objects of this type are typically created and owned by FiniteElementSpace
objects, see FiniteElementSpace::GetElementRestriction(). L-vectors
corresponding to grid functions in L2 finite element spaces differ from
E-vectors only in the ordering of the degrees of freedom. */
class L2ElementRestriction : public Operator
{
const int ne;
const int vdim;
const bool byvdim;
const int ndof;
public:
L2ElementRestriction(const FiniteElementSpace&);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
};
/** @brief A class that performs interpolation from an E-vector to quadrature
point values and/or derivatives (Q-vectors). */
/** An E-vector represents the element-wise discontinuous version of the FE
space and can be obtained, for example, from a GridFunction using the
Operator returned by FiniteElementSpace::GetElementRestriction().
The target quadrature points in the elements can be described either by an
IntegrationRule (all mesh elements must be of the same type in this case) or
by a QuadratureSpace. */
class QuadratureInterpolator
{
protected:
friend class FiniteElementSpace; // Needs access to qspace and IntRule
const FiniteElementSpace *fespace; ///< Not owned
const QuadratureSpace *qspace; ///< Not owned
const IntegrationRule *IntRule; ///< Not owned
mutable bool use_tensor_products;
static const int MAX_NQ2D = 100;
static const int MAX_ND2D = 100;
static const int MAX_VDIM2D = 2;
static const int MAX_NQ3D = 1000;
static const int MAX_ND3D = 1000;
static const int MAX_VDIM3D = 3;
public:
enum EvalFlags
{
VALUES = 1 << 0, ///< Evaluate the values at quadrature points
DERIVATIVES = 1 << 1, ///< Evaluate the derivatives at quadrature points
/** @brief Assuming the derivative at quadrature points form a matrix,
this flag can be used to compute and store their determinants. This
flag can only be used in Mult(). */
DETERMINANTS = 1 << 2
};
QuadratureInterpolator(const FiniteElementSpace &fes,
const IntegrationRule &ir);
QuadratureInterpolator(const FiniteElementSpace &fes,
const QuadratureSpace &qs);
/** @brief Disable the use of tensor product evaluations, for tensor-product
elements, e.g. quads and hexes. */
/** Currently, tensor product evaluations are not implemented and this method
has no effect. */
void DisableTensorProducts(bool disable = true) const
{ use_tensor_products = !disable; }
/// Interpolate the E-vector @a e_vec to quadrature points.
/** The @a eval_flags are a bitwise mask of constants from the EvalFlags
enumeration. When the VALUES flag is set, the values at quadrature points
are computed and stored in the Vector @a q_val. Similarly, when the flag
DERIVATIVES is set, the derivatives are computed and stored in @a q_der.
When the DETERMINANTS flags is set, it is assumed that the derivatives
form a matrix at each quadrature point (i.e. the associated
FiniteElementSpace is a vector space) and their determinants are computed
and stored in @a q_det. */
void Mult(const Vector &e_vec, unsigned eval_flags,
Vector &q_val, Vector &q_der, Vector &q_det) const;
/// Perform the transpose operation of Mult(). (TODO)
void MultTranspose(unsigned eval_flags, const Vector &q_val,
const Vector &q_der, Vector &e_vec) const;
// Compute kernels follow (cannot be private or protected with nvcc)
/// Template compute kernel for 2D.
template<const int T_VDIM = 0, const int T_ND = 0, const int T_NQ = 0>
static void Eval2D(const int NE,
const int vdim,
const DofToQuad &maps,
const Vector &e_vec,
Vector &q_val,
Vector &q_der,
Vector &q_det,
const int eval_flags);
/// Template compute kernel for 3D.
template<const int T_VDIM = 0, const int T_ND = 0, const int T_NQ = 0>
static void Eval3D(const int NE,
const int vdim,
const DofToQuad &maps,
const Vector &e_vec,
Vector &q_val,
Vector &q_der,
Vector &q_det,
const int eval_flags);
};
}
#endif
+15 -21
View File
@@ -30,9 +30,6 @@ using namespace std;
GridFunction::GridFunction(Mesh *m, std::istream &input)
: Vector()
{
// Grid functions are stored on the device
UseDevice(true);
fes = new FiniteElementSpace;
fec = fes->Load(m, input);
@@ -63,8 +60,6 @@ GridFunction::GridFunction(Mesh *m, std::istream &input)
GridFunction::GridFunction(Mesh *m, GridFunction *gf_array[], int num_pieces)
{
UseDevice(true);
// all GridFunctions must have the same FE collection, vdim, ordering
int vdim, ordering;
@@ -168,7 +163,6 @@ void GridFunction::Update()
Vector old_data;
old_data.Swap(*this);
SetSize(T->Height());
UseDevice(true);
T->Mult(old_data, *this);
}
else
@@ -198,9 +192,7 @@ void GridFunction::MakeRef(FiniteElementSpace *f, Vector &v, int v_offset)
MFEM_ASSERT(v.Size() >= v_offset + f->GetVSize(), "");
if (f != fes) { Destroy(); }
fes = f;
v.UseDevice(true);
NewMemoryAndSize(Memory<double>(v.GetMemory(), v_offset, fes->GetVSize()),
fes->GetVSize(), true);
NewDataAndSize((double *)v + v_offset, fes->GetVSize());
sequence = fes->GetSequence();
}
@@ -223,16 +215,13 @@ void GridFunction::MakeTRef(FiniteElementSpace *f, Vector &tv, int tv_offset)
if (!f->GetProlongationMatrix())
{
MakeRef(f, tv, tv_offset);
t_vec.NewMemoryAndSize(data, size, false);
t_vec.NewDataAndSize(data, size);
}
else
{
MFEM_ASSERT(tv.Size() >= tv_offset + f->GetTrueVSize(), "");
SetSpace(f); // works in parallel
tv.UseDevice(true);
const int tv_size = f->GetTrueVSize();
t_vec.NewMemoryAndSize(Memory<double>(tv.GetMemory(), tv_offset, tv_size),
tv_size, true);
t_vec.NewDataAndSize(&tv(tv_offset), f->GetTrueVSize());
}
}
@@ -313,7 +302,7 @@ int GridFunction::VectorDim() const
{
fe = fes->GetFE(0);
}
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
if (fe->GetRangeType() == FiniteElement::SCALAR)
{
return fes->GetVDim();
}
@@ -326,7 +315,7 @@ void GridFunction::GetTrueDofs(Vector &tv) const
if (!R)
{
// R is identity -> make tv a reference to *this
tv.NewDataAndSize(const_cast<double*>((const double*)data), size);
tv.NewDataAndSize(data, size);
}
else
{
@@ -1378,7 +1367,7 @@ void GridFunction::AccumulateAndCountBdrValues(
if (vdofs.Size() == 0) { continue; }
transf = mesh->GetEdgeTransformation(edge);
transf->Attribute = -1; // TODO: set the boundary attribute
transf->Attribute = -1; // FIXME: set the boundary attribute
fe = fes->GetEdgeElement(edge);
if (!vcoeff)
{
@@ -1482,7 +1471,7 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
if (dofs.Size() == 0) { continue; }
T = mesh->GetEdgeTransformation(edge);
T->Attribute = -1; // TODO: set the boundary attribute
T->Attribute = -1; // FIXME: set the boundary attribute
fe = fes->GetEdgeElement(edge);
lvec.SetSize(fe->GetDof());
fe->Project(vcoeff, *T, lvec);
@@ -1716,7 +1705,6 @@ void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
Array<int> vdofs;
Vector vals;
HostWrite();
// maximal element attribute for each dof
dof_attr.SetSize(fes->GetVSize());
dof_attr = -1;
@@ -1788,7 +1776,6 @@ void GridFunction::ProjectBdrCoefficient(VectorCoefficient &vcoeff,
void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[], Array<int> &attr)
{
Array<int> values_counter;
this->HostReadWrite();
AccumulateAndCountBdrValues(coeff, NULL, attr, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
#ifdef MFEM_DEBUG
@@ -2323,7 +2310,14 @@ double GridFunction::ComputeLpError(const double p, Coefficient &exsol,
else
{
int intorder = 2*fe->GetOrder() + 1; // <----------
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
if (fe->Space() == FunctionSpace::SBPk)
{
ir = &(fe->GetNodes());
}
else
{
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
}
}
GetValues(i, *ir, vals);
T = fes->GetElementTransformation(i);
+7 -14
View File
@@ -68,16 +68,15 @@ protected:
public:
GridFunction() { fes = NULL; fec = NULL; sequence = 0; UseDevice(true); }
GridFunction() { fes = NULL; fec = NULL; sequence = 0; }
/// Copy constructor. The internal true-dof vector #t_vec is not copied.
GridFunction(const GridFunction &orig)
: Vector(orig), fes(orig.fes), fec(NULL), sequence(orig.sequence)
{ UseDevice(true); }
: Vector(orig), fes(orig.fes), fec(NULL), sequence(orig.sequence) { }
/// Construct a GridFunction associated with the FiniteElementSpace @a *f.
GridFunction(FiniteElementSpace *f) : Vector(f->GetVSize())
{ fes = f; fec = NULL; sequence = f->GetSequence(); UseDevice(true); }
{ fes = f; fec = NULL; sequence = f->GetSequence(); }
/// Construct a GridFunction using previously allocated array @a data.
/** The GridFunction does not assume ownership of @a data which is assumed to
@@ -85,9 +84,8 @@ public:
for externally allocated array, the pointer @a data can be NULL. The data
array can be replaced later using the method SetData().
*/
GridFunction(FiniteElementSpace *f, double *data)
: Vector(data, f->GetVSize())
{ fes = f; fec = NULL; sequence = f->GetSequence(); UseDevice(true); }
GridFunction(FiniteElementSpace *f, double *data) : Vector(data, f->GetVSize())
{ fes = f; fec = NULL; sequence = f->GetSequence(); }
/// Construct a GridFunction on the given Mesh, using the data from @a input.
/** The content of @a input should be in the format created by the method
@@ -126,7 +124,6 @@ public:
/// @brief Extract the true-dofs from the GridFunction. If all dofs are true,
/// then `tv` will be set to point to the data of `*this`.
/** @warning This method breaks const-ness when all dofs are true. */
void GetTrueDofs(Vector &tv) const;
/// Shortcut for calling GetTrueDofs() with GetTrueVector() as argument.
@@ -434,8 +431,6 @@ public:
/** The GridFunction is resized using the SetSize() method. */
virtual void SetSpace(FiniteElementSpace *f);
using Vector::MakeRef;
/** @brief Make the GridFunction reference external data on a new
FiniteElementSpace. */
/** This method changes the FiniteElementSpace associated with the
@@ -707,7 +702,7 @@ inline void QuadratureFunction::GetElementValues(int idx, Vector &values) const
const int s_offset = qspace->element_offsets[idx];
const int sl_size = qspace->element_offsets[idx+1] - s_offset;
values.SetSize(vdim*sl_size);
const double *q = data + vdim*s_offset;
double *q = data + vdim*s_offset;
for (int i = 0; i<values.Size(); i++)
{
values(i) = *(q++);
@@ -727,14 +722,12 @@ inline void QuadratureFunction::GetElementValues(int idx,
const int s_offset = qspace->element_offsets[idx];
const int sl_size = qspace->element_offsets[idx+1] - s_offset;
values.SetSize(vdim, sl_size);
const double *q = data + vdim*s_offset;
double *q = data + vdim*s_offset;
for (int j = 0; j<sl_size; j++)
{
for (int i = 0; i<vdim; i++)
{
values(i,j) = *(q++);
}
}
}
} // namespace mfem
-13
View File
@@ -78,19 +78,6 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
}
}
const Array<double> &IntegrationRule::GetWeights() const
{
if (weights.Size() != GetNPoints())
{
weights.SetSize(GetNPoints());
for (int i = 0; i < GetNPoints(); i++)
{
weights[i] = IntPoint(i).weight;
}
}
return weights;
}
void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
{
// for pow on older compilers
-8
View File
@@ -87,9 +87,6 @@ class IntegrationRule : public Array<IntegrationPoint>
private:
friend class IntegrationRules;
int Order;
/** @brief The quadrature weights gathered as a contiguous array. Created
by request with the method GetWeights(). */
mutable Array<double> weights;
/// Define n-simplex rule (triangle/tetrahedron for n=2/3) of order (2s+1)
void GrundmannMollerSimplexRule(int s, int n = 3);
@@ -242,11 +239,6 @@ public:
/// Returns a const reference to the i-th integration point
const IntegrationPoint &IntPoint(int i) const { return (*this)[i]; }
/// Return the quadrature weights in a contiguous array.
/** If a contiguous array is not required, the weights can be accessed with
a call like this: `IntPoint(i).weight`. */
const Array<double> &GetWeights() const;
/// Destroys an IntegrationRule object
~IntegrationRule() { }
};
-11
View File
@@ -19,9 +19,6 @@ namespace mfem
LinearForm::LinearForm(FiniteElementSpace *f, LinearForm *lf)
: Vector(f->GetVSize())
{
// Linear forms are stored on the device
UseDevice(true);
fes = f;
extern_lfs = 1;
@@ -86,10 +83,6 @@ void LinearForm::Assemble()
Vector::operator=(0.0);
// The above operation is executed on device because of UseDevice().
// The first use of AddElementVector() below will move it back to host
// because both 'vdofs' and 'elemvect' are on host.
if (dlfi.Size())
{
for (i = 0; i < fes -> GetNE(); i++)
@@ -138,11 +131,7 @@ void LinearForm::Assemble()
eltrans = fes -> GetBdrElementTransformation (i);
for (int k=0; k < blfi.Size(); k++)
{
if (blfi_marker[k] &&
(*blfi_marker[k])[bdr_attr-1] == 0) { continue; }
blfi[k]->AssembleRHSElementVect(*fes->GetBE(i), *eltrans, elemvect);
AddElementVector (vdofs, elemvect);
}
}
+2 -2
View File
@@ -64,7 +64,7 @@ public:
/// Creates linear form associated with FE space @a *f.
/** The pointer @a f is not owned by the newly constructed object. */
LinearForm(FiniteElementSpace *f) : Vector(f->GetVSize())
{ fes = f; extern_lfs = 0; UseDevice(true); }
{ fes = f; extern_lfs = 0; }
/** @brief Create a LinearForm on the FiniteElementSpace @a f, using the
same integrators as the LinearForm @a lf.
@@ -79,7 +79,7 @@ public:
/** The associated FiniteElementSpace can be set later using one of the
methods: Update(FiniteElementSpace *) or
Update(FiniteElementSpace *, Vector &, int). */
LinearForm() { fes = NULL; extern_lfs = 0; UseDevice(true); }
LinearForm() { fes = NULL; extern_lfs = 0; }
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
+19 -18
View File
@@ -36,9 +36,16 @@ void DomainLFIntegrator::AssembleRHSElementVect(const FiniteElement &el,
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
// ir = &IntRules.Get(el.GetGeomType(),
// oa * el.GetOrder() + ob + Tr.OrderW());
ir = &IntRules.Get(el.GetGeomType(), oa * el.GetOrder() + ob);
if (el.Space() == FunctionSpace::SBPk)
{
ir = &el.GetNodes();
}
else
{
// ir = &IntRules.Get(el.GetGeomType(),
// oa * el.GetOrder() + ob + Tr.OrderW());
ir = &IntRules.Get(el.GetGeomType(), oa * el.GetOrder() + ob);
}
}
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -181,7 +188,7 @@ void VectorDomainLFIntegrator::AssembleRHSElementVect(
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = 2*el.GetOrder();
int intorder = el.GetOrder() + 1;
ir = &IntRules.Get(el.GetGeomType(), intorder);
}
@@ -240,7 +247,7 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = 2*el.GetOrder();
int intorder = el.GetOrder() + 1;
ir = &IntRules.Get(el.GetGeomType(), intorder);
}
@@ -275,7 +282,7 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = 2*el.GetOrder();
int intorder = el.GetOrder() + 1;
ir = &IntRules.Get(Tr.FaceGeom, intorder);
}
@@ -350,6 +357,7 @@ void VectorFEDomainLFIntegrator::AssembleDeltaElementVect(
vshape.Mult(vec, elvect);
}
void VectorBoundaryFluxLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
@@ -396,26 +404,19 @@ void VectorFEBoundaryFluxLFIntegrator::AssembleRHSElementVect(
if (ir == NULL)
{
int intorder = 2*el.GetOrder(); // <----------
if (F == NULL)
{
intorder -= el.GetOrder() + 1;
}
ir = &IntRules.Get(el.GetGeomType(), intorder);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint (&ip);
double val = ip.weight*F.Eval(Tr, ip);
el.CalcShape(ip, shape);
double val = ip.weight;
if (F)
{
Tr.SetIntPoint (&ip);
val *= F->Eval(Tr, ip);
}
elvect.Add(val, shape);
add(elvect, val, shape, elvect);
}
}
+2 -3
View File
@@ -279,12 +279,11 @@ public:
class VectorFEBoundaryFluxLFIntegrator : public LinearFormIntegrator
{
private:
Coefficient *F;
Coefficient &F;
Vector shape;
public:
VectorFEBoundaryFluxLFIntegrator() : F(NULL) { }
VectorFEBoundaryFluxLFIntegrator(Coefficient &f) : F(&f) { }
VectorFEBoundaryFluxLFIntegrator(Coefficient &f) : F(f) { }
virtual void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
+3 -72
View File
@@ -65,8 +65,6 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
Vector el_x;
const FiniteElement *fe;
ElementTransformation *T;
Mesh *mesh = fes->GetMesh();
double energy = 0.0;
if (dnfi.Size())
@@ -86,81 +84,14 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
if (fnfi.Size())
{
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
Array<int> vdofs2;
for (int i = 0; i < mesh->GetNumFaces(); i++)
{
tr = mesh->GetInteriorFaceTransformations(i);
if (tr != NULL)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
fes->GetElementVDofs(tr->Elem2No, vdofs2);
vdofs.Append (vdofs2);
x.GetSubVector(vdofs, el_x);
fe1 = fes->GetFE(tr->Elem1No);
fe2 = fes->GetFE(tr->Elem2No);
for (int k = 0; k < fnfi.Size(); k++)
{
energy += fnfi[k]->GetFaceEnergy(*fe1, *fe2, *tr, el_x);
}
}
}
MFEM_ABORT("TODO: add energy contribution from interior face terms");
}
if (bfnfi.Size())
{
FaceElementTransformations *tr;
const FiniteElement *fe1, *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 < bfnfi.Size(); k++)
{
if (bfnfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bfnfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary 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 < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
tr = mesh->GetBdrFaceTransformations (i);
if (tr != NULL)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
x.GetSubVector(vdofs, el_x);
fe1 = fes->GetFE(tr->Elem1No);
// The 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.
fe2 = fe1;
for (int k = 0; k < bfnfi.Size(); k++)
{
if (bfnfi_marker[k] &&
(*bfnfi_marker[k])[bdr_attr-1] == 0) { continue; }
energy += bfnfi[k]->GetFaceEnergy(*fe1, *fe2, *tr, el_x);
}
}
}
MFEM_ABORT("TODO: add energy contribution from boundary face terms");
}
return energy;
}
+1 -1
View File
@@ -111,7 +111,7 @@ public:
be fes->GetVSize(). */
double GetGridFunctionEnergy(const Vector &x) const;
/// Compute the energy corresponding to the state @a x.
/// Compute the enery corresponding to the state @a x.
/** In general, @a x may have non-homogeneous essential boundary values.
The state @a x must be a true-dof vector. */
-8
View File
@@ -55,14 +55,6 @@ double NonlinearFormIntegrator::GetElementEnergy(
return 0.0;
}
double NonlinearFormIntegrator::GetFaceEnergy(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Tr, const Vector &elfun)
{
mfem_error("NonlinearFormIntegrator::GetFaceEnergy"
" is not overloaded!");
return 0.0;
}
void BlockNonlinearFormIntegrator::AssembleElementVector(
const Array<const FiniteElement *> &el,
+1 -7
View File
@@ -63,17 +63,11 @@ public:
FaceElementTransformations &Tr,
const Vector &elfun, DenseMatrix &elmat);
/// Compute the local energy/functional
/// Compute the local energy
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun);
/// Compute the face(s) contribution to the energy/functional
virtual double GetFaceEnergy(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Tr,
const Vector &elfun);
virtual ~NonlinearFormIntegrator() { }
};
+42 -49
View File
@@ -35,30 +35,25 @@ typedef double* QLocal2D_t @dim(Q1D, Q1D, NE);
typedef double* DLocal3D_t @dim(D1D, D1D, D1D, NE);
typedef double* QLocal3D_t @dim(Q1D, Q1D, Q1D, NE);
typedef double* Jacobian2D_t @dim(Q2D, 2, 2, NE);
typedef double* Jacobian3D_t @dim(Q3D, 3, 3, NE);
typedef double* Jacobian2D_t @dim(2, 2, Q2D, NE);
typedef double* Jacobian3D_t @dim(3, 3, Q3D, NE);
typedef double* Coeff2D_t @dim(Q2D, NE);
typedef double* Coeff3D_t @dim(Q3D, NE);
typedef double* SymmOperator2D_t @dim(Q2D, 3, NE);
typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
typedef double* SymmOperator2D_t @dim(3, Q2D, NE);
typedef double* SymmOperator3D_t @dim(6, Q3D, NE);
@kernel void DiffusionSetup2D(const int NE,
@restrict const double *W,
@restrict const Jacobian2D_t J,
@restrict const Coeff2D_t C,
@restrict SymmOperator2D_t op,
const bool const_c) {
const double COEFF,
@restrict SymmOperator2D_t op) {
for (int e = 0; e < NE; ++e; @outer) {
for (int q = 0; q < Q2D; ++q; @inner) {
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e);
const double J21 = J(q, 0, 1, e), J22 = J(q, 1, 1, e);
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / ((J11 * J22) - (J21 * J12));
op(q, 0, e) = c_detJ * (J21*J21 + J22*J22); // (1,1)
op(q, 1, e) = -c_detJ * (J21*J11 + J22*J12); // (1,2), (2,1)
op(q, 2, e) = c_detJ * (J11*J11 + J12*J12); // (2,2)
const double J11 = J(0, 0, q, e), J12 = J(1, 0, q, e);
const double J21 = J(0, 1, q, e), J22 = J(1, 1, q, e);
const double c_detJ = W[q] * COEFF / ((J11 * J22) - (J21 * J12));
op(0, q, e) = c_detJ * (J21*J21 + J22*J22); // (1,1)
op(1, q, e) = -c_detJ * (J21*J11 + J22*J12); // (1,2), (2,1)
op(2, q, e) = c_detJ * (J11*J11 + J12*J12); // (2,2)
}
}
}
@@ -66,20 +61,18 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
@kernel void DiffusionSetup3D(const int NE,
@restrict const double *W,
@restrict const Jacobian3D_t J,
@restrict const Coeff3D_t C,
@restrict SymmOperator3D_t op,
const bool const_c) {
const double COEFF,
@restrict SymmOperator3D_t op) {
for (int e = 0; e < NE; ++e; @outer) {
for (int q = 0; q < Q3D; ++q; @inner) {
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e), J13 = J(q, 2, 0, e);
const double J21 = J(q, 0, 1, e), J22 = J(q, 1, 1, e), J23 = J(q, 2, 1, e);
const double J31 = J(q, 0, 2, e), J32 = J(q, 1, 2, e), J33 = J(q, 2, 2, e);
const double J11 = J(0, 0, q, e), J12 = J(1, 0, q, e), J13 = J(2, 0, q, e);
const double J21 = J(0, 1, q, e), J22 = J(1, 1, q, e), J23 = J(2, 1, q, e);
const double J31 = J(0, 2, q, e), J32 = J(1, 2, q, e), J33 = J(2, 2, q, e);
const double detJ = ((J11 * J22 * J33) + (J12 * J23 * J31) + (J13 * J21 * J32) -
(J13 * J22 * J31) - (J12 * J21 * J33) - (J11 * J23 * J32));
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / detJ;
const double c_detJ = W[q] * COEFF / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
@@ -95,12 +88,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
const double A33 = (J11 * J22) - (J12 * J21);
// adj(J)^Tadj(J)
op(q, 0, e) = c_detJ * (A11*A11 + A21*A21 + A31*A31); // (1,1)
op(q, 1, e) = c_detJ * (A11*A12 + A21*A22 + A31*A32); // (1,2), (2,1)
op(q, 2, e) = c_detJ * (A11*A13 + A21*A23 + A31*A33); // (1,3), (3,1)
op(q, 3, e) = c_detJ * (A12*A12 + A22*A22 + A32*A32); // (2,2)
op(q, 4, e) = c_detJ * (A12*A13 + A22*A23 + A32*A33); // (2,3), (3,2)
op(q, 5, e) = c_detJ * (A13*A13 + A23*A23 + A33*A33); // (3,3)
op(0, q, e) = c_detJ * (A11*A11 + A21*A21 + A31*A31); // (1,1)
op(1, q, e) = c_detJ * (A11*A12 + A21*A22 + A31*A32); // (1,2), (2,1)
op(2, q, e) = c_detJ * (A11*A13 + A21*A23 + A31*A33); // (1,3), (3,1)
op(3, q, e) = c_detJ * (A12*A12 + A22*A22 + A32*A32); // (2,2)
op(4, q, e) = c_detJ * (A12*A13 + A22*A23 + A32*A33); // (2,3), (3,2)
op(5, q, e) = c_detJ * (A13*A13 + A23*A23 + A33*A33); // (3,3)
}
}
}
@@ -153,9 +146,9 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
const int q = QUAD_2D_ID(qx, qy);
const double O11 = op(q, 0, e);
const double O12 = op(q, 1, e);
const double O22 = op(q, 2, e);
const double O11 = op(0, q, e);
const double O12 = op(1, q, e);
const double O22 = op(2, q, e);
const double gradX = grad[qy][qx][0];
const double gradY = grad[qy][qx][1];
@@ -262,9 +255,9 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
}
const int q = QUAD_2D_ID(qx, qy);
const double O11 = op(q, 0, e);
const double O12 = op(q, 1, e);
const double O22 = op(q, 2, e);
const double O11 = op(0, q, e);
const double O12 = op(1, q, e);
const double O22 = op(2, q, e);
s_grad(0, qx, qy) = (O11 * gradX) + (O12 * gradY);
s_grad(1, qx, qy) = (O12 * gradX) + (O22 * gradY);
@@ -389,12 +382,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
const int q = QUAD_3D_ID(qx, qy, qz);
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 O11 = op(0, q, e);
const double O12 = op(1, q, e);
const double O13 = op(2, q, e);
const double O22 = op(3, q, e);
const double O23 = op(4, q, e);
const double O33 = op(5, q, e);
const double gradX = grad[qz][qy][qx][0];
const double gradY = grad[qz][qy][qx][1];
@@ -564,12 +557,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
}
const int q = QUAD_3D_ID(qx, qy, qz);
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 O11 = op(0, q, e);
const double O12 = op(1, q, e);
const double O13 = op(2, q, e);
const double O22 = op(3, q, e);
const double O23 = op(4, q, e);
const double O33 = op(5, q, e);
const double qDxyz = (O11 * Dxyz) + (O12 * xDyz) + (O13 * xyDz);
const double qxDyz = (O12 * Dxyz) + (O22 * xDyz) + (O23 * xyDz);
+1 -8
View File
@@ -203,14 +203,7 @@ void ParBilinearForm::AssembleSharedFaces(int skip_zeros)
vdofs1.Copy(vdofs_all);
for (int j = 0; j < vdofs2.Size(); j++)
{
if (vdofs2[j] >= 0)
{
vdofs2[j] += height;
}
else
{
vdofs2[j] -= height;
}
vdofs2[j] += height;
}
vdofs_all.Append(vdofs2);
for (int k = 0; k < fbfi.Size(); k++)
+80 -416
View File
@@ -14,7 +14,6 @@
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#include "../general/forall.hpp"
#include "../general/sort_pairs.hpp"
#include "../mesh/mesh_headers.hpp"
#include "../general/binaryio.hpp"
@@ -98,8 +97,6 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
gcomm = NULL;
gfdofs = NULL;
P = NULL;
Pconf = NULL;
R = NULL;
@@ -150,37 +147,20 @@ void ParFiniteElementSpace::Construct()
// cut space.
ConstructTrueDofs();
ngedofs = ngfdofs = 0;
gfdofs = NULL;
// calculate number of ghost DOFs
ngvdofs = pncmesh->GetNGhostVertices()
* fec->DofForGeometry(Geometry::POINT);
ngedofs = ngfdofs = 0;
if (pmesh->Dimension() > 1)
{
ngedofs = pncmesh->GetNGhostEdges()
* fec->DofForGeometry(Geometry::SEGMENT);
}
if (pmesh->Dimension() > 2)
{
if (fdofs != NULL) // have mixed faces
{
gfdofs = new int[pncmesh->GetNGhostFaces()+1];
gfdofs[0] = 0;
for (int i = 0; i < pncmesh->GetNGhostFaces(); i++)
{
int ghost = pncmesh->GetNFaces() + i;
ngfdofs += fec->DofForGeometry(pncmesh->GetFaceGeometry(ghost));
gfdofs[i+1] = ngfdofs;
}
}
else
{
ngfdofs = pncmesh->GetNGhostFaces()
* fec->DofForGeometry(pncmesh->GetFaceGeometry(0));
}
ngfdofs = pncmesh->GetNGhostFaces()
* fec->DofForGeometry(pncmesh->GetGhostFaceGeometry(0));
}
// total number of ghost DOFs. Ghost DOFs start at index 'ndofs', i.e.,
@@ -633,15 +613,15 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
int ldof = GetVSize();
int ltdof = TrueVSize();
HYPRE_Int *i_diag = new HYPRE_Int[ldof+1];
HYPRE_Int *j_diag = new HYPRE_Int[ltdof];
HYPRE_Int *i_diag = mfem::New<HYPRE_Int>(ldof+1);
HYPRE_Int *j_diag = mfem::New<HYPRE_Int>(ltdof);
int diag_counter;
HYPRE_Int *i_offd = new HYPRE_Int[ldof+1];
HYPRE_Int *j_offd = new HYPRE_Int[ldof-ltdof];
HYPRE_Int *i_offd = mfem::New<HYPRE_Int>(ldof+1);
HYPRE_Int *j_offd = mfem::New<HYPRE_Int>(ldof-ltdof);
int offd_counter;
HYPRE_Int *cmap = new HYPRE_Int[ldof-ltdof];
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(ldof-ltdof);
HYPRE_Int *col_starts = GetTrueDofOffsets();
HYPRE_Int *row_starts = GetDofOffsets();
@@ -767,14 +747,12 @@ void ParFiniteElementSpace::GetEssentialTrueDofs(const Array<int>
// Verify that in boolean arithmetic: P^T ess_dofs = R ess_dofs.
Array<int> true_ess_dofs2(true_ess_dofs.Size());
HypreParMatrix *Pt = Dof_TrueDof_Matrix()->Transpose();
const int *ess_dofs_data = ess_dofs.HostRead();
Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
Pt->BooleanMult(1, ess_dofs, 0, true_ess_dofs2);
delete Pt;
int counter = 0;
const int *ted = true_ess_dofs.HostRead();
for (int i = 0; i < true_ess_dofs.Size(); i++)
{
if (bool(ted[i]) != bool(true_ess_dofs2[i])) { counter++; }
if (bool(true_ess_dofs[i]) != bool(true_ess_dofs2[i])) { counter++; }
}
MFEM_VERIFY(counter == 0, "internal MFEM error: counter = " << counter);
#endif
@@ -876,20 +854,7 @@ const Operator *ParFiniteElementSpace::GetProlongationMatrix() const
{
if (Conforming())
{
if (!Pconf)
{
if (!Device::Allows(Backend::DEVICE_MASK))
{
Pconf = new ConformingProlongationOperator(*this);
}
else
{
if (NRanks > 1)
{
Pconf = new DeviceConformingProlongationOperator(*this);
}
}
}
if (!Pconf) { Pconf = new ConformingProlongationOperator(*this); }
return Pconf;
}
else
@@ -937,15 +902,11 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
{
GetElementVDofs(my_elems[i], ldofs);
for (int j = 0; j < ldofs.Size(); j++)
{
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
if (ldof_marker[ldof] != fn)
if (ldof_marker[ldofs[j]] != fn)
{
ldof_marker[ldof] = fn;
ldof_marker[ldofs[j]] = fn;
send_face_nbr_ldof.AddAColumnInRow(fn);
}
}
send_nbr_elem_dof.AddColumnsInRow(send_el_off[fn] + i, ldofs.Size());
}
@@ -999,11 +960,9 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
GetElementVDofs(my_elems[i], ldofs);
for (int j = 0; j < ldofs.Size(); j++)
{
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
if (ldof_marker[ldof] != fn)
if (ldof_marker[ldofs[j]] != fn)
{
ldof_marker[ldof] = fn;
ldof_marker[ldofs[j]] = fn;
send_face_nbr_ldof.AddConnection(fn, ldofs[j]);
}
}
@@ -1024,14 +983,12 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
for (int i = 0; i < num_ldofs; i++)
{
int ldof = (ldofs[i] >= 0 ? ldofs[i] : -1-ldofs[i]);
ldof_marker[ldof] = i;
ldof_marker[ldofs[i]] = i;
}
for ( ; j < j_end; j++)
{
int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
send_J[j] = ldof_marker[send_J[j]];
}
}
@@ -1066,14 +1023,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
for ( ; j < j_end; j++)
{
if (recv_J[j] >= 0)
{
recv_J[j] += shift;
}
else
{
recv_J[j] -= shift;
}
recv_J[j] += shift;
}
}
@@ -1122,15 +1072,8 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
for (int fn = 0, j = 0; fn < num_face_nbrs; fn++)
{
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
{
int ldof = face_nbr_ldof.GetJ()[j];
if (ldof < 0)
{
ldof = -1-ldof;
}
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
}
face_nbr_glob_dof_map[j] =
dof_face_nbr_offsets[fn] + face_nbr_ldof.GetJ()[j];
}
MPI_Waitall(num_face_nbrs, send_requests, statuses);
@@ -1343,18 +1286,20 @@ void ParFiniteElementSpace::GetGhostEdgeDofs(const MeshId &edge_id,
void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
Array<int> &dofs) const
{
int nfv, V[4], E[4], Eo[4];
nfv = pmesh->pncmesh->GetFaceVerticesEdges(face_id, V, E, Eo);
const int ghost_face_index = face_id.index - pncmesh->GetNFaces();
MFEM_ASSERT(pncmesh->GetGhostFaceGeometry(ghost_face_index)
== Geometry::SQUARE, "");
int nv = fec->DofForGeometry(Geometry::POINT);
int ne = fec->DofForGeometry(Geometry::SEGMENT);
int nf = fec->DofForGeometry((nfv == 3) ?
Geometry::TRIANGLE : Geometry::SQUARE);
int nf = fec->DofForGeometry(Geometry::SQUARE);
dofs.SetSize(4*nv + 4*ne + nf);
dofs.SetSize(nfv*(nv + ne) + nf);
int V[4], E[4], Eo[4];
pmesh->pncmesh->GetFaceVerticesEdges(face_id, V, E, Eo);
int offset = 0;
for (int i = 0; i < nfv; i++)
for (int i = 0; i < 4; i++)
{
int ghost = pncmesh->GetNVertices();
int first = (V[i] < ghost) ? V[i]*nv : (ndofs + (V[i] - ghost)*nv);
@@ -1364,7 +1309,7 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
}
}
for (int i = 0; i < nfv; i++)
for (int i = 0; i < 4; i++)
{
int ghost = pncmesh->GetNEdges();
int first = (E[i] < ghost) ? nvdofs + E[i]*ne
@@ -1377,10 +1322,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
}
}
const int ghost_face_index = face_id.index - pncmesh->GetNFaces();
int first = ndofs + ngvdofs + ngedofs;
first += gfdofs ? gfdofs[ghost_face_index] : nf*ghost_face_index;
// Assuming all ghost faces have the same number of dofs:
int first = ndofs + ngvdofs + ngedofs + ghost_face_index*nf;
for (int j = 0; j < nf; j++)
{
dofs[offset++] = first + j;
@@ -1422,19 +1365,12 @@ void ParFiniteElementSpace::GetBareDofs(int entity, int index,
break;
default:
ned = fec->DofForGeometry(pncmesh->GetFaceGeometry(index));
MFEM_ASSERT(!pmesh->HasGeometry(Geometry::TRIANGLE), "");
ned = fec->DofForGeometry(Geometry::SQUARE);
ghost = pncmesh->GetNFaces();
if (index < ghost) // regular face
{
first = nvdofs + nedofs + (fdofs ? fdofs[index] : index*ned);
}
else // ghost face
{
index -= ghost;
first = ndofs + ngvdofs + ngedofs +
(gfdofs ? gfdofs[index] : index*ned);
}
first = (index < ghost)
? nvdofs + nedofs + index*ned // regular face
: ndofs + ngvdofs + ngedofs + (index - ghost)*ned; // ghost
break;
}
@@ -1470,30 +1406,16 @@ int ParFiniteElementSpace::PackDof(int entity, int index, int edof) const
: ndofs + ngvdofs + (index - ghost)*ned + edof; // ghost edge
default:
MFEM_ASSERT(!pmesh->HasGeometry(Geometry::TRIANGLE), "");
ghost = pncmesh->GetNFaces();
ned = fec->DofForGeometry(pncmesh->GetFaceGeometry(index));
ned = fec->DofForGeometry(Geometry::SQUARE);
if (index < ghost) // regular face
{
return nvdofs + nedofs + (fdofs ? fdofs[index] : index*ned) + edof;
}
else // ghost face
{
index -= ghost;
return ndofs + ngvdofs + ngedofs +
(gfdofs ? gfdofs[index] : index*ned) + edof;
}
return (index < ghost)
? nvdofs + nedofs + index*ned + edof // regular face
: ndofs + ngvdofs + ngedofs + (index - ghost)*ned + edof; //ghost
}
}
static int bisect(int* array, int size, int value)
{
int* end = array + size;
int* pos = std::upper_bound(array, end, value);
MFEM_VERIFY(pos != end, "value not found");
return pos - array;
}
/** Dissect a DOF number to obtain the entity type (0=vertex, 1=edge, 2=face),
* entity index and the DOF number within the entity.
*/
@@ -1519,17 +1441,9 @@ void ParFiniteElementSpace::UnpackDof(int dof,
dof -= nedofs;
if (dof < nfdofs) // regular face
{
if (fdofs) // have mixed faces
{
index = bisect(fdofs+1, mesh->GetNFaces(), dof);
edof = dof - fdofs[index];
}
else // uniform faces
{
int nf = fec->DofForGeometry(pncmesh->GetFaceGeometry(0));
index = dof / nf, edof = dof % nf;
}
entity = 2;
MFEM_ASSERT(!pmesh->HasGeometry(Geometry::TRIANGLE), "");
int nf = fec->DofForGeometry(Geometry::SQUARE);
entity = 2, index = dof / nf, edof = dof % nf;
return;
}
MFEM_ABORT("Cannot unpack internal DOF");
@@ -1553,17 +1467,8 @@ void ParFiniteElementSpace::UnpackDof(int dof,
dof -= ngedofs;
if (dof < ngfdofs) // ghost face
{
if (gfdofs) // have mixed faces
{
index = bisect(gfdofs+1, pncmesh->GetNGhostFaces(), dof);
edof = dof - gfdofs[index];
}
else // uniform faces
{
int nf = fec->DofForGeometry(pncmesh->GetFaceGeometry(0));
index = pncmesh->GetNFaces() + dof / nf, edof = dof % nf;
}
entity = 2;
int nf = fec->DofForGeometry(pncmesh->GetGhostFaceGeometry(0));
entity = 2, index = pncmesh->GetNFaces() + dof / nf, edof = dof % nf;
return;
}
MFEM_ABORT("Out of range DOF.");
@@ -1746,7 +1651,7 @@ void NeighborRowMessage::Encode(int rank)
mfem::out << "Rank " << pncmesh->MyRank << " sending to " << rank
<< ": ent " << ri.entity << ", index " << ri.index
<< ", edof " << ri.edof << " (id " << id.element << "/"
<< int(id.local) << ")" << std::endl;
<< id.local << ")" << std::endl;
#endif
// handle orientation and sign change
@@ -1789,6 +1694,8 @@ void NeighborRowMessage::Decode(int rank)
rows.clear();
rows.reserve(nrows);
Geometry::Type fgeom = pncmesh->GetFaceGeometry();
// read rows
for (int ent = 0, gi = 0; ent < 3; ent++)
{
@@ -1807,9 +1714,8 @@ void NeighborRowMessage::Decode(int rank)
}
else if (ent == 2)
{
Geometry::Type geom = pncmesh->GetFaceGeometry(id.index);
int fo = pncmesh->GetFaceOrientation(id.index);
ind = fec->DofOrderForOrientation(geom, fo);
ind = fec->DofOrderForOrientation(fgeom, fo);
}
double s = 1.0;
@@ -1898,7 +1804,7 @@ void ParFiniteElementSpace
for (int i = 0; i < dof_group.Size(); i++)
{
os << i << ": ";
if (i < (nvdofs + nedofs + nfdofs) || i >= ndofs)
if (i < (nvdofs + nedofs + nfdofs) || i > ndofs)
{
int ent, idx, edof;
UnpackDof(i, ent, idx, edof);
@@ -1980,7 +1886,15 @@ int ParFiniteElementSpace
if (!list.masters.size()) { continue; }
IsoparametricTransformation T;
DenseMatrix I;
if (entity > 1) { T.SetFE(&QuadrilateralFE); }
else { T.SetFE(&SegmentFE); }
Geometry::Type geom = (entity > 1) ?
Geometry::SQUARE : Geometry::SEGMENT;
const FiniteElement* fe = fec->FiniteElementForGeometry(geom);
if (!fe) { continue; }
DenseMatrix I(fe->GetDof());
// process masters that we own or that affect our edges/faces
for (unsigned mi = 0; mi < list.masters.size(); mi++)
@@ -1994,17 +1908,6 @@ int ParFiniteElementSpace
if (!master_dofs.Size()) { continue; }
const FiniteElement* fe = fec->FiniteElementForGeometry(mf.Geom());
if (!fe) { continue; }
switch (mf.Geom())
{
case Geometry::SQUARE: T.SetFE(&QuadrilateralFE); break;
case Geometry::TRIANGLE: T.SetFE(&TriangleFE); break;
case Geometry::SEGMENT: T.SetFE(&SegmentFE); break;
default: MFEM_ABORT("unsupported geometry");
}
// constrain slaves that exist in our mesh
for (int si = mf.slaves_begin; si < mf.slaves_end; si++)
{
@@ -2055,8 +1958,6 @@ int ParFiniteElementSpace
(l == 1) ? (const MeshId&) list.masters[i]
/* */ : (const MeshId&) list.slaves[i];
if (id.index < 0) { continue; }
GroupId owner = pncmesh->GetEntityOwnerId(entity, id.index);
GroupId group = pncmesh->GetEntityGroupId(entity, id.index);
@@ -2348,7 +2249,7 @@ HypreParMatrix* ParFiniteElementSpace
}
// create offd column mapping
HYPRE_Int *cmap = new HYPRE_Int[col_map.size()];
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(col_map.size());
int offd_col = 0;
for (std::map<HYPRE_Int, int>::iterator
it = col_map.begin(); it != col_map.end(); ++it)
@@ -2357,14 +2258,14 @@ HypreParMatrix* ParFiniteElementSpace
it->second = offd_col++;
}
HYPRE_Int *I_diag = new HYPRE_Int[vdim*local_rows + 1];
HYPRE_Int *I_offd = new HYPRE_Int[vdim*local_rows + 1];
HYPRE_Int *I_diag = mfem::New<HYPRE_Int>(vdim*local_rows + 1);
HYPRE_Int *I_offd = mfem::New<HYPRE_Int>(vdim*local_rows + 1);
HYPRE_Int *J_diag = new HYPRE_Int[nnz_diag];
HYPRE_Int *J_offd = new HYPRE_Int[nnz_offd];
HYPRE_Int *J_diag = mfem::New<HYPRE_Int>(nnz_diag);
HYPRE_Int *J_offd = mfem::New<HYPRE_Int>(nnz_offd);
double *A_diag = new double[nnz_diag];
double *A_offd = new double[nnz_offd];
double *A_diag = mfem::New<double>(nnz_diag);
double *A_offd = mfem::New<double>(nnz_offd);
int vdim1 = bynodes ? vdim : 1;
int vdim2 = bynodes ? 1 : vdim;
@@ -2415,7 +2316,7 @@ HypreParMatrix* ParFiniteElementSpace
static HYPRE_Int* make_i_array(int nrows)
{
HYPRE_Int *I = new HYPRE_Int[nrows+1];
HYPRE_Int *I = mfem::New<HYPRE_Int>(nrows+1);
for (int i = 0; i <= nrows; i++) { I[i] = -1; }
return I;
}
@@ -2427,7 +2328,7 @@ static HYPRE_Int* make_j_array(HYPRE_Int* I, int nrows)
{
if (I[i] >= 0) { nnz++; }
}
HYPRE_Int *J = new HYPRE_Int[nnz];
HYPRE_Int *J = mfem::New<HYPRE_Int>(nnz);
I[nrows] = -1;
for (int i = 0, k = 0; i <= nrows; i++)
@@ -2526,7 +2427,7 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
}
SortPairs<HYPRE_Int, int>(cmap_offd, offd_cols);
HYPRE_Int* cmap = new HYPRE_Int[offd_cols];
HYPRE_Int* cmap = mfem::New<HYPRE_Int>(offd_cols);
for (int i = 0; i < offd_cols; i++)
{
cmap[i] = cmap_offd[i].one;
@@ -2553,9 +2454,6 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
MFEM_VERIFY(Nonconforming(), "Not implemented for conforming meshes.");
MFEM_VERIFY(pmesh->GetNumGeometries(pmesh->Dimension()) == 1,
"Not implemented for mixed meshes.");
MFEM_VERIFY(old_dof_offsets[nrk], "Missing previous (finer) space.");
MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
"Previous space is not finer.");
@@ -2569,7 +2467,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
Vector row;
ParNCMesh* pncmesh = pmesh->pncmesh;
Geometry::Type geom = pncmesh->GetElementGeometry(0); // TODO mixed meshes
Geometry::Type geom = pncmesh->GetElementGeometry();
int ldof = fec->FiniteElementForGeometry(geom)->GetDof();
const CoarseFineTransformations &dtrans = pncmesh->GetDerefinementTransforms();
@@ -2725,7 +2623,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
offd->SetWidth(col_map.size());
// create offd column mapping for use by hypre
HYPRE_Int *cmap = new HYPRE_Int[offd->Width()];
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(offd->Width());
for (std::map<HYPRE_Int, int>::iterator
it = col_map.begin(); it != col_map.end(); ++it)
{
@@ -2793,8 +2691,6 @@ void ParFiniteElementSpace::Destroy()
delete Pconf; Pconf = NULL;
delete R; R = NULL;
delete [] gfdofs; gfdofs = NULL;
delete gcomm; gcomm = NULL;
num_face_nbr_dofs = -1;
@@ -2967,8 +2863,9 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
MFEM_ASSERT(x.Size() == Width(), "");
MFEM_ASSERT(y.Size() == Height(), "");
const double *xdata = x.HostRead();
double *ydata = y.HostWrite();
const double *xdata = x.GetData();
double *ydata = y.GetData();
x.Pull();
const int m = external_ldofs.Size();
const int in_layout = 2; // 2 - input is ltdofs array
@@ -2985,6 +2882,7 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
const int out_layout = 0; // 0 - output is ldofs array
gc.BcastEnd(ydata, out_layout);
y.Push();
}
void ConformingProlongationOperator::MultTranspose(
@@ -2993,8 +2891,9 @@ void ConformingProlongationOperator::MultTranspose(
MFEM_ASSERT(x.Size() == Height(), "");
MFEM_ASSERT(y.Size() == Width(), "");
const double *xdata = x.HostRead();
double *ydata = y.HostWrite();
const double *xdata = x.GetData();
double *ydata = y.GetData();
x.Pull();
const int m = external_ldofs.Size();
gc.ReduceBegin(xdata);
@@ -3010,242 +2909,7 @@ void ConformingProlongationOperator::MultTranspose(
const int out_layout = 2; // 2 - output is an array on all ltdofs
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
}
DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
const ParFiniteElementSpace &pfes) :
ConformingProlongationOperator(pfes),
mpi_gpu_aware(Device::GetGPUAwareMPI())
{
MFEM_ASSERT(pfes.Conforming(), "internal error");
const SparseMatrix *R = pfes.GetRestrictionMatrix();
MFEM_ASSERT(R->Finalized(), "");
const int tdofs = R->Height();
MFEM_ASSERT(tdofs == pfes.GetTrueVSize(), "");
MFEM_ASSERT(tdofs == R->GetI()[tdofs], "");
ltdof_ldof = Array<int>(const_cast<int*>(R->GetJ()), tdofs);
ltdof_ldof.UseDevice();
{
Table nbr_ltdof;
gc.GetNeighborLTDofTable(nbr_ltdof);
const int nb_connections = nbr_ltdof.Size_of_connections();
shr_ltdof.SetSize(nb_connections);
shr_ltdof.CopyFrom(nbr_ltdof.GetJ());
shr_buf.SetSize(nb_connections);
shr_buf.UseDevice(true);
shr_buf_offsets = nbr_ltdof.GetI();
{
Array<int> shr_ltdof(nbr_ltdof.GetJ(), nb_connections);
Array<int> unique_ltdof(shr_ltdof);
unique_ltdof.Sort();
unique_ltdof.Unique();
// Note: the next loop modifies the J array of nbr_ltdof
for (int i = 0; i < shr_ltdof.Size(); i++)
{
shr_ltdof[i] = unique_ltdof.FindSorted(shr_ltdof[i]);
MFEM_ASSERT(shr_ltdof[i] != -1, "internal error");
}
Table unique_shr;
Transpose(shr_ltdof, unique_shr, unique_ltdof.Size());
unq_ltdof = Array<int>(unique_ltdof, unique_ltdof.Size());
unq_shr_i = Array<int>(unique_shr.GetI(), unique_shr.Size()+1);
unq_shr_j = Array<int>(unique_shr.GetJ(), unique_shr.Size_of_connections());
}
delete [] nbr_ltdof.GetJ();
nbr_ltdof.LoseData();
}
{
Table nbr_ldof;
gc.GetNeighborLDofTable(nbr_ldof);
const int nb_connections = nbr_ldof.Size_of_connections();
ext_ldof.SetSize(nb_connections);
ext_ldof.CopyFrom(nbr_ldof.GetJ());
ext_buf.SetSize(nb_connections);
ext_buf.UseDevice(true);
ext_buf_offsets = nbr_ldof.GetI();
delete [] nbr_ldof.GetJ();
nbr_ldof.LoseData();
}
const GroupTopology &gtopo = gc.GetGroupTopology();
int req_counter = 0;
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0) { req_counter++; }
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0) { req_counter++; }
}
requests = new MPI_Request[req_counter];
}
static void ExtractSubVector(const int N,
const Array<int> &indices,
const Vector &in, Vector &out)
{
auto y = out.Write();
const auto x = in.Read();
const auto I = indices.Read();
MFEM_FORALL(i, N, y[i] = x[I[i]];); // indices can be repeated
}
void DeviceConformingProlongationOperator::BcastBeginCopy(
const Vector &x) const
{
// shr_buf[i] = src[shr_ltdof[i]]
if (shr_ltdof.Size() == 0) { return; }
ExtractSubVector(shr_ltdof.Size(), shr_ltdof, x, shr_buf);
// If the above kernel is executed asynchronously, we should wait for it to
// complete
if (mpi_gpu_aware) { Device::Synchronize(); }
}
static void SetSubVector(const int N,
const Array<int> &indices,
const Vector &in, Vector &out)
{
auto y = out.Write();
const auto x = in.Read();
const auto I = indices.Read();
MFEM_FORALL(i, N, y[I[i]] = x[i];);
}
void DeviceConformingProlongationOperator::BcastLocalCopy(
const Vector &x, Vector &y) const
{
// dst[ltdof_ldof[i]] = src[i]
if (ltdof_ldof.Size() == 0) { return; }
SetSubVector(ltdof_ldof.Size(), ltdof_ldof, x, y);
}
void DeviceConformingProlongationOperator::BcastEndCopy(
Vector &y) const
{
// dst[ext_ldof[i]] = ext_buf[i]
if (ext_ldof.Size() == 0) { return; }
SetSubVector(ext_ldof.Size(), ext_ldof, ext_buf, y);
}
void DeviceConformingProlongationOperator::Mult(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
BcastBeginCopy(x); // copy to 'shr_buf'
int req_counter = 0;
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
}
BcastLocalCopy(x, y);
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
}
DeviceConformingProlongationOperator::~DeviceConformingProlongationOperator()
{
delete [] requests;
delete [] ext_buf_offsets;
delete [] shr_buf_offsets;
}
void DeviceConformingProlongationOperator::ReduceBeginCopy(
const Vector &x) const
{
// ext_buf[i] = src[ext_ldof[i]]
if (ext_ldof.Size() == 0) { return; }
ExtractSubVector(ext_ldof.Size(), ext_ldof, x, ext_buf);
// If the above kernel is executed asynchronously, we should wait for it to
// complete
if (mpi_gpu_aware) { Device::Synchronize(); }
}
void DeviceConformingProlongationOperator::ReduceLocalCopy(
const Vector &x, Vector &y) const
{
// dst[i] = src[ltdof_ldof[i]]
if (ltdof_ldof.Size() == 0) { return; }
ExtractSubVector(ltdof_ldof.Size(), ltdof_ldof, x, y);
}
static void AddSubVector(const int num_unique_dst_indices,
const Array<int> &unique_dst_indices,
const Array<int> &unique_to_src_offsets,
const Array<int> &unique_to_src_indices,
const Vector &src,
Vector &dst)
{
auto y = dst.Write();
const auto x = src.Read();
const auto DST_I = unique_dst_indices.Read();
const auto SRC_O = unique_to_src_offsets.Read();
const auto SRC_I = unique_to_src_indices.Read();
MFEM_FORALL(i, num_unique_dst_indices,
{
const int dst_idx = DST_I[i];
double sum = y[dst_idx];
const int end = SRC_O[i+1];
for (int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
y[dst_idx] = sum;
});
}
void DeviceConformingProlongationOperator::ReduceEndAssemble(Vector &y) const
{
// dst[shr_ltdof[i]] += shr_buf[i]
const int unq_ltdof_size = unq_ltdof.Size();
if (unq_ltdof_size == 0) { return; }
AddSubVector(unq_ltdof_size, unq_ltdof, unq_shr_i, unq_shr_j, shr_buf, y);
}
void DeviceConformingProlongationOperator::MultTranspose(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
ReduceBeginCopy(x); // copy to 'ext_buf'
int req_counter = 0;
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int send_offset = ext_buf_offsets[nbr];
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
}
ReduceLocalCopy(x, y);
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y); // assemble from 'shr_buf'
y.Push();
}
} // namespace mfem
+1 -48
View File
@@ -46,7 +46,6 @@ private:
/// Number of vertex/edge/face/total ghost DOFs (nonconforming case).
int ngvdofs, ngedofs, ngfdofs, ngdofs;
int* gfdofs;
/// The group of each local dof.
Array<int> ldof_group;
@@ -114,7 +113,7 @@ private:
void GetGhostFaceDofs(const MeshId &face_id, Array<int> &dofs) const;
void GetGhostDofs(int entity, const MeshId &id, Array<int> &dofs) const;
/// Return the dofs associated with the interior of the given mesh entity.
// Return the dofs associated with the interior of the given mesh entity.
void GetBareDofs(int entity, int index, Array<int> &dofs) const;
int PackDof(int entity, int index, int edof) const;
@@ -388,52 +387,6 @@ public:
virtual void MultTranspose(const Vector &x, Vector &y) const;
};
/// Auxiliary device class used by ParFiniteElementSpace.
class DeviceConformingProlongationOperator: public
ConformingProlongationOperator
{
protected:
bool mpi_gpu_aware;
Array<int> shr_ltdof, ext_ldof;
mutable Vector shr_buf, ext_buf;
int *shr_buf_offsets, *ext_buf_offsets;
Array<int> ltdof_ldof, unq_ltdof;
Array<int> unq_shr_i, unq_shr_j;
MPI_Request *requests;
// Kernel: copy ltdofs from 'src' to 'shr_buf' - prepare for send.
// shr_buf[i] = src[shr_ltdof[i]]
void BcastBeginCopy(const Vector &src) const;
// Kernel: copy ltdofs from 'src' to ldofs in 'dst'.
// dst[ltdof_ldof[i]] = src[i]
void BcastLocalCopy(const Vector &src, Vector &dst) const;
// Kernel: copy ext. dofs from 'ext_buf' to 'dst' - after recv.
// dst[ext_ldof[i]] = ext_buf[i]
void BcastEndCopy(Vector &dst) const;
// Kernel: copy ext. dofs from 'src' to 'ext_buf' - prepare for send.
// ext_buf[i] = src[ext_ldof[i]]
void ReduceBeginCopy(const Vector &src) const;
// Kernel: copy owned ldofs from 'src' to ltdofs in 'dst'.
// dst[i] = src[ltdof_ldof[i]]
void ReduceLocalCopy(const Vector &src, Vector &dst) const;
// Kernel: assemble dofs from 'shr_buf' into to 'dst' - after recv.
// dst[shr_ltdof[i]] += shr_buf[i]
void ReduceEndAssemble(Vector &dst) const;
public:
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes);
virtual ~DeviceConformingProlongationOperator();
virtual void Mult(const Vector &x, Vector &y) const;
virtual void MultTranspose(const Vector &x, Vector &y) const;
};
}
#endif // MFEM_USE_MPI
+16 -19
View File
@@ -225,13 +225,11 @@ void ParGridFunction::ExchangeFaceNbrData()
MPI_Request *recv_requests = requests + num_face_nbrs;
MPI_Status *statuses = new MPI_Status[num_face_nbrs];
const double *h_data = this->HostRead();
for (int i = 0; i < send_data.Size(); i++)
{
send_data[i] = h_data[send_ldof[i]];
send_data[i] = data[send_ldof[i]];
}
double *h_face_nbr_data = face_nbr_data.HostWrite();
for (int fn = 0; fn < num_face_nbrs; fn++)
{
int nbr_rank = pmesh->GetFaceNbrRank(fn);
@@ -241,7 +239,7 @@ void ParGridFunction::ExchangeFaceNbrData()
send_offset[fn+1] - send_offset[fn],
MPI_DOUBLE, nbr_rank, tag, MyComm, &send_requests[fn]);
MPI_Irecv(&h_face_nbr_data[recv_offset[fn]],
MPI_Irecv(&face_nbr_data(recv_offset[fn]),
recv_offset[fn+1] - recv_offset[fn],
MPI_DOUBLE, nbr_rank, tag, MyComm, &recv_requests[fn]);
}
@@ -369,10 +367,10 @@ void ParGridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<int>(zones_per_vdof, GroupCommunicator::Sum);
gcomm.Bcast(zones_per_vdof);
// Accumulate for all vdofs.
gcomm.Reduce<double>(data, GroupCommunicator::Sum);
gcomm.Bcast<double>(data);
// Accumulate for all tdofs.
HypreParVector *tv = this->ParallelAssemble();
this->Distribute(tv);
delete tv;
ComputeMeans(type, zones_per_vdof);
}
@@ -391,10 +389,10 @@ void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &vcoeff,
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<int>(zones_per_vdof, GroupCommunicator::Sum);
gcomm.Bcast(zones_per_vdof);
// Accumulate for all vdofs.
gcomm.Reduce<double>(data, GroupCommunicator::Sum);
gcomm.Bcast<double>(data);
// Accumulate for all tdofs.
HypreParVector *tv = this->ParallelAssemble();
this->Distribute(tv);
delete tv;
ComputeMeans(type, zones_per_vdof);
}
@@ -427,8 +425,8 @@ void ParGridFunction::ProjectBdrCoefficient(
}
else
{
// TODO: is this the same as the conforming case (after the merge of
// cut-mesh-groups-dev)?
// FIXME: same as the conforming case after 'cut-mesh-groups-dev-*' is
// merged?
ComputeMeans(ARITHMETIC, values_counter);
}
#ifdef MFEM_DEBUG
@@ -471,8 +469,8 @@ void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
}
else
{
// TODO: is this the same as the conforming case (after the merge of
// cut-mesh-groups-dev)?
// FIXME: same as the conforming case after 'cut-mesh-groups-dev-*' is
// merged?
ComputeMeans(ARITHMETIC, values_counter);
}
#ifdef MFEM_DEBUG
@@ -489,17 +487,16 @@ void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
void ParGridFunction::Save(std::ostream &out) const
{
double *data_ = const_cast<double*>(HostRead());
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
if (pfes->GetDofSign(i) < 0) { data[i] = -data[i]; }
}
GridFunction::Save(out);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
if (pfes->GetDofSign(i) < 0) { data[i] = -data[i]; }
}
}
-2
View File
@@ -112,8 +112,6 @@ public:
/// Associate a new parallel space with the ParGridFunction.
void SetSpace(ParFiniteElementSpace *f);
using GridFunction::MakeRef;
/** @brief Make the ParGridFunction reference external data on a new
FiniteElementSpace. */
/** This method changes the FiniteElementSpace associated with the
+2 -2
View File
@@ -46,7 +46,7 @@ double ParNonlinearForm::GetParGridFunctionEnergy(const Vector &x) const
void ParNonlinearForm::Mult(const Vector &x, Vector &y) const
{
NonlinearForm::Mult(x, y); // x --(P)--> aux1 --(A_local)--> aux2
Y.MakeRef(aux2, 0); // aux2 contains A_local.P.x
Y.SetData(aux2.GetData()); // aux2 contains A_local.P.x
if (fnfi.Size())
{
@@ -58,7 +58,7 @@ void ParNonlinearForm::Mult(const Vector &x, Vector &y) const
Array<int> vdofs1, vdofs2;
Vector el_x, el_y;
X.MakeRef(aux1, 0); // aux1 contains P.x
X.SetData(aux1.GetData()); // aux1 contains P.x
X.ExchangeFaceNbrData();
const int n_shared_faces = pmesh->GetNSharedFaces();
for (int i = 0; i < n_shared_faces; i++)
+8 -6
View File
@@ -16,7 +16,9 @@
#include "fem.hpp"
#include <axom/sidre.hpp>
#ifdef MFEM_USE_MPI
#include <sidre/IOManager.hpp>
#endif
#include <string>
#include <iomanip> // for setw, setfill
@@ -202,10 +204,10 @@ SidreDataCollection::get_file_path(const std::string &filename) const
axom::sidre::View *
SidreDataCollection::AllocNamedBuffer(const std::string& buffer_name,
axom::sidre::IndexType sz,
axom::sidre::SidreLength sz,
axom::sidre::TypeID type)
{
sz = std::max(sz, sidre::IndexType(0));
sz = std::max(sz, sidre::SidreLength(0));
sidre::Group *f = named_buffers_grp();
sidre::View *v = NULL;
@@ -823,7 +825,7 @@ void SidreDataCollection::Save(const std::string& filename,
void SidreDataCollection::
addScalarBasedGridFunction(const std::string &field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset)
axom::sidre::SidreLength offset)
{
sidre::Group* grp = m_bp_grp->getGroup("fields/" + field_name);
MFEM_ASSERT(grp != NULL, "field " << field_name << " does not exist");
@@ -886,7 +888,7 @@ addScalarBasedGridFunction(const std::string &field_name, GridFunction *gf,
void SidreDataCollection::
addVectorBasedGridFunction(const std::string& field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset)
axom::sidre::SidreLength offset)
{
sidre::Group* grp = m_bp_grp->getGroup("fields/" + field_name);
MFEM_ASSERT(grp != NULL, "field " << field_name << " does not exist");
@@ -1011,7 +1013,7 @@ DeregisterFieldInBPIndex(const std::string& field_name)
void SidreDataCollection::RegisterField(const std::string &field_name,
GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset)
axom::sidre::SidreLength offset)
{
if ( field_name.empty() || buffer_name.empty() ||
gf == NULL || gf->FESpace() == NULL )
+5 -5
View File
@@ -25,7 +25,7 @@
# pragma GCC diagnostic ignored "-Wpedantic"
# endif
#endif
#include <axom/sidre.hpp>
#include <sidre/sidre.hpp>
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
# pragma GCC diagnostic pop
#endif
@@ -246,7 +246,7 @@ public:
*/
void RegisterField(const std::string &field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset);
axom::sidre::SidreLength offset);
/// Registers an attribute field in the Sidre DataStore
/** The registration process is similar to that of RegisterField()
@@ -385,7 +385,7 @@ public:
*/
axom::sidre::View *
AllocNamedBuffer(const std::string& buffer_name,
axom::sidre::IndexType sz,
axom::sidre::SidreLength sz,
axom::sidre::TypeID type =
axom::sidre::DOUBLE_ID);
@@ -469,7 +469,7 @@ private:
void addScalarBasedGridFunction(const std::string& field_name,
GridFunction* gf,
const std::string &buffer_name,
axom::sidre::IndexType offset);
axom::sidre::SidreLength offset);
/**
* \brief A private helper function to set up the views associated with the
@@ -483,7 +483,7 @@ private:
void addVectorBasedGridFunction(const std::string& field_name,
GridFunction* gf,
const std::string &buffer_name,
axom::sidre::IndexType offset);
axom::sidre::SidreLength offset);
/** @brief A private helper function to set up the Views associated with
attribute field named @a field_name */
+19 -196
View File
@@ -12,7 +12,6 @@
#include "tmop.hpp"
#include "linearform.hpp"
#include "pgridfunc.hpp"
#include "tmop_tools.hpp"
namespace mfem
{
@@ -769,7 +768,7 @@ void TMOP_Metric_352::AssembleH(const DenseMatrix &Jpt,
void TargetConstructor::ComputeAvgVolume() const
{
MFEM_VERIFY(nodes, "Nodes are not given!");
MFEM_ASSERT(avg_volume == 0.0, "The average volume is already computed!");
MFEM_ASSERT(avg_volume == 0.0, "the average volume is already computed!");
Mesh *mesh = nodes->FESpace()->GetMesh();
const int NE = mesh->GetNE();
@@ -788,13 +787,9 @@ void TargetConstructor::ComputeAvgVolume() const
volume += ip.weight * Tr.Weight();
}
}
NCMesh *ncmesh = mesh->ncmesh;
if (Parallel() == false)
if (!Parallel())
{
avg_volume = (ncmesh == NULL) ?
volume / NE : volume / ncmesh->GetNumRootElements();
avg_volume = volume / NE;
}
#ifdef MFEM_USE_MPI
else
@@ -802,8 +797,7 @@ void TargetConstructor::ComputeAvgVolume() const
double area_NE[4];
area_NE[0] = volume; area_NE[1] = NE;
MPI_Allreduce(area_NE, area_NE + 2, 2, MPI_DOUBLE, MPI_SUM, comm);
avg_volume = (ncmesh == NULL) ?
area_NE[2] / area_NE[3] : area_NE[2] / ncmesh->GetNumRootElements();
avg_volume = area_NE[2] / area_NE[3];
}
#endif
}
@@ -811,7 +805,6 @@ void TargetConstructor::ComputeAvgVolume() const
// virtual method
void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_ASSERT(target_type == IDEAL_SHAPE_UNIT_SIZE || nodes != NULL, "");
@@ -834,15 +827,7 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
{
if (avg_volume == 0.0) { ComputeAvgVolume(); }
DenseMatrix W(Wideal.Height());
NCMesh *ncmesh = nodes->FESpace()->GetMesh()->ncmesh;
double el_volume = avg_volume;
if (ncmesh)
{
el_volume = avg_volume / ncmesh->GetElementSizeReduction(e_id);
}
W.Set(std::pow(volume_scale * el_volume / Wideal.Det(),
W.Set(std::pow(volume_scale * avg_volume / Wideal.Det(),
1./W.Height()), Wideal);
for (int i = 0; i < ir.GetNPoints(); i++) { Jtr(i) = W; }
break;
@@ -868,7 +853,7 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
if (target_type == IDEAL_SHAPE_GIVEN_SIZE)
{
const double det = Jtr(i).Det();
MFEM_VERIFY(det > 0.0, "The given mesh is inverted!");
MFEM_VERIFY(det > 0.0, "Initial mesh is inverted!");
Jtr(i).Set(std::pow(det / detW, 1./dim), Wideal);
}
}
@@ -879,162 +864,6 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
}
}
void AnalyticAdaptTC::SetAnalyticTargetSpec(Coefficient *sspec,
VectorCoefficient *vspec,
MatrixCoefficient *mspec)
{
scalar_tspec = sspec;
vector_tspec = vspec;
matrix_tspec = mspec;
}
void AnalyticAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
DenseMatrix point_mat;
point_mat.UseExternalData(elfun.GetData(), fe.GetDof(), fe.GetDim());
switch (target_type)
{
case GIVEN_FULL:
{
MFEM_VERIFY(matrix_tspec != NULL,
"Target type GIVEN_FULL requires a MatrixCoefficient.");
IsoparametricTransformation Tpr;
Tpr.SetFE(&fe);
Tpr.ElementNo = e_id;
Tpr.GetPointMat().Transpose(point_mat);
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
Tpr.SetIntPoint(&ip);
matrix_tspec->Eval(Jtr(i), Tpr, ip);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
#ifdef MFEM_USE_MPI
void DiscreteAdaptTC::SetParDiscreteTargetSpec(ParGridFunction &tspec)
{
target_spec.SetSize(tspec.Size());
target_spec = tspec;
tspec_fes = tspec.FESpace();
// Default evaluator is based on CG advection.
if (adapt_eval == NULL) { adapt_eval = new AdvectorCG; }
adapt_eval->SetParMetaInfo(*tspec.ParFESpace()->GetParMesh(),
*tspec.FESpace()->FEColl(),
tspec.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec.FESpace()->GetMesh()->GetNodes(), target_spec);
}
#endif
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(GridFunction &tspec)
{
target_spec.SetSize(tspec.Size());
target_spec = tspec;
tspec_fes = tspec.FESpace();
// Default evaluator is based on CG advection.
if (adapt_eval == NULL) { adapt_eval = new AdvectorCG; }
adapt_eval->SetSerialMetaInfo(*tspec.FESpace()->GetMesh(),
*tspec.FESpace()->FEColl(),
tspec.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec.FESpace()->GetMesh()->GetNodes(), target_spec);
}
void DiscreteAdaptTC::UpdateTargetSpecification(const Vector &new_x)
{
MFEM_VERIFY(target_spec.Size() > 0, "Target specification is not set!");
adapt_eval->ComputeAtNewPosition(new_x, target_spec);
}
void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_VERIFY(tspec_fes, "A call to SetDiscreteTargerSpec() is needed.");
switch (target_type)
{
case IDEAL_SHAPE_GIVEN_SIZE:
{
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fe.GetGeomType());
const int dim = Wideal.Height(),
ntspec_dofs = tspec_fes->GetFE(0)->GetDof();
Vector shape(ntspec_dofs), tspec_vals(ntspec_dofs);
Array<int> dofs;
tspec_fes->GetElementDofs(e_id, dofs);
target_spec.GetSubVector(dofs, tspec_vals);
const double min_size = tspec_vals.Min();
MFEM_ASSERT(min_size > 0.0,
"Non-positive size propagated in the target definition.");
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
const double size = std::max(shape * tspec_vals, min_size);
Jtr(i).Set(std::pow(size / Wideal.Det(), 1.0/dim), Wideal);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec,
int num_comp)
{
delete fes;
delete mesh;
mesh = new Mesh(m, true);
fes = new FiniteElementSpace(mesh, &fec, num_comp);
}
#ifdef MFEM_USE_MPI
void AdaptivityEvaluator::SetParMetaInfo(const ParMesh &m,
const FiniteElementCollection &fec,
int num_comp)
{
delete pfes;
delete pmesh;
pmesh = new ParMesh(m, true);
pfes = new ParFiniteElementSpace(pmesh, &fec, num_comp);
}
#endif
AdaptivityEvaluator::~AdaptivityEvaluator()
{
delete fes;
delete mesh;
#ifdef MFEM_USE_MPI
delete pfes;
delete pmesh;
#endif
}
void TMOP_Integrator::EnableLimiting(const GridFunction &n0,
const GridFunction &dist, Coefficient &w0,
TMOP_LimiterFunction *lfunc)
@@ -1092,7 +921,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
// Limited case.
Vector shape, p, p0, d_vals;
@@ -1127,13 +956,13 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
}
// TODO: computing the coefficients 'coeff1' and 'coeff0' in physical
// coordinates means that, generally, the gradient and Hessian of the
// TMOP_Integrator will depend on the derivatives of the coefficients.
// FIXME: computing the coefficients 'coeff1' and 'coeff0' in physical
// coordinates means that, generally, the gradient and Hessian of the
// TMOP_Integrator will depend on the derivatives of the coefficients.
//
// In some cases the coefficients are independent of any movement of
// the physical coordinates (i.e. changes in 'elfun'), e.g. when the
// coefficient is a ConstantCoefficient or a GridFunctionCoefficient.
// In some cases the coefficients are independent of any movement of
// the physical coordinates (i.e. changes in 'elfun'), e.g. when the
// coefficient is a ConstantCoefficient or a GridFunctionCoefficient.
for (int i = 0; i < ir->GetNPoints(); i++)
{
@@ -1161,7 +990,6 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy += weight * val;
}
delete Tpr;
return energy;
}
@@ -1188,7 +1016,7 @@ void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
elvect = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
// Limited case.
DenseMatrix pos0;
@@ -1244,8 +1072,6 @@ void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
P *= weight_m;
AddMultABt(DS, P, PMatO);
// TODO: derivatives of adaptivity-based targets.
if (coeff0)
{
el.CalcShape(ip, shape);
@@ -1281,7 +1107,7 @@ void TMOP_Integrator::AssembleElementGrad(const FiniteElement &el,
elmat = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
// Limited case.
DenseMatrix pos0, grad_grad;
@@ -1334,8 +1160,6 @@ void TMOP_Integrator::AssembleElementGrad(const FiniteElement &el,
metric->AssembleH(Jpt, DS, weight_m, elmat);
// TODO: derivatives of adaptivity-based targets.
if (coeff0)
{
el.CalcShape(ip, shape);
@@ -1410,12 +1234,11 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
for (int i = 0; i < fes->GetNE(); i++)
{
fe = fes->GetFE(i);
targetC->ComputeElementTargets(i, *fe, *ir, Jtr);
fes->GetElementVDofs(i, vdofs);
x.GetSubVector(vdofs, x_vals);
PMatI.UseExternalData(x_vals.GetData(), dof, dim);
targetC->ComputeElementTargets(i, *fe, *ir, x_vals, Jtr);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
@@ -1451,6 +1274,9 @@ void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
const IntegrationRule &ir = metric_gf.FESpace()->GetFE(i)->GetNodes();
const int nsp = ir.GetNPoints(), dof = fe_pos.GetDof();
W.SetSize(dim, dim, nsp);
tc.ComputeElementTargets(i, fe_pos, ir, W);
dshape.SetSize(dof, dim);
pos.SetSize(dof, dim);
posV.SetDataAndSize(pos.Data(), dof * dim);
@@ -1459,9 +1285,6 @@ void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
nodes.FESpace()->GetElementVDofs(i, pos_dofs);
nodes.GetSubVector(pos_dofs, posV);
W.SetSize(dim, dim, nsp);
tc.ComputeElementTargets(i, fe_pos, ir, posV, W);
for (int j = 0; j < nsp; j++)
{
const DenseMatrix &Wj = W(j);
+3 -124
View File
@@ -12,6 +12,7 @@
#ifndef MFEM_TMOP_HPP
#define MFEM_TMOP_HPP
#include "../config/config.hpp"
#include "../linalg/invariants.hpp"
#include "nonlininteg.hpp"
@@ -513,51 +514,6 @@ public:
virtual ~TMOP_QuadraticLimiter() { }
};
class FiniteElementCollection;
class FiniteElementSpace;
class ParFiniteElementSpace;
class AdaptivityEvaluator
{
protected:
// Owned.
Mesh *mesh;
FiniteElementSpace *fes;
#ifdef MFEM_USE_MPI
// Owned.
ParMesh *pmesh;
ParFiniteElementSpace *pfes;
#endif
public:
AdaptivityEvaluator() : mesh(NULL), fes(NULL)
{
#ifdef MFEM_USE_MPI
pmesh = NULL;
pfes = NULL;
#endif
}
virtual ~AdaptivityEvaluator();
/** Specifies the Mesh and FiniteElementCollection of the solution that will
be evaluated. The given mesh will be copied into the internal object. */
void SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec, int num_comp);
#ifdef MFEM_USE_MPI
/// Parallel version of SetSerialMetaInfo.
void SetParMetaInfo(const ParMesh &m,
const FiniteElementCollection &fec, int num_comp);
#endif
// TODO use GridFunctions to make clear it's on the ldofs?
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field) = 0;
virtual void ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field) = 0;
};
/** @brief Base class representing target-matrix construction algorithms for
mesh optimization via the target-matrix optimization paradigm (TMOP). */
@@ -582,11 +538,9 @@ public:
IDEAL_SHAPE_GIVEN_SIZE, /**<
Ideal shape, given size/volume; the given nodes define the target
volume at all quadrature points. */
GIVEN_SHAPE_AND_SIZE, /**<
GIVEN_SHAPE_AND_SIZE /**<
Given shape, given size/volume; the given nodes define the exact target
Jacobian matrix at all quadrature points. */
GIVEN_FULL /**<
Full target tensor is specified at every quadrature point. */
};
protected:
@@ -635,89 +589,14 @@ public:
void SetVolumeScale(double vol_scale) { volume_scale = vol_scale; }
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun. */
transformation Jacobians for each quadrature point in the element. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
class AnalyticAdaptTC : public TargetConstructor
{
protected:
// Analytic target specification.
Coefficient *scalar_tspec;
VectorCoefficient *vector_tspec;
MatrixCoefficient *matrix_tspec;
public:
AnalyticAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
scalar_tspec(NULL), vector_tspec(NULL), matrix_tspec(NULL) { }
virtual void SetAnalyticTargetSpec(Coefficient *sspec,
VectorCoefficient *vspec,
MatrixCoefficient *mspec);
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
class ParGridFunction;
class DiscreteAdaptTC : public TargetConstructor
{
protected:
// Discrete target specification.
// Data is owned, updated by UpdateTargetSpecification.
Vector target_spec;
// Note: do not use the Nodes of this space as they may not be on the
// positions corresponding to the values of tspec.
const FiniteElementSpace *tspec_fes;
// Evaluation of the discrete target specification on different meshes.
// Owned.
AdaptivityEvaluator *adapt_eval;
public:
DiscreteAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
target_spec(), tspec_fes(NULL), adapt_eval(NULL) { }
virtual ~DiscreteAdaptTC() { delete adapt_eval; }
virtual void SetSerialDiscreteTargetSpec(GridFunction &tspec);
#ifdef MFEM_USE_MPI
virtual void SetParDiscreteTargetSpec(ParGridFunction &tspec);
#endif
/** Used to update the target specification after the mesh has changed. The
new mesh positions are given by new_x. */
void UpdateTargetSpecification(const Vector &new_x);
void SetAdaptivityEvaluator(AdaptivityEvaluator *ae)
{
if (adapt_eval) { delete adapt_eval; }
adapt_eval = ae;
}
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun.
Note that this function assumes that UpdateTargetSpecification() has
been called with the position vector corresponding to @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
/** @brief A TMOP integrator class based on any given TMOP_QualityMetric and
TargetConstructor.
-518
View File
@@ -1,518 +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 "tmop_tools.hpp"
#include "nonlinearform.hpp"
#include "pnonlinearform.hpp"
#include "../general/osockstream.hpp"
namespace mfem
{
using namespace mfem;
void AdvectorCG::SetInitialField(const Vector &init_nodes,
const Vector &init_field)
{
nodes0 = init_nodes;
field0 = init_field;
}
void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field)
{
int myid = 0;
Mesh *m = mesh;
#ifdef MFEM_USE_MPI
if (pfes) { MPI_Comm_rank(pfes->GetComm(), &myid); }
if (pmesh) { m = pmesh; }
#endif
MFEM_VERIFY(m != NULL, "No mesh has been given to the AdaptivityEvaluator.");
// This will be used to move the positions.
GridFunction *mesh_nodes = m->GetNodes();
*mesh_nodes = nodes0;
new_field = field0;
// Velocity of the positions.
GridFunction u(mesh_nodes->FESpace());
subtract(new_nodes, nodes0, u);
TimeDependentOperator *oper = NULL;
// This must be the fes of the ind, associated with the object's mesh.
if (fes) { oper = new SerialAdvectorCGOper(nodes0, u, *fes); }
#ifdef MFEM_USE_MPI
else if (pfes) { oper = new ParAdvectorCGOper(nodes0, u, *pfes); }
#endif
MFEM_VERIFY(oper != NULL,
"No FE space has been given to the AdaptivityEvaluator.");
ode_solver.Init(*oper);
// Compute some time step [mesh_size / speed].
double min_h = std::numeric_limits<double>::infinity();
for (int i = 0; i < m->GetNE(); i++)
{
min_h = std::min(min_h, m->GetElementSize(i));
}
double v_max = 0.0;
const int s = u.FESpace()->GetVSize() / 2;
for (int i = 0; i < s; i++)
{
const double vel = u(i) * u(i) + u(i+s) * u(i+s);
v_max = std::max(v_max, vel);
}
if (v_max == 0.0)
{
// No need to change the field.
return;
}
v_max = std::sqrt(v_max);
double dt = 0.5 * min_h / v_max;
double glob_dt = dt;
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(&dt, &glob_dt, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
}
#endif
double t = 0.0;
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
if (t + glob_dt >= 1.0)
{
#ifdef MFEM_DEBUG
if (myid == 0)
{
mfem::out << "Remap took " << ti << " steps." << std::endl;
}
#endif
glob_dt = 1.0 - t;
last_step = true;
}
ode_solver.Step(new_field, t, glob_dt);
}
// Trim the overshoots and undershoots.
const double minv = field0.Min(), maxv = field0.Max();
for (int i = 0; i < new_field.Size(); i++)
{
if (new_field(i) < minv) { new_field(i) = minv; }
if (new_field(i) > maxv) { new_field(i) = maxv; }
}
nodes0 = new_nodes;
field0 = new_field;
delete oper;
}
SerialAdvectorCGOper::SerialAdvectorCGOper(const Vector &x_start,
GridFunction &vel,
FiniteElementSpace &fes)
: TimeDependentOperator(fes.GetVSize()),
x0(x_start), x_now(*fes.GetMesh()->GetNodes()),
u(vel), u_coeff(&u), M(&fes), K(&fes)
{
ConvectionIntegrator *Kinteg = new ConvectionIntegrator(u_coeff);
K.AddDomainIntegrator(Kinteg);
K.Assemble(0);
K.Finalize(0);
MassIntegrator *Minteg = new MassIntegrator;
M.AddDomainIntegrator(Minteg);
M.Assemble();
M.Finalize();
}
void SerialAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
{
// Move the mesh.
const double t = GetTime();
add(x0, t, u, x_now);
// Assemble on the new mesh.
K.BilinearForm::operator=(0.0);
K.Assemble();
Vector rhs(K.Size());
K.Mult(ind, rhs);
M.BilinearForm::operator=(0.0);
M.Assemble();
di_dt = 0.0;
CGSolver lin_solver;
DSmoother prec;
lin_solver.SetPreconditioner(prec);
lin_solver.SetOperator(M.SpMat());
lin_solver.SetRelTol(1e-12); lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(rhs, di_dt);
}
#ifdef MFEM_USE_MPI
ParAdvectorCGOper::ParAdvectorCGOper(const Vector &x_start,
GridFunction &vel,
ParFiniteElementSpace &pfes)
: TimeDependentOperator(pfes.GetVSize()),
x0(x_start), x_now(*pfes.GetMesh()->GetNodes()),
u(vel), u_coeff(&u), M(&pfes), K(&pfes)
{
ConvectionIntegrator *Kinteg = new ConvectionIntegrator(u_coeff);
K.AddDomainIntegrator(Kinteg);
K.Assemble(0);
K.Finalize(0);
MassIntegrator *Minteg = new MassIntegrator;
M.AddDomainIntegrator(Minteg);
M.Assemble();
M.Finalize();
}
void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
{
// Move the mesh.
const double t = GetTime();
add(x0, t, u, x_now);
// Assemble on the new mesh.
K.BilinearForm::operator=(0.0);
K.Assemble();
ParGridFunction rhs(K.ParFESpace());
K.Mult(ind, rhs);
M.BilinearForm::operator=(0.0);
M.Assemble();
HypreParVector *RHS = rhs.ParallelAssemble();
HypreParVector X(K.ParFESpace());
X = 0.0;
HypreParMatrix *Mh = M.ParallelAssemble();
CGSolver lin_solver(M.ParFESpace()->GetParMesh()->GetComm());
HypreSmoother prec;
prec.SetType(HypreSmoother::Jacobi, 1);
lin_solver.SetPreconditioner(prec);
lin_solver.SetOperator(*Mh);
lin_solver.SetRelTol(1e-8);
lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(*RHS, X);
K.ParFESpace()->GetProlongationMatrix()->Mult(X, di_dt);
delete Mh;
delete RHS;
}
#endif
double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const FiniteElementSpace *fes = NULL;
double energy_in = 0.0;
#ifdef MFEM_USE_MPI
const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
if (parallel)
{
fes = p_nlf->FESpace();
energy_in = p_nlf->GetEnergy(x);
}
#endif
const bool serial = !parallel;
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
if (serial)
{
fes = nlf->FESpace();
energy_in = nlf->GetEnergy(x);
}
const bool have_b = (b.Size() == Height());
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_out(x.Size()), x_out_loc(fes->GetVSize());
bool x_out_ok = false;
double scale = 1.0, energy_out;
double norm0 = Norm(r);
// Decreases the scaling of the update until the new mesh is valid.
for (int i = 0; i < 12; i++)
{
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP) {x_out_loc.SetData(x_out.GetData());}
else {cP->Mult(x_out,x_out_loc);}
energy_out = nlf->GetGridFunctionEnergy(x_out_loc);
}
#ifdef MFEM_USE_MPI
else
{
fes->GetProlongationMatrix()->Mult(x_out, x_out_loc);
energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
}
#endif
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Increasing energy.\n"; }
scale *= 0.5; continue;
}
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
int jac_ok_all = jac_ok;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (jac_ok_all == 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= 0.5; continue;
}
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { scale = 0.0; }
return scale;
}
void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
{
if (discr_tc)
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
Vector x_loc(nlf->ParFESpace()->GetVSize());
nlf->ParFESpace()->GetProlongationMatrix()->Mult(x, x_loc);
discr_tc->UpdateTargetSpecification(x_loc);
#endif
}
else { discr_tc->UpdateTargetSpecification(x); }
}
}
double TMOPDescentNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const FiniteElementSpace *fes = NULL;
double energy_in = 0.0;
#ifdef MFEM_USE_MPI
const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
if (parallel)
{
fes = p_nlf->FESpace();
energy_in = p_nlf->GetEnergy(x);
}
#endif
const bool serial = !parallel;
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
if (serial)
{
fes = nlf->FESpace();
energy_in = nlf->GetEnergy(x);
}
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_loc(fes->GetVSize());
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_loc.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
}
double min_detJ_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (print_level >= 0)
{
mfem::out << "Minimum det(J) = " << min_detJ_all << '\n';
}
Vector x_out(x.Size());
bool x_out_ok = false;
double scale = 1.0, energy_out;
for (int i = 0; i < 7; i++)
{
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP) {x_loc.SetData(x_out.GetData());}
else {cP->Mult(x_out,x_loc);}
energy_out = nlf->GetGridFunctionEnergy(x_loc);
}
#ifdef MFEM_USE_MPI
else
{
fes->GetProlongationMatrix()->Mult(x_out, x_loc);
energy_out = p_nlf->GetParGridFunctionEnergy(x_loc);
}
#endif
if (energy_out > energy_in || std::isnan(energy_out) != 0)
{
scale *= 0.5;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { return 0.0; }
return scale;
}
void TMOPDescentNewtonSolver::ProcessNewState(const Vector &x) const
{
if (discr_tc)
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
Vector x_loc(nlf->ParFESpace()->GetVSize());
nlf->ParFESpace()->GetProlongationMatrix()->Mult(x, x_loc);
discr_tc->UpdateTargetSpecification(x_loc);
#endif
}
else { discr_tc->UpdateTargetSpecification(x); }
}
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_tmop_metric_p(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, ParMesh &pmesh,
char *title, int position)
{
L2_FECollection fec(order, pmesh.Dimension(), BasisType::GaussLobatto);
ParFiniteElementSpace fes(&pmesh, &fec, 1);
ParGridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, pmesh, metric);
socketstream sock;
if (pmesh.GetMyRank() == 0)
{
sock.open("localhost", 19916);
sock << "solution\n";
}
pmesh.PrintAsOne(sock);
metric.SaveAsOne(sock);
if (pmesh.GetMyRank() == 0)
{
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA\n";
}
}
#endif
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, Mesh &mesh,
char *title, int position)
{
L2_FECollection fec(order, mesh.Dimension(), BasisType::GaussLobatto);
FiniteElementSpace fes(&mesh, &fec, 1);
GridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, mesh, metric);
osockstream sock(19916, "localhost");
sock << "solution\n";
mesh.Print(sock);
metric.Save(sock);
sock.send();
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA\n";
}
}

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