Compare commits

..
135 changed files with 3617 additions and 12207 deletions
-1
View File
@@ -169,7 +169,6 @@ miniapps/performance/sol.*
miniapps/tools/display-basis
miniapps/tools/load-dc
miniapps/tools/convert-dc
miniapps/tools/lor-transfer
miniapps/nurbs/ex1
miniapps/nurbs/ex1p
+22 -85
View File
@@ -8,69 +8,12 @@
http://mfem.org
Version 4.0-RC1, Apr 11, 2019
=============================
Requirements and Limitations
----------------------------
- This is a release candidate for mfem-4.0.
- Use at your own risk -- not everything will work, the API may change.
- We are looking for feedback from friendly users.
- Unlike previous MFEM releases, this version requires a C++11 compiler.
- GPU-related limitations:
* NVCC is not supported in the CMake build system yet.
* Element batching is currently ignored.
* Full-assembly (on device), element assembly, and matrix-free bilinear forms
are not supported yet.
* FunctionCoefficients do not currently work on GPUs.
* Partial assembly kernels are not implemented yet for simplices.
GPU support
-----------
- Added initial support for hardware devices, such as GPUs, and programming
models, such as CUDA, OCCA, RAJA and OpenMP.
- The GPU/device support is based on MFEM's new backends and kernels working
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.
- 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
the linear algebra and finite element operations (e.g. partially assembled
bilinear forms) have been extended to take advantage of kernel acceleration by
simply replacing loops with the MFEM_FORALL() macro.
- 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
---------------------------
- 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 22.
- Added a variety of coefficients which are sums or products of existing
coefficients as well as grid function coefficients which return the
divergence, gradient, or curl of their GridFunctions.
Version 3.4.1 (development)
===========================
Support for wedge elements and meshes with mixed element types
--------------------------------------------------------------
- Added support for wedge-shaped mesh elements of arbitrary order (with Geometry
- Added support for wedge shaped mesh elements of arbitrary order (with Geometry
type PRISM) which have two triangular faces and three quadrilateral faces.
Several examples of such meshes can be found in the data/ directory.
@@ -96,18 +39,13 @@ Other meshing improvements
follows precisely the paper:
D. Arnold, A. Mukherjee, and L. Pouly, "Locally Adapted Tetrahedral Meshes
Using Bisection", SIAM J. Sci. Comput. 22 (2000), 431448.
Using Bisection", SIAM J. Sci. Comput., 22(2), 431448.
This guarantees that the shape regularity of the elements will be preserved
under refinement.
- Added support for parallel communication groups on non-conforming meshes.
- Improved parallel partitioning of non-conforming meshes. If the coarse mesh
elements are ordered as a sequence of face-neighbors, the parallel partitions
are now guaranteed to be continuous. To that end, inline quadrilateral and
hexahedral meshes are now by default ordered along a space-filling curve.
- A boundary in a NURBS mesh can now be connected with another boundary. Such a
periodic NURBS mesh is a simple way to impose periodic boundary conditions.
@@ -115,8 +53,24 @@ Other meshing improvements
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.
space-dependent limiting terms. Improved the TMOP objective functions by
more accurate normalization of the different terms.
Discretization improvements
---------------------------
- 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 22.
- Added a variety of coefficients which are sums or products of existing
coefficients as well as grid function coefficients which return the
divergence, gradient, or curl of their GridFunctions.
New and improved solvers and preconditioners
--------------------------------------------
- Added support for parallel ILU preconditioning via hypre's Euclid solver.
New and updated examples and miniapps
-------------------------------------
@@ -126,9 +80,6 @@ New and updated examples and miniapps
- Added a new meshing miniapp, Extruder, that demonstrates the capability to
produce 3D meshes by extruding 2D meshes.
- Added a simple miniapp, LOR Transfer, for visualizing the actions of the
transfer operators between a high-order and a low-order refined spaces.
- Added a new example, Example 20/20p, that solves a system of 1D ODEs derived
from a Hamiltonian. The example demonstrates the use of the variable order,
symplectic integration algorithm implemented in class SIAVSolver.
@@ -136,22 +87,10 @@ New and updated examples and miniapps
- 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
--------------------------------------------
- Added support for parallel ILU preconditioning via hypre's Euclid solver.
- Added support for STRUMPACK v3 with a small API change in the class
STRUMPACKSolver, see "API changes" below.
Miscellaneous
-------------
- 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.
- Altered the way FGMRES counts its iterations so that it matches GMRES.
- Various other simplifications, extensions, and bugfixes in the code.
@@ -176,8 +115,6 @@ API changes
- Removed the virtual method Element::GetRefinementFlag, it is only used by the
derived class Tetrahedron.
- Added new methods: Array::CopyTo, Tetrahedron::Init.
- In class STRUMPACKSolver, the method SetMC64Job() was replaced by the new
methods: DisableMatching(), EnableMatching(), and EnableParallelMatching().
Version 3.4, released on May 29, 2018
+5 -9
View File
@@ -13,11 +13,6 @@ cmake_minimum_required(VERSION 2.8.11)
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
"Path to optional user configuration file.")
# Require C++11 and disable compiler-specific extensions
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Load user settings before the defaults - this way the defaults will not
# overwrite the user set options. If the user has not set all options, we still
# have the defaults.
@@ -175,11 +170,12 @@ if (MFEM_USE_LAPACK)
endif()
# OpenMP
if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
if (NOT MFEM_THREAD_SAFE AND MFEM_USE_LEGACY_OPENMP)
message(FATAL_ERROR " *** MFEM_USE_LEGACY_OPENMP requires MFEM_THREAD_SAFE=ON.")
if (MFEM_USE_OPENMP)
if (MFEM_THREAD_SAFE)
find_package(OpenMP REQUIRED)
else()
message(FATAL_ERROR " *** MFEM_USE_OPENMP requires MFEM_THREAD_SAFE=ON.")
endif()
find_package(OpenMP REQUIRED)
endif()
# SuiteSparse (before SUNDIALS which may depend on KLU)
+7 -81
View File
@@ -21,24 +21,6 @@ requires an MPI C++ compiler, as well as the following external libraries:
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, 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
- OCCA support requires the OCCA library
https://libocca.org
- OpenMP support requires a compiler implementing the OpenMP API
https://www.openmp.org
- RAJA support requires installation of the RAJA performance portability layer
with (optionally) support for CUDA and OpenMP
https://github.com/LLNL/RAJA
The library supports two build systems: one based on GNU make, and a second one
based on CMake. Both build systems are described below. Some hints for building
without GNU make or CMake can be found at the end of this file.
@@ -65,10 +47,6 @@ Parallel build:
(build hypre 2.10.0b in ../hypre-2.10.0b relative to mfem/)
make parallel -j 4
CUDA build:
make cuda -j 4
(build for a specific compute capability: 'make cuda -j 4 CUDA_ARCH=sm_30')
Example codes (serial/parallel, depending on the build):
cd examples
make -j 4
@@ -79,6 +57,7 @@ Build everything (library, examples and miniapps) with current configuration:
Quick-check the build by running Example 1/1p (optional):
make check
Quick start with CMake
======================
Serial build:
@@ -153,10 +132,6 @@ are also defined:
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.
@@ -218,9 +193,8 @@ Configuration options (GNU make)
See the configuration file config/defaults.mk for the default settings.
Compilers:
CXX - C++ compiler, serial build
MPICXX - MPI C++ compiler, parallel build
CUDA_CXX - The CUDA compiler, 'nvcc'
CXX - C++ compiler, serial build
MPICXX - MPI C++ compiler, parallel build
Compiler options:
OPTIM_FLAGS - Options for optimized build
@@ -256,7 +230,7 @@ MFEM_DEBUG = YES/NO
and consistency checks that may simplify bug-hunting.
MFEM_USE_EXCEPTIONS = YES/NO
Enable the use of exceptions. In particular, modifies the default behavior
Enable the use of exceptions. In particular, modifies the default bahavior
when errors are encountered: throw an exception, instead of aborting.
MFEM_USE_LIBUNWIND = YES/NO
@@ -276,11 +250,8 @@ MFEM_THREAD_SAFE = YES/NO
Use thread-safe implementation for some classes/methods. This comes at the
cost of extra memory allocation and de-allocation.
MFEM_USE_LEGACY_OPENMP = YES/NO
Enable (basic) experimental OpenMP support. Requires MFEM_THREAD_SAFE.
MFEM_USE_OPENMP = YES/NO
Enable the OpenMP backend.
Enable (basic) experimental OpenMP support. Requires MFEM_THREAD_SAFE.
MFEM_USE_MEMALLOC = YES/NO
Internal MFEM option: enable batch allocation for some small objects.
@@ -391,29 +362,6 @@ 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.
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). This option requires MFEM_USE_MM. The variable CUDA_ARCH is
used to specify the CUDA compute capability used during compilation (by
default, CUDA_ARCH=sm_60). When enabled, this option uses the CUDA_* build
options, see below.
MFEM_USE_RAJA = YES/NO
Enable support for the RAJA performance portability layer in MFEM. RAJA
provides a portable abstraction for loops, supporting different programming
model backends. When using the RAJA CUDA backend, MFEM_USE_MM is required.
MFEM_USE_OCCA = YES/NO
Enables support for the OCCA library in MFEM. OCCA is an open-source library
which aims to make it easy to program different types of devices (e.g. CPU,
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
backends. When using the OCCA CUDA backend, MFEM_USE_MM is required.
MFEM_BUILD_TAG = (any value)
An optional tag to characterize the build. Exported to config/config.mk.
Can be used to identify the MFEM build from other makefiles.
@@ -449,8 +397,7 @@ The specific libraries and their options are:
http://math-atlas.sourceforge.net (ATLAS)
Options: LAPACK_OPT (currently not used/needed), LAPACK_LIB.
- OpenMP (optional), usually part of compiler, used when either MFEM_USE_OPENMP
or MFEM_USE_LEGACY_OPENMP is set to YES.
- OpenMP (optional), usually part of compiler, used when MFEM_USE_OPENMP = YES.
Options: OPENMP_OPT, OPENMP_LIB.
- High-resolution POSIX clocks: when using MFEM_TIMER_TYPE = 2, it may be
@@ -482,8 +429,7 @@ The specific libraries and their options are:
- STRUMPACK (optional), used when MFEM_USE_STRUMPACK = YES. Note that STRUMPACK
requires the PT-Scotch and Scalapack libraries as well as ParMETIS, which
includes METIS 5 in its distribution. Starting with STRUMPACK v2.2.0, ParMETIS
and PT-Scotch are optional dependencies.
includes METIS 5 in its distribution.
The support for STRUMPACK was added in MFEM v3.3.2 and it requires STRUMPACK
2.0.0 or later.
URL: http://portal.nersc.gov/project/sparse/strumpack
@@ -529,18 +475,6 @@ The specific libraries and their options are:
URL: https://scorec.rpi.edu/pumi
Options: PUMI_OPT, PUMI_LIB.
- CUDA, used when MFEM_USE_CUDA = YES.
URL: https://developer.nvidia.com/cuda-toolkit
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_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.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
- MPFR (optional), used when MFEM_USE_MPFR = YES.
URL: http://mpfr.org, it depends on the GMP library: https://gmplib.org
Options: MPFR_OPT, MPFR_LIB.
@@ -662,7 +596,6 @@ MFEM_USE_METIS - Set to ${MFEM_USE_MPI}, can be overwritten.
MFEM_USE_LIBUNWIND
MFEM_USE_LAPACK
MFEM_THREAD_SAFE
MFEM_USE_LEGACY_OPENMP
MFEM_USE_OPENMP
MFEM_USE_MEMALLOC
MFEM_TIMER_TYPE - Set automatically, can be overwritten.
@@ -676,13 +609,6 @@ MFEM_USE_MPFR
MFEM_USE_GZSTREAM
MFEM_USE_PUMI
The following GNU make options are not supported with CMake yet:
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_RAJA
MFEM_USE_MM
The following options are CMake specific:
MFEM_ENABLE_TESTING - Enable the ctest framework for testing.
-1
View File
@@ -25,7 +25,6 @@ set(MFEM_USE_LIBUNWIND @MFEM_USE_LIBUNWIND@)
set(MFEM_USE_LAPACK @MFEM_USE_LAPACK@)
set(MFEM_THREAD_SAFE @MFEM_THREAD_SAFE@)
set(MFEM_USE_OPENMP @MFEM_USE_OPENMP@)
set(MFEM_USE_LEGACY_OPENMP @MFEM_USE_LEGACY_OPENMP@)
set(MFEM_USE_MEMALLOC @MFEM_USE_MEMALLOC@)
set(MFEM_TIMER_TYPE @MFEM_TIMER_TYPE@)
set(MFEM_USE_SUNDIALS @MFEM_USE_SUNDIALS@)
+1 -4
View File
@@ -62,12 +62,9 @@
// allocation and de-allocation.
#cmakedefine MFEM_THREAD_SAFE
// Enable the OpenMP backend.
// Enable experimental OpenMP support. Requires MFEM_THREAD_SAFE.
#cmakedefine MFEM_USE_OPENMP
// [Deprecated] Enable experimental OpenMP support. Requires MFEM_THREAD_SAFE.
#cmakedefine MFEM_USE_LEGACY_OPENMP
// Enable MFEM functionality based on the Mesquite library.
#cmakedefine MFEM_USE_MESQUITE
@@ -714,11 +714,11 @@ function(mfem_export_mk_files)
# Convert Boolean vars to YES/NO without writting the values to cache
set(CONFIG_MK_BOOL_VARS MFEM_USE_MPI MFEM_USE_METIS MFEM_USE_METIS_5
MFEM_DEBUG MFEM_USE_EXCEPTIONS MFEM_USE_GZSTREAM MFEM_USE_LIBUNWIND
MFEM_USE_LAPACK MFEM_THREAD_SAFE MFEM_USE_OPENMP MFEM_USE_LEGACY_OPENMP
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_LAPACK MFEM_THREAD_SAFE MFEM_USE_OPENMP 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)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
-5
View File
@@ -15,9 +15,6 @@
//
// Otherwise, use the local file: _config.hpp.
#ifndef MFEM_CONFIG_HPP
#define MFEM_CONFIG_HPP
#ifdef MFEM_BUILD_DIR
#define MFEM_QUOTE(a) #a
#define MFEM_MAKE_PATH(x,y) MFEM_QUOTE(x/y)
@@ -55,5 +52,3 @@
#error Building with PUMI (MFEM_USE_PUMI=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
#endif // MFEM_USE_MPI not defined
#endif // MFEM_CONFIG_HPP
+1 -23
View File
@@ -30,12 +30,6 @@
#define MFEM_VERSION_MINOR (((MFEM_VERSION)/100)%100)
#define MFEM_VERSION_PATCH ((MFEM_VERSION)%100)
// The absolute path of the MFEM source prefix
// #define MFEM_SOURCE_DIR "@MFEM_SOURCE_DIR@"
// The absolute path of the MFEM installation prefix
// #define MFEM_INSTALL_DIR "@MFEM_INSTALL_DIR@"
// Description of the git commit used to build MFEM.
// #define MFEM_GIT_STRING "@MFEM_GIT_STRING@"
@@ -68,12 +62,9 @@
// allocation and de-allocation.
// #define MFEM_THREAD_SAFE
// Enable the OpenMP backend.
// Enable experimental OpenMP support. Requires MFEM_THREAD_SAFE.
// #define MFEM_USE_OPENMP
// [Deprecated] Enable experimental OpenMP support. Requires MFEM_THREAD_SAFE.
// #define MFEM_USE_LEGACY_OPENMP
// Internal MFEM option: enable group/batch allocation for some small objects.
// #define MFEM_USE_MEMALLOC
@@ -121,19 +112,6 @@
// Enable MFEM functionality based on the PUMI library
// #define MFEM_USE_PUMI
// Build the GPU/CUDA-enabled version of the MFEM library.
// Requires a CUDA compiler (nvcc).
// #define MFEM_USE_CUDA
// 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@
+28 -38
View File
@@ -10,41 +10,34 @@
# Software Foundation) version 2.1 dated February 1999.
# Variables corresponding to defines in config.hpp (YES, NO, or value)
MFEM_VERSION = @MFEM_VERSION@
MFEM_VERSION_STRING = @MFEM_VERSION_STRING@
MFEM_SOURCE_DIR = @MFEM_SOURCE_DIR@
MFEM_INSTALL_DIR = @MFEM_INSTALL_DIR@
MFEM_GIT_STRING = @MFEM_GIT_STRING@
MFEM_USE_MPI = @MFEM_USE_MPI@
MFEM_USE_METIS = @MFEM_USE_METIS@
MFEM_USE_METIS_5 = @MFEM_USE_METIS_5@
MFEM_DEBUG = @MFEM_DEBUG@
MFEM_USE_EXCEPTIONS = @MFEM_USE_EXCEPTIONS@
MFEM_USE_GZSTREAM = @MFEM_USE_GZSTREAM@
MFEM_USE_LIBUNWIND = @MFEM_USE_LIBUNWIND@
MFEM_USE_LAPACK = @MFEM_USE_LAPACK@
MFEM_THREAD_SAFE = @MFEM_THREAD_SAFE@
MFEM_USE_LEGACY_OPENMP = @MFEM_USE_LEGACY_OPENMP@
MFEM_USE_OPENMP = @MFEM_USE_OPENMP@
MFEM_USE_MEMALLOC = @MFEM_USE_MEMALLOC@
MFEM_TIMER_TYPE = @MFEM_TIMER_TYPE@
MFEM_USE_SUNDIALS = @MFEM_USE_SUNDIALS@
MFEM_USE_MESQUITE = @MFEM_USE_MESQUITE@
MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
MFEM_USE_GECKO = @MFEM_USE_GECKO@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
MFEM_USE_MPFR = @MFEM_USE_MPFR@
MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
MFEM_USE_PUMI = @MFEM_USE_PUMI@
MFEM_USE_CUDA = @MFEM_USE_CUDA@
MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
MFEM_USE_MM = @MFEM_USE_MM@
MFEM_VERSION = @MFEM_VERSION@
MFEM_VERSION_STRING = @MFEM_VERSION_STRING@
MFEM_GIT_STRING = @MFEM_GIT_STRING@
MFEM_USE_MPI = @MFEM_USE_MPI@
MFEM_USE_METIS = @MFEM_USE_METIS@
MFEM_USE_METIS_5 = @MFEM_USE_METIS_5@
MFEM_DEBUG = @MFEM_DEBUG@
MFEM_USE_EXCEPTIONS = @MFEM_USE_EXCEPTIONS@
MFEM_USE_GZSTREAM = @MFEM_USE_GZSTREAM@
MFEM_USE_LIBUNWIND = @MFEM_USE_LIBUNWIND@
MFEM_USE_LAPACK = @MFEM_USE_LAPACK@
MFEM_THREAD_SAFE = @MFEM_THREAD_SAFE@
MFEM_USE_OPENMP = @MFEM_USE_OPENMP@
MFEM_USE_MEMALLOC = @MFEM_USE_MEMALLOC@
MFEM_TIMER_TYPE = @MFEM_TIMER_TYPE@
MFEM_USE_SUNDIALS = @MFEM_USE_SUNDIALS@
MFEM_USE_MESQUITE = @MFEM_USE_MESQUITE@
MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
MFEM_USE_GECKO = @MFEM_USE_GECKO@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
MFEM_USE_MPFR = @MFEM_USE_MPFR@
MFEM_USE_SIDRE = @MFEM_USE_SIDRE@
MFEM_USE_CONDUIT = @MFEM_USE_CONDUIT@
MFEM_USE_PUMI = @MFEM_USE_PUMI@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
@@ -72,8 +65,5 @@ MFEM_MPIEXEC = @MFEM_MPIEXEC@
MFEM_MPIEXEC_NP = @MFEM_MPIEXEC_NP@
MFEM_MPI_NP = @MFEM_MPI_NP@
# The NVCC compiler cannot link with -x=cu
MFEM_LINK_FLAGS := $(filter-out -x=cu, $(MFEM_FLAGS))
# Optional extra configuration
@MFEM_CONFIG_EXTRA@
+2 -5
View File
@@ -26,8 +26,7 @@ option(MFEM_USE_GZSTREAM "Enable gzstream for compressed data streams." OFF)
option(MFEM_USE_LIBUNWIND "Enable backtrace for errors." OFF)
option(MFEM_USE_LAPACK "Enable LAPACK usage" OFF)
option(MFEM_THREAD_SAFE "Enable thread safety" OFF)
option(MFEM_USE_OPENMP "Enable the OpenMP backend" OFF)
option(MFEM_USE_LEGACY_OPENMP "Enable legacy OpenMP usage" OFF)
option(MFEM_USE_OPENMP "Enable OpenMP usage" OFF)
option(MFEM_USE_MEMALLOC "Enable the internal MEMALLOC option." ON)
option(MFEM_USE_SUNDIALS "Enable SUNDIALS usage" OFF)
option(MFEM_USE_MESQUITE "Enable MESQUITE usage" OFF)
@@ -103,7 +102,6 @@ set(SuperLUDist_REQUIRED_PACKAGES "MPI" "BLAS" "ParMETIS" CACHE STRING
set(STRUMPACK_DIR "${MFEM_DIR}/../STRUMPACK-build" CACHE PATH
"Path to the STRUMPACK library.")
# STRUMPACK may also depend on "OpenMP", depending on how it was compiled.
# Starting with v2.2.0 of STRUMPACK, ParMETIS and Scotch are optional.
set(STRUMPACK_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "ParMETIS" "METIS"
"ScaLAPACK" "Scotch/ptscotch/ptscotcherr/scotch/scotcherr" CACHE STRING
"Additional packages required by STRUMPACK.")
@@ -111,8 +109,7 @@ set(STRUMPACK_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "ParMETIS" "METIS"
# set(STRUMPACK_REQUIRED_LIBRARIES "gfortran" "mpi_mpifh" CACHE STRING
# "Additional libraries required by STRUMPACK.")
# The Scotch library, required by STRUMPACK <= v2.1.0, optional in STRUMPACK >=
# v2.2.0.
# The Scotch library, required by STRUMPACK
set(Scotch_DIR "${MFEM_DIR}/../scotch_6.0.4" CACHE PATH
"Path to the Scotch and PT-Scotch libraries.")
set(Scotch_REQUIRED_PACKAGES "Threads" CACHE STRING
+37 -77
View File
@@ -21,13 +21,8 @@ NOTMAC := $(subst Darwin,,$(shell uname -s))
CXX = g++
MPICXX = mpicxx
BASE_FLAGS = -std=c++11
OPTIM_FLAGS = -O3 $(BASE_FLAGS)
DEBUG_FLAGS = -g $(XCOMPILER)-Wall $(BASE_FLAGS)
# Prefixes for passing flags to the compiler and linker when using CXX or MPICXX
CXX_XCOMPILER =
CXX_XLINKER = -Wl,
OPTIM_FLAGS = -O3
DEBUG_FLAGS = -g -Wall
# Destination location of make install
# PREFIX = $(HOME)/mfem
@@ -38,41 +33,33 @@ INSTALL = /usr/bin/install
STATIC = YES
SHARED = NO
# CUDA configuration options
CUDA_CXX = nvcc
CUDA_ARCH = sm_60
CUDA_FLAGS = -x=cu --expt-extended-lambda -arch=$(CUDA_ARCH)
# Prefixes for passing flags to the host compiler and linker when using CUDA_CXX
CUDA_XCOMPILER = -Xcompiler=
CUDA_XLINKER = -Xlinker=
ifneq ($(NOTMAC),)
AR = ar
ARFLAGS = cruv
RANLIB = ranlib
PICFLAG = $(XCOMPILER)-fPIC
PICFLAG = -fPIC
SO_EXT = so
SO_VER = so.$(MFEM_VERSION_STRING)
BUILD_SOFLAGS = -shared $(XLINKER)-soname,libmfem.$(SO_VER)
BUILD_RPATH = $(XLINKER)-rpath,$(BUILD_REAL_DIR)
BUILD_SOFLAGS = -shared -Wl,-soname,libmfem.$(SO_VER)
BUILD_RPATH = -Wl,-rpath,$(BUILD_REAL_DIR)
INSTALL_SOFLAGS = $(BUILD_SOFLAGS)
INSTALL_RPATH = $(XLINKER)-rpath,@MFEM_LIB_DIR@
INSTALL_RPATH = -Wl,-rpath,@MFEM_LIB_DIR@
else
# Silence "has no symbols" warnings on Mac OS X
AR = ar
ARFLAGS = Scruv
RANLIB = ranlib -no_warning_for_no_symbols
PICFLAG = $(XCOMPILER)-fPIC
PICFLAG = -fPIC
SO_EXT = dylib
SO_VER = $(MFEM_VERSION_STRING).dylib
MAKE_SOFLAGS = $(XLINKER)-dylib,-install_name,$(1)/libmfem.$(SO_VER),\
MAKE_SOFLAGS = -Wl,-dylib,-install_name,$(1)/libmfem.$(SO_VER),\
-compatibility_version,$(MFEM_VERSION_STRING),\
-current_version,$(MFEM_VERSION_STRING),\
-undefined,dynamic_lookup
BUILD_SOFLAGS = $(subst $1 ,,$(call MAKE_SOFLAGS,$(BUILD_REAL_DIR)))
BUILD_RPATH = $(XLINKER)-undefined,dynamic_lookup
BUILD_RPATH = -Wl,-undefined,dynamic_lookup
INSTALL_SOFLAGS = $(subst $1 ,,$(call MAKE_SOFLAGS,$(MFEM_LIB_DIR)))
INSTALL_RPATH = $(XLINKER)-undefined,dynamic_lookup
INSTALL_RPATH = -Wl,-undefined,dynamic_lookup
endif
# Set CXXFLAGS to overwrite the default selection of DEBUG_FLAGS/OPTIM_FLAGS
@@ -95,36 +82,31 @@ MFEM_MPI_NP = 4
# config.hpp. The values below are the defaults for generating the actual values
# in config.mk and config.hpp.
MFEM_USE_MPI = NO
MFEM_USE_METIS = $(MFEM_USE_MPI)
MFEM_USE_METIS_5 = NO
MFEM_DEBUG = NO
MFEM_USE_EXCEPTIONS = NO
MFEM_USE_GZSTREAM = NO
MFEM_USE_LIBUNWIND = NO
MFEM_USE_LAPACK = NO
MFEM_THREAD_SAFE = NO
MFEM_USE_OPENMP = NO
MFEM_USE_LEGACY_OPENMP = NO
MFEM_USE_MEMALLOC = YES
MFEM_TIMER_TYPE = $(if $(NOTMAC),2,4)
MFEM_USE_SUNDIALS = NO
MFEM_USE_MESQUITE = NO
MFEM_USE_SUITESPARSE = NO
MFEM_USE_SUPERLU = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_GECKO = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
MFEM_USE_MPFR = NO
MFEM_USE_SIDRE = NO
MFEM_USE_CONDUIT = NO
MFEM_USE_PUMI = NO
MFEM_USE_CUDA = NO
MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_MM = NO
MFEM_USE_MPI = NO
MFEM_USE_METIS = $(MFEM_USE_MPI)
MFEM_USE_METIS_5 = NO
MFEM_DEBUG = NO
MFEM_USE_EXCEPTIONS = NO
MFEM_USE_GZSTREAM = NO
MFEM_USE_LIBUNWIND = NO
MFEM_USE_LAPACK = NO
MFEM_THREAD_SAFE = NO
MFEM_USE_OPENMP = NO
MFEM_USE_MEMALLOC = YES
MFEM_TIMER_TYPE = $(if $(NOTMAC),2,4)
MFEM_USE_SUNDIALS = NO
MFEM_USE_MESQUITE = NO
MFEM_USE_SUITESPARSE = NO
MFEM_USE_SUPERLU = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_GECKO = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
MFEM_USE_MPFR = NO
MFEM_USE_SIDRE = NO
MFEM_USE_CONDUIT = NO
MFEM_USE_PUMI = NO
# Compile and link options for zlib.
ZLIB_DIR =
@@ -154,8 +136,6 @@ ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK),NONO)
else
# ParMETIS: currently needed by SuperLU or STRUMPACK. We assume that METIS 5
# (included with ParMETIS) is installed in the same location.
# Starting with STRUMPACK v2.2.0, ParMETIS is an optional dependency while
# METIS is still required.
METIS_DIR = @MFEM_DIR@/../parmetis-4.0.3
METIS_OPT = -I$(METIS_DIR)/include
METIS_LIB = -L$(METIS_DIR)/lib -lparmetis -lmetis
@@ -167,7 +147,7 @@ LAPACK_OPT =
LAPACK_LIB = $(if $(NOTMAC),-llapack -lblas,-framework Accelerate)
# OpenMP configuration
OPENMP_OPT = $(XCOMPILER)-fopenmp
OPENMP_OPT = -fopenmp
OPENMP_LIB =
# Used when MFEM_TIMER_TYPE = 2
@@ -203,8 +183,7 @@ SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_5.1.0
SUPERLU_OPT = -I$(SUPERLU_DIR)/SRC
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)
# SCOTCH library configuration (required by STRUMPACK)
SCOTCH_DIR = @MFEM_DIR@/../scotch_6.0.4
SCOTCH_OPT = -I$(SCOTCH_DIR)/include
SCOTCH_LIB = -L$(SCOTCH_DIR)/lib -lptscotch -lptscotcherr -lscotch -lscotcherr\
@@ -300,25 +279,6 @@ PUMI_OPT = -I$(PUMI_DIR)/include
PUMI_LIB = -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf -lpcu -lgmi -lparma\
-llion -lmth -lapf_zoltan -lspr
# CUDA library configuration. Since we compile and link with nvcc (when CUDA is
# enabled) we only need to explicitly link with the CUDA driver, libcuda.*,
# which is usually in a system path.
CUDA_OPT =
CUDA_LIB = $(if $(NOTMAC),,-L/usr/local/cuda/lib) -lcuda
# OCCA library configuration
OCCA_DIR ?= @MFEM_DIR@/../occa
OCCA_OPT = -I$(OCCA_DIR)/include
OCCA_LIB = $(XLINKER)-rpath,$(OCCA_DIR)/lib -L$(OCCA_DIR)/lib -locca
# RAJA library configuration
RAJA_DIR ?= @MFEM_DIR@/../raja
RAJA_OPT = -I$(RAJA_DIR)/include
ifdef CUB_DIR
RAJA_OPT += -I$(CUB_DIR)
endif
RAJA_LIB = $(XLINKER)-rpath,$(RAJA_DIR)/lib -L$(RAJA_DIR)/lib -lRAJA
# If YES, enable some informational messages
VERBOSE = NO
+3 -3
View File
@@ -19,10 +19,10 @@ elements
1 5 0 1 4 3 9 10 13 12
1 5 3 4 7 6 12 13 16 15
1 5 2 3 6 5 11 12 15 14
1 5 11 12 15 14 20 21 24 23
1 5 12 13 16 15 21 22 25 24
1 5 9 10 13 12 18 19 22 21
1 5 8 9 12 11 17 18 21 20
1 5 9 10 13 12 18 19 22 21
1 5 12 13 16 15 21 22 25 24
1 5 11 12 15 14 20 21 24 23
boundary
24
-1
View File
@@ -124,7 +124,6 @@ namespace mfem {
* - <a class="el" href="display-basis_8cpp_source.html">Display Basis</a>: visualize finite element basis functions
* - <a class="el" href="load-dc_8cpp_source.html">Load DC</a>: visualize fields saved via DataCollection classes
* - <a class="el" href="convert-dc_8cpp_source.html">Convert DC</a>: convert between diffirent DataCollection formats
* - <a class="el" href="lor-transfer_8cpp_source.html">LOR Transfer</a>: map functions between high-order and low-order refined spaces
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
*
-11
View File
@@ -27,7 +27,6 @@ list(APPEND ALL_EXE_SRCS
ex18.cpp
ex19.cpp
ex20.cpp
ex21.cpp
ex22.cpp
)
@@ -53,7 +52,6 @@ if (MFEM_USE_MPI)
ex18p.cpp
ex19p.cpp
ex20p.cpp
ex21p.cpp
ex22p.cpp
)
endif()
@@ -88,15 +86,6 @@ foreach(SRC_FILE ${ALL_EXE_SRCS})
endif()
endforeach()
# If STRUMPACK is enabled, add a test run that uses it.
if (MFEM_USE_STRUMPACK)
add_test(NAME ex11p_strumpack_np=4
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:ex11p> "-no-vis" "--strumpack"
${MPIEXEC_POSTFLAGS})
endif()
# Include the examples/sundials directory if SUNDIALS is enabled.
if (MFEM_USE_SUNDIALS)
add_subdirectory(sundials)
+22 -53
View File
@@ -25,14 +25,6 @@
// ex1 -m ../data/mobius-strip.mesh
// ex1 -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// > ex1 -pa -d cuda
// > ex1 -pa -d raja-cuda
// > ex1 -pa -d occa-cuda
// > ex1 -pa -d raja-omp
// > ex1 -pa -d occa-omp
// > ex1 -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
@@ -61,9 +53,7 @@ int main(int argc, char *argv[])
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device = "cpu";
bool visualization = true;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -73,10 +63,6 @@ int main(int argc, char *argv[])
" isoparametric space.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -148,65 +134,48 @@ 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
// 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;
// 9. Set up the bilinear form a(.,.) on the finite element space
// 8. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
BilinearForm *a = new BilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DiffusionIntegrator(one));
// 10. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, etc.
// 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(); }
a->Assemble();
OperatorPtr A;
SparseMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
cout << "Size of linear system: " << A->Height() << endl;
cout << "Size of linear system: " << A.Height() << endl;
// 11. Solve the linear system A X = B.
if (!pa)
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
// 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, 200, 1e-12, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
// 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
}
else // No preconditioning for now in partial assembly mode.
{
CG(*A, B, X, 1, 2000, 1e-12, 0.0);
}
// 12. Recover the solution as a finite element grid function.
// 11. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
// 13. Switch back to the host.
Device::Disable();
// 14. Save the refined mesh and the solution. This output can be viewed later
// 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);
@@ -215,7 +184,7 @@ int main(int argc, char *argv[])
sol_ofs.precision(8);
x.Save(sol_ofs);
// 15. Send the solution by socket to a GLVis server.
// 13. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -225,7 +194,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *mesh << x << flush;
}
// 16. Free the used memory.
// 14. Free the used memory.
delete a;
delete b;
delete fespace;
+2 -1
View File
@@ -261,7 +261,8 @@ int main(int argc, char *argv[])
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->DisableMatching();
strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
precond = strumpack;
+27 -45
View File
@@ -25,11 +25,6 @@
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// > mpirun -np 4 ex1p -pa -d cuda
// > mpirun -np 4 ex1p -pa -d occa-cuda
// > mpirun -np 4 ex1p -pa -d raja-omp
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
@@ -64,9 +59,7 @@ int main(int argc, char *argv[])
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device = "cpu";
bool visualization = true;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -76,10 +69,6 @@ int main(int argc, char *argv[])
" isoparametric space.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -177,58 +166,49 @@ 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.
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(fespace);
x = 0.0;
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
// 10. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DiffusionIntegrator(one));
// 12. Assemble the parallel bilinear form and the corresponding linear
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
OperatorPtr A;
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * With partial assembly, use no preconditioner, for now.
Solver *prec = NULL;
if (!pa) { prec = new HypreBoomerAMG; }
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec) { cg.SetPreconditioner(*prec); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
if (myid == 0)
{
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
// 14. Recover the parallel grid function corresponding to X. This is the
// 12. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
// preconditioner from hypre.
HypreSolver *amg = new HypreBoomerAMG(A);
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(200);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*amg);
pcg->Mult(B, X);
// 13. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 15. Switch back to the host.
Device::Disable();
// 16. Save the refined mesh and the solution in parallel. This output can
// 14. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
@@ -244,7 +224,7 @@ int main(int argc, char *argv[])
x.Save(sol_ofs);
}
// 17. Send the solution by socket to a GLVis server.
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -255,7 +235,9 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 18. Free the used memory.
// 16. Free the used memory.
delete pcg;
delete amg;
delete a;
delete b;
delete fespace;
-477
View File
@@ -1,477 +0,0 @@
// MFEM Example 21
//
// Compile with: make ex21
//
// Sample runs: ex21 -m ../data/inline-segment.mesh -o 3
// ex21 -m ../data/inline-tri.mesh -o 3
// ex21 -m ../data/inline-quad.mesh -o 3
// ex21 -m ../data/inline-quad.mesh -o 3 -p 1
// ex21 -m ../data/inline-quad.mesh -o 3 -p 2
// ex21 -m ../data/inline-tet.mesh -o 2
// ex21 -m ../data/inline-hex.mesh -o 2
// ex21 -m ../data/inline-hex.mesh -o 2 -p 1
// ex21 -m ../data/inline-hex.mesh -o 2 -p 2
// ex21 -m ../data/star.mesh -o 2 -sigma 10.0
//
// Description: This example code demonstrates the use of MFEM to define and
// solve simple complex-valued linear systems. We implement three
// variants of a damped harmonic oscillator:
//
// 1) A scalar H1 field
// -Div(a Grad u) - omega^2 b u + i omega c u = 0
//
// 2) A vector H(Curl) field
// Curl(a Curl u) - omega^2 b u + i omega c u = 0
//
// 3) A vector H(Div) field
// -Grad(a Div u) - omega^2 b u + i omega c u = 0
//
// In each case the field is driven by a forced oscillation, with
// angular frequency omega, imposed at the boundary or a portion
// of the boundary.
//
// In electromagnetics the coefficients are typically named the
// permeability, mu = 1/a, permittivity, epsilon = b, and
// conductivity, sigma = c. The user can specify these constants
// using either set of names.
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
static double mu_ = 1.0;
static double epsilon_ = 1.0;
static double sigma_ = 20.0;
static double omega_ = 10.0;
double u0_real_exact(const Vector &);
double u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
bool check_for_inline_mesh(const char * mesh_file);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/inline-quad.mesh";
int ref_levels = 0;
int order = 1;
int prob = 0;
double freq = -1.0;
double a_coef = 0.0;
bool visualization = 1;
bool herm_conv = true;
bool exact_sol = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&prob, "-p", "--problem-type",
"Choose from 0: H_1, 1: H(Curl), or 2: H(Div) "
"damped harmonic oscillator.");
args.AddOption(&a_coef, "-a", "--stiffness-coef",
"Stiffness coefficient (spring constant or 1/mu).");
args.AddOption(&epsilon_, "-b", "--mass-coef",
"Mass coefficient (or epsilon).");
args.AddOption(&sigma_, "-c", "--damping-coef",
"Damping coefficient (or sigma).");
args.AddOption(&mu_, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon_, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma_, "-sigma", "--conductivity",
"Conductivity (or damping constant).");
args.AddOption(&freq, "-f", "--frequency",
"Frequency (in Hz).");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if ( a_coef != 0.0 )
{
mu_ = 1.0 / a_coef;
}
if ( freq > 0.0 )
{
omega_ = 2.0 * M_PI * freq;
}
exact_sol = check_for_inline_mesh(mesh_file);
if (exact_sol)
{
cout << "Identified an 'inline' mesh" << endl;
}
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, 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 resolution. In this example we do
// 'ref_levels' of uniform refinement where the user specifies
// the number of levels with the '-r' option.
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
// 4. Define a finite element space on the mesh. Here we use continuous
// Lagrange, Nedelec, or Raviart-Thomas finite elements of the specified
// order.
if (dim == 1 && prob != 0 )
{
cout << "Switching to problem type 0, H1 basis functions, "
<< "for 1 dimensional mesh." << endl;
prob = 0;
}
FiniteElementCollection *fec;
switch (prob)
{
case 0: fec = new H1_FECollection(order, dim); break;
case 1: fec = new ND_FECollection(order, dim); break;
case 2: fec = new RT_FECollection(order - 1, dim); break;
}
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
cout << "Number of finite element unknowns: " << fespace->GetTrueVSize()
<< endl;
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined based on the type
// of mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh->bdr_attributes.Size())
{
ess_bdr.SetSize(mesh->bdr_attributes.Max());
ess_bdr = 1;
if (exact_sol)
{
switch (prob)
{
case 0: ess_bdr = 0; ess_bdr[0] = 1; break;
default: ess_bdr = 1; ess_bdr[2] = 0; break;
}
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 6. Set up the linear form b(.) which corresponds to the
// right-hand side of the FEM linear system.
ComplexLinearForm b(fespace, conv);
b.Vector::operator=(0.0);
// 7. Define the solution vector u as a finite element grid function
// corresponding to fespace. Initialize u with initial guess of 1+0i
// or the exact solution if it is known.
ComplexGridFunction u(fespace);
ComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
switch (prob)
{
case 0:
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u0_r, u0_i); }
break;
case 1:
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u1_r, u1_i); }
break;
case 2:
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u2_r, u2_i); }
break;
}
if (visualization && exact_sol)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u_exact->real()
<< "window_title 'Exact Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u_exact->imag()
<< "window_title 'Exact Imaginary Part'" << flush;
}
// 8. Set up the sesquilinear form a(.,.) on the finite element
// space corresponding to the damped harmonic oscillator operator
// of the appropriate type:
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + i omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) - omega^2 b + i omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + i omega c
//
ConstantCoefficient stiffnessCoef(1.0/mu_);
ConstantCoefficient massCoef(-omega_ * omega_ * epsilon_);
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
SesquilinearForm *a = new SesquilinearForm(fespace, conv);
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
}
// 9. Assemble the bilinear form and the corresponding linear
// system, applying any necessary transformations such as:
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, etc.
a->Assemble();
OperatorHandle A;
Vector B, U;
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
u = 0.0;
U = 0.0;
{
ComplexSparseMatrix * Asp =
dynamic_cast<ComplexSparseMatrix*>(A.Ptr());
cout << "Size of linear system: "
<< 2 * Asp->real().Width() << endl << endl;
}
// 10. Define and apply a GMRES solver for AU=B.
{
GMRESSolver gmres;
gmres.SetOperator(*A.Ptr());
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(1000);
gmres.SetPrintLevel(1);
gmres.Mult(B, U);
}
// 11. Recover the solution as a finite element grid function and
// compute the errors if the exact solution is known.
a->RecoverFEMSolution(U, b, u);
if (exact_sol)
{
double err_r = -1.0;
double err_i = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
}
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
// 12. Save the refined mesh and the solution. This output can be
// viewed later using GLVis: "glvis -m mesh -g sol".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_r_ofs("sol_r.gf");
ofstream sol_i_ofs("sol_i.gf");
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
}
// 13. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u.real()
<< "window_title 'Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u.imag()
<< "window_title 'Comp Imaginary Part'" << flush;
}
if (visualization && exact_sol)
{
*u_exact -= u;
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *mesh << u_exact->real()
<< "window_title 'Exact-Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *mesh << u_exact->imag()
<< "window_title 'Exact-Comp Imaginary Part'" << flush;
}
if (visualization)
{
GridFunction u_t(fespace);
u_t = u.real();
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << u_t
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
int num_frames = 32;
int i = 0;
while (sol_sock)
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
add(cos( 2.0 * M_PI * t), u.real(),
sin(-2.0 * M_PI * t), u.imag(), u_t);
sol_sock << "solution\n" << *mesh << u_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
}
}
// 14. Free the used memory.
delete a;
delete u_exact;
delete fespace;
delete fec;
delete mesh;
return 0;
}
bool check_for_inline_mesh(const char * mesh_file)
{
string file(mesh_file);
size_t p0 = file.find_last_of("/");
string s0 = file.substr((p0==string::npos)?0:(p0+1),7);
return s0 == "inline-";
}
complex<double> u0_exact(const Vector &x)
{
int dim = x.Size();
complex<double> i(0.0, 1.0);
complex<double> alpha = (epsilon_ * omega_ - i * sigma_);
complex<double> kappa = std::sqrt(mu_ * omega_* alpha);
return std::exp(-i * kappa * x[dim - 1]);
}
double u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
double u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
}
-658
View File
@@ -1,658 +0,0 @@
// MFEM Example 21 - Parallel Version
//
// Compile with: make ex21p
//
// Sample runs: mpirun -np 4 ex21p -m ../data/inline-segment.mesh -o 3
// mpirun -np 4 ex21p -m ../data/inline-tri.mesh -o 3
// mpirun -np 4 ex21p -m ../data/inline-quad.mesh -o 3
// mpirun -np 4 ex21p -m ../data/inline-quad.mesh -o 3 -p 1
// mpirun -np 4 ex21p -m ../data/inline-quad.mesh -o 3 -p 2
// mpirun -np 4 ex21p -m ../data/inline-tet.mesh -o 2
// mpirun -np 4 ex21p -m ../data/inline-hex.mesh -o 2
// mpirun -np 4 ex21p -m ../data/inline-hex.mesh -o 2 -p 1
// mpirun -np 4 ex21p -m ../data/inline-hex.mesh -o 2 -p 2
// mpirun -np 4 ex21p -m ../data/star.mesh -o 2 -sigma 10.0
//
// Description: This example code demonstrates the use of MFEM to define and
// solve simple complex-valued linear systems. We implement three
// variants of a damped harmonic oscillator:
//
// 1) A scalar H1 field
// -Div(a Grad u) - omega^2 b u + i omega c u = 0
//
// 2) A vector H(Curl) field
// Curl(a Curl u) - omega^2 b u + i omega c u = 0
//
// 3) A vector H(Div) field
// -Grad(a Div u) - omega^2 b u + i omega c u = 0
//
// In each case the field is driven by a forced oscillation, with
// angular frequency omega, imposed at the boundary or a portion
// of the boundary.
//
// In electromagnetics the coefficients are typically named the
// permeability, mu = 1/a, permittivity, epsilon = b, and
// conductivity, sigma = c. The user can specify these constants
// using either set of names.
//
//#define MFEM_STRUMPACK_SRC
#include <fstream>
#include <iostream>
#include "mfem.hpp"
using namespace std;
using namespace mfem;
static double mu_ = 1.0;
static double epsilon_ = 1.0;
static double sigma_ = 20.0;
static double omega_ = 10.0;
double u0_real_exact(const Vector &);
double u0_imag_exact(const Vector &);
void u1_real_exact(const Vector &, Vector &);
void u1_imag_exact(const Vector &, Vector &);
void u2_real_exact(const Vector &, Vector &);
void u2_imag_exact(const Vector &, Vector &);
bool check_for_inline_mesh(const char * mesh_file);
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm comm = MPI_COMM_WORLD;
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/inline-quad.mesh";
int ser_ref_levels = 1;
int par_ref_levels = 1;
int order = 1;
int prob = 0;
double freq = -1.0;
double a_coef = 0.0;
bool visualization = 1;
bool herm_conv = true;
bool exact_sol = true;
#ifdef MFEM_USE_STRUMPACK
bool strumpack = false;
#endif
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&prob, "-p", "--problem-type",
"Choose from 0: H_1, 1: H(Curl), or 2: H(Div) "
"damped harmonic oscillator.");
args.AddOption(&a_coef, "-a", "--stiffness-coef",
"Stiffness coefficient (spring constant or 1/mu).");
args.AddOption(&epsilon_, "-b", "--mass-coef",
"Mass coefficient (or epsilon).");
args.AddOption(&sigma_, "-c", "--damping-coef",
"Damping coefficient (or sigma).");
args.AddOption(&mu_, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon_, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma_, "-sigma", "--conductivity",
"Conductivity (or damping constant).");
args.AddOption(&freq, "-f", "--frequency",
"Frequency (in Hz).");
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&strumpack, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use STRUMPACK's double complex linear solver.");
#endif
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
if ( a_coef != 0.0 )
{
mu_ = 1.0 / a_coef;
}
if ( freq > 0.0 )
{
omega_ = 2.0 * M_PI * freq;
}
exact_sol = check_for_inline_mesh(mesh_file);
if (myid == 0 && exact_sol)
{
cout << "Identified an 'inline' mesh" << endl;
}
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 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();
// 4. Refine the serial mesh on all processors to increase the resolution.
for (int l = 0; l < ser_ref_levels; l++)
{
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
// 6. Define a parallel finite element space on the parallel
// mesh. Here we use continuous Lagrange, Nedelec, or
// Raviart-Thomas finite elements of the specified order.
if (dim == 1 && prob != 0 )
{
if (myid == 0)
{
cout << "Switching to problem type 0, H1 basis functions, "
<< "for 1 dimensional mesh." << endl;
}
prob = 0;
}
FiniteElementCollection *fec;
switch (prob)
{
case 0: fec = new H1_FECollection(order, dim); break;
case 1: fec = new ND_FECollection(order, dim); break;
case 2: fec = new RT_FECollection(order - 1, dim); break;
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// based on the type of mesh and the problem type.
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
if (exact_sol)
{
switch (prob)
{
case 0: ess_bdr = 0; ess_bdr[0] = 1; break;
default: ess_bdr = 1; ess_bdr[2] = 0; break;
}
}
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system.
ParComplexLinearForm b(fespace, conv);
b.Vector::operator=(0.0);
// 9. Define the solution vector u as a parallel finite element
// grid function corresponding to fespace. Initialize u with
// initial guess of 1+0i or the exact solution if it is known.
ParComplexGridFunction u(fespace);
ParComplexGridFunction * u_exact = NULL;
if (exact_sol) { u_exact = new ParComplexGridFunction(fespace); }
FunctionCoefficient u0_r(u0_real_exact);
FunctionCoefficient u0_i(u0_imag_exact);
VectorFunctionCoefficient u1_r(dim, u1_real_exact);
VectorFunctionCoefficient u1_i(dim, u1_imag_exact);
VectorFunctionCoefficient u2_r(dim, u2_real_exact);
VectorFunctionCoefficient u2_i(dim, u2_imag_exact);
ConstantCoefficient zeroCoef(0.0);
ConstantCoefficient oneCoef(1.0);
Vector zeroVec(dim); zeroVec = 0.0;
Vector oneVec(dim); oneVec = 0.0; oneVec[(prob==2)?(dim-1):0] = 1.0;
VectorConstantCoefficient zeroVecCoef(zeroVec);
VectorConstantCoefficient oneVecCoef(oneVec);
switch (prob)
{
case 0:
u.ProjectBdrCoefficient(oneCoef, zeroCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u0_r, u0_i); }
break;
case 1:
u.ProjectBdrCoefficientTangent(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u1_r, u1_i); }
break;
case 2:
u.ProjectBdrCoefficientNormal(oneVecCoef, zeroVecCoef, ess_bdr);
if (exact_sol) { u_exact->ProjectCoefficient(u2_r, u2_i); }
break;
}
if (visualization && exact_sol)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact Imaginary Part'" << flush;
}
// 10. Set up the parallel sesquilinear form a(.,.) on the finite element
// space corresponding to the damped harmonic oscillator operator
// of the appropriate type:
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + i omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) - omega^2 b + i omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + i omega c
//
ConstantCoefficient stiffnessCoef(1.0/mu_);
ConstantCoefficient massCoef(-omega_ * omega_ * epsilon_);
ConstantCoefficient lossCoef(omega_ * sigma_);
ConstantCoefficient negMassCoef(omega_ * omega_ * epsilon_);
ParSesquilinearForm *a = new ParSesquilinearForm(fespace, conv);
switch (prob)
{
case 0:
a->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new MassIntegrator(massCoef),
new MassIntegrator(lossCoef));
break;
case 1:
a->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
case 2:
a->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef),
NULL);
a->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef),
new VectorFEMassIntegrator(lossCoef));
break;
}
// 10a. Set up the parallel bilinear form for the preconditioner
// corresponding to the appropriate operator if the STRUMPACK solver
// has not been selected.
//
// 0) A scalar H1 field
// -Div(a Grad) - omega^2 b + omega c
//
// 1) A vector H(Curl) field
// Curl(a Curl) + omega^2 b + omega c
//
// 2) A vector H(Div) field
// -Grad(a Div) - omega^2 b + omega c
//
ParBilinearForm *pcOp = NULL;
#ifdef MFEM_USE_STRUMPACK
if (!strumpack)
#endif
{
pcOp = new ParBilinearForm(fespace);
switch (prob)
{
case 0:
pcOp->AddDomainIntegrator(new DiffusionIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new MassIntegrator(lossCoef));
break;
case 1:
pcOp->AddDomainIntegrator(new CurlCurlIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(negMassCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
case 2:
pcOp->AddDomainIntegrator(new DivDivIntegrator(stiffnessCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(massCoef));
pcOp->AddDomainIntegrator(new VectorFEMassIntegrator(lossCoef));
break;
}
}
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, etc.
a->Assemble();
if (pcOp) { pcOp->Assemble(); }
OperatorHandle A;
Vector B, U;
a->FormLinearSystem(ess_tdof_list, u, b, A, U, B);
u = 0.0;
U = 0.0;
OperatorHandle PCOp;
if (pcOp) { pcOp->FormSystemMatrix(ess_tdof_list, PCOp); }
if (myid == 0)
{
ComplexHypreParMatrix * Ahyp =
dynamic_cast<ComplexHypreParMatrix*>(A.Ptr());
cout << "Size of linear system: "
<< 2 * Ahyp->real().GetGlobalNumRows() << endl << endl;
}
// 12. Define and apply a parallel FGMRES solver for AU=B with a
// block diagonal preconditioner based on the appropriate multigrid
// preconditioner from hypre or simply use STRUMPACK.
#ifdef MFEM_USE_STRUMPACK
if (!strumpack)
#endif
{
Array<HYPRE_Int> blockTrueOffsets;
blockTrueOffsets.SetSize(3);
blockTrueOffsets[0] = 0;
blockTrueOffsets[1] = PCOp.Ptr()->Height();
blockTrueOffsets[2] = PCOp.Ptr()->Height();
blockTrueOffsets.PartialSum();
BlockDiagonalPreconditioner BDP(blockTrueOffsets);
Operator * pc_r = NULL;
Operator * pc_i = NULL;
switch (prob)
{
case 0:
pc_r =
new HypreBoomerAMG(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()));
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
break;
case 1:
pc_r = new HypreAMS(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()),
fespace);
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
break;
case 2:
if (dim == 2 )
{
pc_r = new HypreAMS(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()),
fespace);
}
else
{
pc_r = new HypreADS(dynamic_cast<HypreParMatrix&>(*PCOp.Ptr()),
fespace);
}
pc_i = new ScaledOperator(pc_r,
(conv == ComplexOperator::HERMITIAN) ?
1.0:-1.0);
break;
}
BDP.SetDiagonalBlock(0, pc_r);
BDP.SetDiagonalBlock(1, pc_i);
BDP.owns_blocks = 0;
FGMRESSolver fgmres(MPI_COMM_WORLD);
fgmres.SetPreconditioner(BDP);
fgmres.SetOperator(*A.Ptr());
fgmres.SetRelTol(1e-12);
fgmres.SetMaxIter(1000);
fgmres.SetPrintLevel(1);
fgmres.Mult(B, U);
}
#ifdef MFEM_USE_STRUMPACK
else
{
ComplexHypreParMatrix * Ahyp =
dynamic_cast<ComplexHypreParMatrix*>(A.Ptr());
STRUMPACKRowLocCmplxMatrix A_strmp(Ahyp->real(), Ahyp->imag());
STRUMPACKCmplxSolver strmp(argc, argv, comm);
strmp.SetPrintFactorStatistics(true);
strmp.SetPrintSolveStatistics(true);
// strmp.SetKrylovSolver(strumpack::KrylovSolver::AUTO); // core dump
strmp.SetKrylovSolver(strumpack::KrylovSolver::DIRECT); // core dump
// strmp.SetKrylovSolver(strumpack::KrylovSolver::REFINE); // core dump
// strmp.SetKrylovSolver(strumpack::KrylovSolver::PREC_GMRES); // index out of range asserts from strumpack::DenseMatrix
// strmp.SetKrylovSolver(strumpack::KrylovSolver::GMRES); // WORKS
strmp.SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strmp.SetOperator(A_strmp);
strmp.SetFromCommandLine();
strmp.Mult(B, U);
}
#endif
// 13. Recover the parallel grid function corresponding to U. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(U, b, u);
if (exact_sol)
{
double err_r = -1.0;
double err_i = -1.0;
switch (prob)
{
case 0:
err_r = u.real().ComputeL2Error(u0_r);
err_i = u.imag().ComputeL2Error(u0_i);
break;
case 1:
err_r = u.real().ComputeL2Error(u1_r);
err_i = u.imag().ComputeL2Error(u1_i);
break;
case 2:
err_r = u.real().ComputeL2Error(u2_r);
err_i = u.imag().ComputeL2Error(u2_i);
break;
}
if ( myid == 0 )
{
cout << endl;
cout << "|| Re (u_h - u) ||_{L^2} = " << err_r << endl;
cout << "|| Im (u_h - u) ||_{L^2} = " << err_i << endl;
cout << endl;
}
}
// 14. Save the refined mesh and the solution in parallel. This output can be
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_r_name, sol_i_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_r_name << "sol_r." << setfill('0') << setw(6) << myid;
sol_i_name << "sol_i." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_r_ofs(sol_r_name.str().c_str());
ofstream sol_i_ofs(sol_i_name.str().c_str());
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
}
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u.real()
<< "window_title 'Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u.imag()
<< "window_title 'Comp Imaginary Part'" << flush;
}
if (visualization && exact_sol)
{
*u_exact -= u;
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact-Comp Real Part'" << flush;
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact-Comp Imaginary Part'" << flush;
}
if (visualization)
{
ParGridFunction u_t(fespace);
u_t = u.real();
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << u_t
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
if (myid == 0)
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
int num_frames = 32;
int i = 0;
while (sol_sock)
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
add(cos( 2.0 * M_PI * t), u.real(),
sin(-2.0 * M_PI * t), u.imag(), u_t);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock << "solution\n" << *pmesh << u_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
}
}
// 16. Free the used memory.
delete a;
delete u_exact;
delete pcOp;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
bool check_for_inline_mesh(const char * mesh_file)
{
string file(mesh_file);
size_t p0 = file.find_last_of("/");
string s0 = file.substr((p0==string::npos)?0:(p0+1),7);
return s0 == "inline-";
}
complex<double> u0_exact(const Vector &x)
{
int dim = x.Size();
complex<double> i(0.0, 1.0);
complex<double> alpha = (epsilon_ * omega_ - i * sigma_);
complex<double> kappa = std::sqrt(mu_ * omega_* alpha);
return std::exp(-i * kappa * x[dim - 1]);
}
double u0_real_exact(const Vector &x)
{
return u0_exact(x).real();
}
double u0_imag_exact(const Vector &x)
{
return u0_exact(x).imag();
}
void u1_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_real_exact(x);
}
void u1_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[0] = u0_imag_exact(x);
}
void u2_real_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_real_exact(x);
}
void u2_imag_exact(const Vector &x, Vector &v)
{
int dim = x.Size();
v.SetSize(dim); v = 0.0; v[dim-1] = u0_imag_exact(x);
}
-334
View File
@@ -1,334 +0,0 @@
// MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p
//
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
// mpirun -np 4 ex3p -m ../data/fichera.mesh
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
//
// Description: This example code solves a simple electromagnetic diffusion
// problem corresponding to the second order definite Maxwell
// equation curl curl E + E = f with boundary condition
// E x n = <given tangential field>. Here, we use a given exact
// solution E and compute the corresponding r.h.s. f.
// We discretize with Nedelec finite elements in 2D or 3D.
//
// The example demonstrates the use of H(curl) finite element
// spaces with the curl-curl and the (vector finite element) mass
// bilinear form, as well as the computation of discretization
// error when the exact solution is known. Static condensation is
// also illustrated.
//
// We recommend viewing examples 1-2 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 1;
bool static_cond = false;
bool visualization = 1;
#ifdef MFEM_USE_STRUMPACK
bool use_strumpack = false;
#endif
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
" solution.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&use_strumpack, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use STRUMPACK's double complex linear solver.");
#endif
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels =
(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
// spaces on them.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
pmesh->ReorientTetMesh();
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
VectorFunctionCoefficient f(sdim, f_exact);
ParLinearForm *b = new ParLinearForm(fespace);
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary edges will be used
// when eliminating the non-homogeneous boundary condition to modify the
// r.h.s. vector b.
ParGridFunction x(fespace);
VectorFunctionCoefficient E(sdim, E_exact);
x.ProjectCoefficient(E);
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl + sigma I, by adding the curl-curl and the
// mass domain integrators.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *sigma = new ConstantCoefficient(-1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (myid == 0)
{
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
StopWatch chrono;
chrono.Clear();
chrono.Start();
#ifdef MFEM_USE_STRUMPACK
if (use_strumpack)
{
Operator * Arow = new STRUMPACKRowLocMatrix(A);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
//Solver * precond = strumpack;
strumpack->Mult(B, X);
delete strumpack;
delete Arow;
}
else
#endif
{
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
// preconditioner from hypre.
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
HypreSolver *ams = new HypreAMS(A, prec_fespace);
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(500);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*ams);
pcg->Mult(B, X);
delete pcg;
delete ams;
}
chrono.Stop();
cout << "Solver time " << chrono.RealTime() << endl;
// 13. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 14. Compute and print the L^2 norm of the error.
{
double err = x.ComputeL2Error(E);
if (myid == 0)
{
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
}
}
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 17. Free the used memory.
delete a;
delete sigma;
delete muinv;
delete b;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
void E_exact(const Vector &x, Vector &E)
{
if (dim == 3)
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(2));
E(2) = sin(kappa * x(0));
}
else
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(0));
if (x.Size() == 3) { E(2) = 0.0; }
}
}
void f_exact(const Vector &x, Vector &f)
{
if (dim == 3)
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
}
else
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
if (x.Size() == 3) { f(2) = 0.0; }
}
}
+32 -59
View File
@@ -15,11 +15,6 @@
// ex6 -m ../data/square-disc-surf.mesh -o 2
// ex6 -m ../data/amr-quad.mesh
//
// Device sample runs:
// > ex6 -pa -d cuda
// > ex6 -pa -d occa-cuda
// > ex6 -pa -d raja-omp
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
// equation -Delta u = 1 with homogeneous Dirichlet boundary
@@ -48,19 +43,13 @@ int main(int argc, char *argv[])
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool pa = false;
const char *device = "cpu";
bool visualization = true;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -96,15 +85,10 @@ int main(int argc, char *argv[])
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
// 5. 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.
BilinearForm a(&fespace);
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
LinearForm b(&fespace);
ConstantCoefficient one(1.0);
@@ -114,18 +98,18 @@ int main(int argc, char *argv[])
a.AddDomainIntegrator(integ);
b.AddDomainIntegrator(new DomainLFIntegrator(one));
// 7. The solution vector x and the associated finite element grid function
// 6. The solution vector x and the associated finite element grid function
// will be maintained over the AMR iterations. We initialize it to zero.
GridFunction x(&fespace);
x = 0.0;
// 8. All boundary attributes will be used for essential (Dirichlet) BC.
// 7. All boundary attributes will be used for essential (Dirichlet) BC.
MFEM_VERIFY(mesh.bdr_attributes.Size() > 0,
"Boundary attributes required in the mesh.");
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
// 9. Connect to GLVis.
// 8. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
@@ -134,23 +118,23 @@ int main(int argc, char *argv[])
sol_sock.open(vishost, visport);
}
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the DiffusionIntegrator to
// recover a smoothed flux (gradient) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^sdim (i.e., vector-valued) space is used here.
// 9. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// that uses the ComputeElementFlux method of the DiffusionIntegrator to
// recover a smoothed flux (gradient) that is subtracted from the element
// flux to get an error indicator. We need to supply the space for the
// smoothed flux: an (H1)^sdim (i.e., vector-valued) space is used here.
FiniteElementSpace flux_fespace(&mesh, &fec, sdim);
ZienkiewiczZhuEstimator estimator(*integ, x, flux_fespace);
estimator.SetAnisotropic();
// 11. A refiner selects and refines elements based on a refinement strategy.
// 10. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 12. The main AMR loop. In each iteration we solve the problem on the
// 11. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
const int max_dofs = 50000;
for (int it = 0; ; it++)
@@ -159,55 +143,44 @@ int main(int argc, char *argv[])
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << cdofs << endl;
// 13. Assemble the right-hand side.
// 12. Assemble the stiffness matrix and the right-hand side.
a.Assemble();
b.Assemble();
// 14. Set Dirichlet boundary values in the GridFunction x.
// 13. Set Dirichlet boundary values in the GridFunction x.
// Determine the list of Dirichlet true DOFs in the linear system.
Array<int> ess_tdof_list;
x.ProjectBdrCoefficient(zero, ess_bdr);
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 15. Switch to the device and assemble the stiffness matrix.
Device::Enable();
a.Assemble();
// 16. Create the linear system: eliminate boundary conditions, constrain
// 14. Create the linear system: eliminate boundary conditions, constrain
// hanging nodes and possibly apply other transformations. The system
// will be solved for true (unconstrained) DOFs only.
OperatorPtr A;
SparseMatrix A;
Vector B, X;
const int copy_interior = 1;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
// 17. Solve the linear system A X = B.
if (!pa)
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 3, 200, 1e-12, 0.0);
// 15. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the linear system with PCG.
GSSmoother M(A);
PCG(A, M, B, X, 3, 200, 1e-12, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
// 15. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the
// the linear system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(B, X);
#endif
}
else // No preconditioning for now in partial assembly mode.
{
CG(*A, B, X, 3, 2000, 1e-12, 0.0);
}
// 18. After solving the linear system, reconstruct the solution as a
// 16. 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.
// 17. Send solution by socket to the GLVis server.
if (visualization && sol_sock.good())
{
sol_sock.precision(8);
@@ -220,7 +193,7 @@ int main(int argc, char *argv[])
break;
}
// 20. Call the refiner to modify the mesh. The refiner calls the error
// 18. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
@@ -231,7 +204,7 @@ int main(int argc, char *argv[])
break;
}
// 21. Update the space to reflect the new state of the mesh. Also,
// 19. Update the space to reflect the new state of the mesh. Also,
// interpolate the solution x so that it lies in the new space but
// represents the same function. This saves solver iterations later
// since we'll have a good initial guess of x in the next step.
@@ -240,7 +213,7 @@ int main(int argc, char *argv[])
fespace.Update();
x.Update();
// 22. Inform also the bilinear and linear forms that the space has
// 20. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
+35 -58
View File
@@ -15,11 +15,6 @@
// mpirun -np 4 ex6p -m ../data/square-disc-surf.mesh -o 2
// mpirun -np 4 ex6p -m ../data/amr-quad.mesh
//
// Device sample runs:
// > mpirun -np 4 ex6p -pa -d cuda
// > mpirun -np 4 ex6p -pa -d occa-cuda
// > mpirun -np 4 ex6p -pa -d raja-omp
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
// equation -Delta u = 1 with homogeneous Dirichlet boundary
@@ -54,8 +49,6 @@ int main(int argc, char *argv[])
// 2. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int order = 1;
bool pa = false;
const char *device = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
@@ -63,10 +56,6 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -117,15 +106,10 @@ int main(int argc, char *argv[])
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
// 7. 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.
ParBilinearForm a(&fespace);
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
ParLinearForm b(&fespace);
ConstantCoefficient one(1.0);
@@ -134,12 +118,12 @@ int main(int argc, char *argv[])
a.AddDomainIntegrator(integ);
b.AddDomainIntegrator(new DomainLFIntegrator(one));
// 9. The solution vector x and the associated finite element grid function
// 8. The solution vector x and the associated finite element grid function
// will be maintained over the AMR iterations. We initialize it to zero.
ParGridFunction x(&fespace);
x = 0;
// 10. Connect to GLVis.
// 9. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
@@ -161,7 +145,7 @@ int main(int argc, char *argv[])
sout.precision(8);
}
// 11. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// 10. Set up an error estimator. Here we use the Zienkiewicz-Zhu estimator
// with L2 projection in the smoothing step to better handle hanging
// nodes and parallel partitioning. We need to supply a space for the
// discontinuous flux (L2) and a space for the smoothed flux (H(div) is
@@ -175,14 +159,14 @@ int main(int argc, char *argv[])
// ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec, dim);
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, smooth_flux_fes);
// 12. A refiner selects and refines elements based on a refinement strategy.
// 11. A refiner selects and refines elements based on a refinement strategy.
// The strategy here is to refine elements with errors larger than a
// fraction of the maximum element error. Other strategies are possible.
// The refiner will call the given error estimator.
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
// 13. The main AMR loop. In each iteration we solve the problem on the
// 12. The main AMR loop. In each iteration we solve the problem on the
// current mesh, visualize the solution, and refine the mesh.
const int max_dofs = 100000;
for (int it = 0; ; it++)
@@ -194,48 +178,41 @@ int main(int argc, char *argv[])
cout << "Number of unknowns: " << global_dofs << endl;
}
// 14. Assemble the right-hand side and determine the list of true
// (i.e. parallel conforming) essential boundary dofs.
Array<int> ess_tdof_list;
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// 13. Assemble the stiffness matrix and the right-hand side. 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.
a.Assemble();
b.Assemble();
// 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.
// 14. Create the parallel linear system: eliminate boundary conditions,
// constrain hanging nodes and nodes across processor boundaries.
// The system will be solved for true (unconstrained/unique) DOFs only.
OperatorPtr A;
Vector B, X;
Array<int> ess_tdof_list;
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
HypreParMatrix A;
Vector B, X;
const int copy_interior = 1;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
// 17. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * With partial assembly, use no preconditioner, for now.
HypreBoomerAMG *amg = NULL;
if (!pa) { amg = new HypreBoomerAMG; amg->SetPrintLevel(0); }
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3); // print the first and the last iterations only
if (amg) { cg.SetPreconditioner(*amg); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete amg;
// 15. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
// preconditioner from hypre.
HypreBoomerAMG amg;
amg.SetPrintLevel(0);
CGSolver pcg(A.GetComm());
pcg.SetPreconditioner(amg);
pcg.SetOperator(A);
pcg.SetRelTol(1e-6);
pcg.SetMaxIter(200);
pcg.SetPrintLevel(3); // print the first and the last iterations only
pcg.Mult(B, X);
// 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();
// 16. Extract the parallel grid function corresponding to the finite element
// approximation X. This is the local solution on each processor.
a.RecoverFEMSolution(X, b, x);
// 19. Send the solution by socket to a GLVis server.
// 17. Send the solution by socket to a GLVis server.
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
@@ -251,7 +228,7 @@ int main(int argc, char *argv[])
break;
}
// 20. Call the refiner to modify the mesh. The refiner calls the error
// 18. Call the refiner to modify the mesh. The refiner calls the error
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
@@ -265,7 +242,7 @@ int main(int argc, char *argv[])
break;
}
// 21. Update the finite element space (recalculate the number of DOFs,
// 19. Update the finite element space (recalculate the number of DOFs,
// etc.) and create a grid function update matrix. Apply the matrix
// to any GridFunctions over the space. In this case, the update
// matrix is an interpolation matrix so the updated GridFunction will
@@ -273,7 +250,7 @@ int main(int argc, char *argv[])
fespace.Update();
x.Update();
// 22. Load balance the mesh, and update the space and solution. Currently
// 20. Load balance the mesh, and update the space and solution. Currently
// available only for nonconforming meshes.
if (pmesh.Nonconforming())
{
@@ -285,7 +262,7 @@ int main(int argc, char *argv[])
x.Update();
}
// 23. Inform also the bilinear and linear forms that the space has
// 21. Inform also the bilinear and linear forms that the space has
// changed.
a.Update();
b.Update();
+13 -12
View File
@@ -132,8 +132,8 @@ int main(int argc, char *argv[])
// 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. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
@@ -147,6 +147,7 @@ int main(int argc, char *argv[])
case 6: ode_solver = new RK6Solver; break;
default:
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
delete mesh;
return 3;
}
@@ -156,18 +157,18 @@ int main(int argc, char *argv[])
// 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));
// 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;
@@ -207,7 +208,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);
@@ -221,14 +222,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);
@@ -253,7 +254,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."
@@ -285,7 +286,7 @@ int main(int argc, char *argv[])
if (visualization)
{
sout << "solution\n" << mesh << u << flush;
sout << "solution\n" << *mesh << u << flush;
}
if (visit)
+3 -9
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 ex22
ex18 ex19 ex20 ex22
PAR_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p ex12p\
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex22p
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
@@ -96,12 +96,6 @@ ex15-test-seq: ex15
@$(call mfem-test,$<,, Serial example,-e 1)
ex15p-test-par: ex15p
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-e 1)
# Testing: optional tests
ifeq ($(MFEM_USE_STRUMPACK),YES)
ex11p-test-strumpack: ex11p
@$(call mfem-test,$<, $(RUN_MPI), STRUMPACK example,--strumpack)
test-par-YES: ex11p-test-strumpack
endif
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
@@ -118,7 +112,7 @@ clean-build:
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
@rm -rf Example5* Example9* Example15* Example16*
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f sphere_refined.* sol.* sol_u.* sol_p.*
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f deformed.* velocity.* elastic_energy.* mode_*
@rm -f ex16.mesh ex16-mesh.* ex16-init.* ex16-final.*
-4
View File
@@ -11,9 +11,7 @@
set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
bilininteg_ext.cpp
coefficient.cpp
datacollection.cpp
eltrans.cpp
@@ -35,9 +33,7 @@ set(SRCS
set(HDRS
bilinearform.hpp
bilinearform_ext.hpp
bilininteg.hpp
bilininteg_ext.hpp
coefficient.hpp
datacollection.hpp
eltrans.hpp
+23 -109
View File
@@ -12,7 +12,6 @@
// Implementation of class BilinearForm
#include "fem.hpp"
#include "../general/device.hpp"
#include <cmath>
namespace mfem
@@ -55,7 +54,7 @@ void BilinearForm::AllocMat()
int *I = dof_dof.GetI();
int *J = dof_dof.GetJ();
double *data = mfem::New<double>(I[height]);
double *data = new double[I[height]];
mat = new SparseMatrix(I, J, data, height, height, true, true, true);
*mat = 0.0;
@@ -63,7 +62,7 @@ void BilinearForm::AllocMat()
dof_dof.LoseData();
}
BilinearForm::BilinearForm(FiniteElementSpace * f)
BilinearForm::BilinearForm (FiniteElementSpace * f)
: Matrix (f->GetVSize())
{
fes = f;
@@ -75,10 +74,6 @@ BilinearForm::BilinearForm(FiniteElementSpace * f)
hybridization = NULL;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::FULL;
batch = 1;
ext = NULL;
}
BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
@@ -94,10 +89,6 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
precompute_sparsity = ps;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::FULL;
batch = 1;
ext = NULL;
// Copy the pointers to the integrators
dbfi = bf->dbfi;
@@ -112,48 +103,9 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
AllocMat();
}
void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
{
if (ext)
{
MFEM_ABORT("the assembly level has already been set!");
}
assembly = assembly_level;
switch (assembly)
{
case AssemblyLevel::FULL:
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:
mfem_error("Element assembly not supported yet... stay tuned!");
// ext = new EABilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext = new PABilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFBilinearFormExtension(this);
break;
default:
mfem_error("Unknown assembly level");
}
}
void BilinearForm::EnableStaticCondensation()
{
delete static_cond;
if (assembly != AssemblyLevel::FULL)
{
static_cond = NULL;
MFEM_WARNING("Static condensation not supported for this assembly level");
return;
}
static_cond = new StaticCondensation(fes);
if (static_cond->ReducesTrueVSize())
{
@@ -173,13 +125,6 @@ void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space,
const Array<int> &ess_tdof_list)
{
delete hybridization;
if (assembly != AssemblyLevel::FULL)
{
delete constr_integ;
hybridization = NULL;
MFEM_WARNING("Hybridization not supported for this assembly level");
return;
}
hybridization = new Hybridization(fes, constr_space);
hybridization->SetConstraintIntegrator(constr_integ);
hybridization->Init(ess_tdof_list);
@@ -234,9 +179,9 @@ void BilinearForm::Finalize (int skip_zeros)
if (hybridization) { hybridization->Finalize(); }
}
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
void BilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
{
dbfi.Append(bfi);
dbfi.Append (bfi);
}
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
@@ -342,29 +287,20 @@ void BilinearForm::AssembleBdrElementMatrix(
}
}
void BilinearForm::Assemble(int skip_zeros)
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();
return;
}
ElementTransformation *eltrans;
Mesh *mesh = fes -> GetMesh();
DenseMatrix elmat, *elmat_p;
int i;
if (mat == NULL)
{
AllocMat();
}
#ifdef MFEM_USE_LEGACY_OPENMP
#ifdef MFEM_USE_OPENMP
int free_element_matrices = 0;
if (!element_matrices)
{
@@ -375,7 +311,7 @@ void BilinearForm::Assemble(int skip_zeros)
if (dbfi.Size())
{
for (int i = 0; i < fes -> GetNE(); i++)
for (i = 0; i < fes -> GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs);
if (element_matrices)
@@ -432,7 +368,7 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
for (int i = 0; i < fes -> GetNBE(); i++)
for (i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
@@ -470,7 +406,7 @@ void BilinearForm::Assemble(int skip_zeros)
Array<int> vdofs2;
int nfaces = mesh->GetNumFaces();
for (int i = 0; i < nfaces; i++)
for (i = 0; i < nfaces; i++)
{
tr = mesh -> GetInteriorFaceTransformations (i);
if (tr != NULL)
@@ -515,7 +451,7 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
for (int i = 0; i < fes -> GetNBE(); i++)
for (i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
@@ -541,7 +477,7 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
#ifdef MFEM_USE_LEGACY_OPENMP
#ifdef MFEM_USE_OPENMP
if (free_element_matrices)
{
FreeElementMatrices();
@@ -584,16 +520,11 @@ void BilinearForm::ConformingAssemble()
width = mat->Width();
}
void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
Vector &b, OperatorHandle &A, Vector &X,
Vector &B, int copy_interior)
void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
SparseMatrix &A, Vector &X, Vector &B,
int copy_interior)
{
if (ext)
{
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
FormSystemMatrix(ess_tdof_list, A);
@@ -656,14 +587,8 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
}
void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
SparseMatrix &A)
{
if (ext)
{
ext->FormSystemMatrix(ess_tdof_list, A);
return;
}
// Finish the matrix assembly and perform BC elimination, storing the
// eliminated part of the matrix.
if (static_cond)
@@ -675,7 +600,7 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
static_cond->EliminateReducedTrueDofs(diag_policy);
static_cond->Finalize(); // finalize eliminated part
}
A.Reset(&static_cond->GetMatrix(), false);
A.MakeRef(static_cond->GetMatrix());
}
else
{
@@ -689,11 +614,11 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
}
if (hybridization)
{
A.Reset(&hybridization->GetMatrix(), false);
A.MakeRef(hybridization->GetMatrix());
}
else
{
A.Reset(mat, false);
A.MakeRef(*mat);
}
}
}
@@ -701,12 +626,6 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
void BilinearForm::RecoverFEMSolution(const Vector &X,
const Vector &b, Vector &x)
{
if (ext)
{
ext->RecoverFEMSolution(X, b, x);
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
if (!P) // conforming space
{
@@ -768,7 +687,7 @@ void BilinearForm::ComputeElementMatrices()
DenseMatrix tmp;
IsoparametricTransformation eltrans;
#ifdef MFEM_USE_LEGACY_OPENMP
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for private(tmp,eltrans)
#endif
for (int i = 0; i < num_elements; i++)
@@ -795,8 +714,7 @@ void BilinearForm::ComputeElementMatrices()
}
void BilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
const Vector &sol, Vector &rhs, DiagonalPolicy dpolicy)
{
Array<int> ess_dofs, conf_ess_dofs;
fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
@@ -970,8 +888,6 @@ void BilinearForm::Update(FiniteElementSpace *nfes)
}
height = width = fes->GetVSize();
if (ext) { ext->Update(); }
}
void BilinearForm::SetDiagonalPolicy(DiagonalPolicy policy)
@@ -995,8 +911,6 @@ BilinearForm::~BilinearForm()
for (k=0; k < fbfi.Size(); k++) { delete fbfi[k]; }
for (k=0; k < bfbfi.Size(); k++) { delete bfbfi[k]; }
}
delete ext;
}
+8 -85
View File
@@ -18,32 +18,12 @@
#include "gridfunc.hpp"
#include "linearform.hpp"
#include "bilininteg.hpp"
#include "bilinearform_ext.hpp"
#include "staticcond.hpp"
#include "hybridization.hpp"
namespace mfem
{
/// Enumeration defining the assembly level for bilinear and nonlinear form
/// classes derived from Operator.
enum class AssemblyLevel
{
/// Fully assembled form, i.e. a global sparse matrix in MFEM, Hypre or PETSC
/// format.
FULL,
/// Form assembled at element level, which computes and stores dense element
/// matrices.
ELEMENT,
/// Partially-assembled form, which computes and stores data only at
/// quadrature points.
PARTIAL,
/// "Matrix-free" form that computes all of its action on-the-fly without any
/// substantial storage.
NONE,
};
/** Class for bilinear form - "Matrix" with associated FE space and
BLFIntegrators. */
class BilinearForm : public Matrix
@@ -58,14 +38,6 @@ protected:
/// FE space on which the form lives. Not owned.
FiniteElementSpace *fes;
/// The form assembly level (full, partial, etc.)
AssemblyLevel assembly;
/// Element batch size used in the form action (1, 8, num_elems, etc.)
int batch;
/** Extension for supporting Full Assembly (FA), Element Assembly (EA),
Partial Assembly (PA), or Matrix Free assembly (MF). */
BilinearFormExtension *ext;
/// Indicates the Mesh::sequence corresponding to the current state of the
/// BilinearForm.
long sequence;
@@ -117,9 +89,6 @@ protected:
static_cond = NULL; hybridization = NULL;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::FULL;
batch = 1;
ext = NULL;
}
private:
@@ -149,10 +118,6 @@ public:
/// Get the size of the BilinearForm as a square matrix.
int Size() const { return height; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
/** Enable the use of static condensation. For details see the description
for class StaticCondensation in fem/staticcond.hpp This method should be
called before assembly. If the number of unknowns after static
@@ -326,12 +291,11 @@ public:
virtual const Operator *GetRestriction() const
{ return fes->GetConformingRestriction(); }
/** @brief Form the linear system A X = B, corresponding to this bilinear
form and the linear form @a b(.). */
/** This method applies any necessary transformations to the linear system
such as: eliminating boundary conditions; applying conforming constraints
for non-conforming AMR; parallel assembly; static condensation;
hybridization.
/// Form a linear system, A X = B.
/** Form the linear system A X = B, corresponding to the current bilinear
form and b(.), by applying any necessary transformations such as:
eliminating boundary conditions; applying conforming constraints for
non-conforming AMR; static condensation; hybridization.
The GridFunction-size vector @a x must contain the essential b.c. The
BilinearForm and the LinearForm-size vector @a b must be assembled.
@@ -352,52 +316,12 @@ public:
NOTE: If there are no transformations, @a X simply reuses the data of
@a x. */
virtual void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
Vector &b, OperatorHandle &A, Vector &X,
Vector &B, int copy_interior = 0);
/** @brief Form the linear system A X = B, corresponding to this bilinear
form and the linear form @a b(.). */
/** Version of the method FormLinearSystem() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OpType &A, Vector &X, Vector &B,
int copy_interior = 0)
{
OperatorHandle Ah;
FormLinearSystem(ess_tdof_list, x, b, Ah, X, B, copy_interior);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
/// Form the linear system matrix @a A, see FormLinearSystem() for details.
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A);
SparseMatrix &A, Vector &X, Vector &B,
int copy_interior = 0);
/// Form the linear system matrix A, see FormLinearSystem() for details.
/** Version of the method FormSystemMatrix() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormSystemMatrix(const Array<int> &ess_tdof_list, OpType &A)
{
OperatorHandle Ah;
FormSystemMatrix(ess_tdof_list, Ah);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
void FormSystemMatrix(const Array<int> &ess_tdof_list, SparseMatrix &A);
/// Recover the solution of a linear system formed with FormLinearSystem().
/** Call this method after solving a linear system constructed using the
@@ -489,7 +413,6 @@ public:
virtual ~BilinearForm();
};
/**
Class for assembling of bilinear forms `a(u,v)` defined on different
trial and test spaces. The assembled matrix `A` is such that
-236
View File
@@ -1,236 +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.
// Implementations of classes FABilinearFormExtension, EABilinearFormExtension,
// PABilinearFormExtension and MFBilinearFormExtension.
#include "../general/forall.hpp"
#include "bilinearform.hpp"
namespace mfem
{
BilinearFormExtension::BilinearFormExtension(BilinearForm *form)
: Operator(form->Size()), a(form)
{
// empty
}
const Operator *BilinearFormExtension::GetProlongation() const
{
return a->GetProlongation();
}
const Operator *BilinearFormExtension::GetRestriction() const
{
return a->GetRestriction();
}
// Data and methods for partially-assembled bilinear forms
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()
{
delete elem_restrict;
}
void PABilinearFormExtension::Assemble()
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->Assemble(*a->FESpace());
}
}
void PABilinearFormExtension::Update()
{
FiniteElementSpace *fes = a->FESpace();
height = width = fes->GetVSize();
trialFes = fes;
testFes = fes;
localX.SetSize(trialFes->GetNE() * trialFes->GetFE(0)->GetDof() *
trialFes->GetVDim());
localY.SetSize(testFes->GetNE() * testFes->GetFE(0)->GetDof() *
testFes->GetVDim());
delete elem_restrict;
elem_restrict = new ElemRestriction(*fes);
}
void PABilinearFormExtension::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
const Operator* trialP = trialFes->GetProlongationMatrix();
const Operator* testP = testFes->GetProlongationMatrix();
Operator *rap = this;
if (trialP) { rap = new RAPOperator(*testP, *this, *trialP); }
const bool own_A = (rap!=this);
A.Reset(new ConstrainedOperator(rap, ess_tdof_list, own_A));
}
void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B,
int copy_interior)
{
Operator *oper;
Operator::FormLinearSystem(ess_tdof_list, x, b, oper, X, B, copy_interior);
A.Reset(oper); // A will own oper
}
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();
for (int i = 0; i < iSz; ++i)
{
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();
for (int i = 0; i < iSz; ++i)
{
integrators[i]->MultAssembledTranspose(localX, localY);
}
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)
{
const FiniteElement *fe = fes.GetFE(e);
const TensorBasisElement* el =
dynamic_cast<const TensorBasisElement*>(fe);
if (el) { continue; }
mfem_error("Finite element not supported with partial assembly");
}
const FiniteElement *fe = fes.GetFE(0);
const TensorBasisElement* el = dynamic_cast<const TensorBasisElement*>(fe);
const Array<int> &dof_map = el->GetDofMap();
const bool dof_map_is_identity = (dof_map.Size()==0);
const Table& e2dTable = fes.GetElementToDofTable();
const int* elementMap = e2dTable.GetJ();
// We'll be keeping a count of how many local nodes point to its global dof
for (int i = 0; i <= ndofs; ++i)
{
offsets[i] = 0;
}
for (int e = 0; e < ne; ++e)
{
for (int d = 0; d < dof; ++d)
{
const int gid = elementMap[dof*e + d];
++offsets[gid + 1];
}
}
// Aggregate to find offsets for each global dof
for (int i = 1; i <= ndofs; ++i)
{
offsets[i] += offsets[i - 1];
}
// For each global dof, fill in all local nodes that point to it
for (int e = 0; e < ne; ++e)
{
for (int d = 0; d < dof; ++d)
{
const int did = dof_map_is_identity?d:dof_map[d];
const int gid = elementMap[dof*e + did];
const int lid = dof*e + d;
indices[offsets[gid]++] = lid;
}
}
// We shifted the offsets vector by 1 by using it as a counter
// Now we shift it back.
for (int i = ndofs; i > 0; --i)
{
offsets[i] = offsets[i - 1];
}
offsets[0] = 0;
}
void 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
-151
View File
@@ -1,151 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_BILINEARFORM_EXT
#define MFEM_BILINEARFORM_EXT
#include "../config/config.hpp"
#include "fespace.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;
};
class BilinearFormExtension : public Operator
{
protected:
BilinearForm *a; ///< Not owned
public:
BilinearFormExtension(BilinearForm *form);
/// Get the finite element space prolongation matrix
virtual const Operator *GetProlongation() const;
/// Get the finite element space restriction matrix
virtual const Operator *GetRestriction() const;
virtual void Assemble() = 0;
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A) = 0;
virtual void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) = 0;
virtual void Update() = 0;
};
/// Data and methods for fully-assembled bilinear forms
class FABilinearFormExtension : public BilinearFormExtension
{
public:
FABilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form) { }
/// TODO
void Assemble() {}
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
~FABilinearFormExtension() {}
};
/// Data and methods for element-assembled bilinear forms
class EABilinearFormExtension : public BilinearFormExtension
{
public:
EABilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form) { }
/// TODO
void Assemble() {}
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
~EABilinearFormExtension() {}
};
/// Data and methods for partially-assembled bilinear forms
class PABilinearFormExtension : public BilinearFormExtension
{
protected:
const FiniteElementSpace *trialFes, *testFes;
mutable Vector localX, localY;
ElemRestriction *elem_restrict;
public:
PABilinearFormExtension(BilinearForm*);
void Assemble();
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
~PABilinearFormExtension();
};
/// Data and methods for matrix-free bilinear forms
class MFBilinearFormExtension : public BilinearFormExtension
{
public:
MFBilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form) { }
/// TODO
void Assemble() {}
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
~MFBilinearFormExtension() {}
};
}
#endif
-17
View File
@@ -19,23 +19,6 @@ using namespace std;
namespace mfem
{
void BilinearFormIntegrator::Assemble(const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::Assemble (...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::MultAssembled(Vector&, Vector&)
{
mfem_error ("BilinearFormIntegrator::MultAssembled (...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::MultAssembledTranspose(Vector&, Vector&)
{
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose (...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleElementMatrix (
const FiniteElement &el, ElementTransformation &Trans,
+8 -37
View File
@@ -14,8 +14,6 @@
#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "fespace.hpp"
#include "bilininteg_ext.hpp"
namespace mfem
{
@@ -23,20 +21,11 @@ namespace mfem
/// Abstract base class BilinearFormIntegrator
class BilinearFormIntegrator : public NonlinearFormIntegrator
{
public:
protected:
BilinearFormIntegrator(const IntegrationRule *ir = NULL) :
NonlinearFormIntegrator(ir) { }
public:
/// Method defining partial assembly.
virtual void Assemble(const FiniteElementSpace&);
/// Method for partially assembled action.
virtual void MultAssembled(Vector&, Vector&);
/// Method for partially assembled transposed action.
virtual void MultAssembledTranspose(Vector&, Vector&);
/// Given a particular Finite Element computes the element matrix elmat.
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -1645,19 +1634,16 @@ private:
#endif
Coefficient *Q;
MatrixCoefficient *MQ;
// PA extension
DofToQuad *maps;
GeometryExtension *geom;
int dim, ne, dofs1D, quad1D;
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator() { Q = NULL; MQ = NULL; maps = NULL; geom = NULL; }
DiffusionIntegrator() { Q = NULL; MQ = 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; }
/// 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; }
/** Given a particular Finite Element
computes the element stiffness matrix elmat. */
@@ -1684,12 +1670,6 @@ public:
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
/// PA extension
virtual void Assemble(const FiniteElementSpace&);
virtual void MultAssembled(Vector&, Vector&);
virtual ~DiffusionIntegrator();
};
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
@@ -1700,17 +1680,13 @@ protected:
Vector shape, te_shape;
#endif
Coefficient *Q;
// PA extension
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; }
: BilinearFormIntegrator(ir) { Q = NULL; }
/// Construct a mass integrator with coefficient q
MassIntegrator(Coefficient &q, const IntegrationRule *ir = NULL)
: BilinearFormIntegrator(ir), Q(&q) { maps = NULL; geom = NULL; }
: BilinearFormIntegrator(ir), Q(&q) { }
/** Given a particular Finite Element
computes the element mass matrix elmat. */
@@ -1721,11 +1697,6 @@ public:
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
/// PA extension
virtual void Assemble(const FiniteElementSpace&);
virtual void MultAssembled(Vector&, Vector&);
virtual ~MassIntegrator();
};
class BoundaryMassIntegrator : public MassIntegrator
File diff suppressed because it is too large Load Diff
-80
View File
@@ -1,80 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_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
-9
View File
@@ -28,11 +28,6 @@ double PWConstCoefficient::Eval(ElementTransformation & T,
return (constants(att-1));
}
DeviceFunctionCoefficientPtr FunctionCoefficient::GetDeviceFunction()
{
return DeviceFunction;
}
double FunctionCoefficient::Eval(ElementTransformation & T,
const IntegrationPoint & ip)
{
@@ -45,10 +40,6 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
{
return ((*Function)(transip));
}
else if (DeviceFunction)
{
return ((*DeviceFunction)(Vector3(x)));
}
else
{
return (*TDFunction)(transip, GetTime());
+1 -27
View File
@@ -96,7 +96,7 @@ public:
{ constants.SetSize(c.Size()); constants=c; }
/// Update constants
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
void UpdateConstants(Vector &c) {constants.SetSize(c.Size()); constants=c;}
/// Member function to access or modify the value of the i-th constant
double &operator()(int i) { return constants(i-1); }
@@ -112,15 +112,12 @@ public:
const IntegrationPoint &ip);
};
typedef double (*DeviceFunctionCoefficientPtr)(const Vector3&);
/// class for C-function coefficient
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
@@ -128,7 +125,6 @@ public:
{
Function = f;
TDFunction = NULL;
DeviceFunction = NULL;
}
/// Define a time-dependent coefficient from a C-function
@@ -136,16 +132,6 @@ 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
@@ -155,7 +141,6 @@ public:
{
Function = reinterpret_cast<double(*)(const Vector&)>(f);
TDFunction = NULL;
DeviceFunction = NULL;
}
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
@@ -165,17 +150,11 @@ 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;
@@ -469,7 +448,6 @@ public:
void SetGridFunction(GridFunction *gf);
GridFunction * GetGridFunction() const { return GridFunc; }
using VectorCoefficient::Eval;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -812,7 +790,6 @@ public:
/// Evaluate the coefficient
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
/// Vector coefficient defined as a product of a scalar and a vector
@@ -827,7 +804,6 @@ public:
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
/// Vector coefficient defined as a cross product of two vectors
@@ -845,7 +821,6 @@ public:
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
/// Vector coefficient defined as a matrix vector product
@@ -863,7 +838,6 @@ public:
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
/// Matrix coefficient defined as the identity of dimension d
-784
View File
@@ -1,784 +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 "complex_fem.hpp"
using namespace std;
namespace mfem
{
ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *fes)
: Vector(2*(fes->GetVSize()))
{
gfr_ = new GridFunction(fes, &data[0]);
gfi_ = new GridFunction(fes, &data[fes->GetVSize()]);
}
void
ComplexGridFunction::Update()
{
FiniteElementSpace * fes = gfr_->FESpace();
int vsize = fes->GetVSize();
const Operator *T = fes->GetUpdateOperator();
if (T)
{
// Update the individual GridFunction objects. This will allocate
// new data arrays for each GridFunction.
gfr_->Update();
gfi_->Update();
// Our data array now contains old data as well as being the wrong size
// so reallocate it.
this->SetSize(2 * vsize);
// Create temporary vectors which point to the new data array
Vector gf_r(&data[0], vsize);
Vector gf_i(&data[vsize], vsize);
// Copy the updated GridFunctions into the new data array
gf_r = *gfr_;
gf_i = *gfi_;
// Replace the individual data arrays with pointers into the new data array
gfr_->NewDataAndSize(&data[0], vsize);
gfi_->NewDataAndSize(&data[vsize], vsize);
}
else
{
// The existing data will not be transferred to the new GridFunctions
// so delete it a allocate a new array
this->SetSize(2 * vsize);
// Point the individual GridFunctions to the new data array
gfr_->NewDataAndSize(&data[0], vsize);
gfi_->NewDataAndSize(&data[vsize], vsize);
// These updates will only set the proper 'sequence' value within
// the individual GridFunction objects because their sizes are
// already correct
gfr_->Update();
gfi_->Update();
}
}
void
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff)
{
gfr_->ProjectCoefficient(real_coeff);
gfi_->ProjectCoefficient(imag_coeff);
}
void
ComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff)
{
gfr_->ProjectCoefficient(real_vcoeff);
gfi_->ProjectCoefficient(imag_vcoeff);
}
void
ComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
Array<int> &attr)
{
gfr_->ProjectBdrCoefficient(real_coeff, attr);
gfi_->ProjectBdrCoefficient(imag_coeff, attr);
}
void
ComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff,
Array<int> &attr)
{
gfr_->ProjectBdrCoefficientNormal(real_vcoeff, attr);
gfi_->ProjectBdrCoefficientNormal(imag_vcoeff, attr);
}
void
ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&real_vcoeff,
VectorCoefficient
&imag_vcoeff,
Array<int> &attr)
{
gfr_->ProjectBdrCoefficientTangent(real_vcoeff, attr);
gfi_->ProjectBdrCoefficientTangent(imag_vcoeff, attr);
}
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *f,
ComplexOperator::Convention convention)
: Vector(2*(f->GetVSize())),
conv_(convention)
{
lfr_ = new LinearForm(f, &data[0]);
lfi_ = new LinearForm(f, &data[f->GetVSize()]);
}
ComplexLinearForm::~ComplexLinearForm()
{
delete lfr_;
delete lfi_;
}
void
ComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { lfr_->AddDomainIntegrator(lfi_real); }
if ( lfi_imag ) { lfi_->AddDomainIntegrator(lfi_imag); }
}
void
ComplexLinearForm::Update()
{
FiniteElementSpace *fes = lfr_->FESpace();
this->Update(fes);
}
void
ComplexLinearForm::Update(FiniteElementSpace *fes)
{
int vsize = fes->GetVSize();
SetSize(2 * vsize);
Vector lfr(&data[0], vsize);
Vector lfi(&data[vsize], vsize);
lfr_->Update(fes, lfr, 0);
lfi_->Update(fes, lfi, 0);
}
void
ComplexLinearForm::Assemble()
{
lfr_->Assemble();
lfi_->Assemble();
if (conv_ == ComplexOperator::BLOCK_SYMMETRIC)
{
*lfi_ *= -1.0;
}
}
complex<double>
ComplexLinearForm::operator()(const ComplexGridFunction &gf) const
{
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
return complex<double>((*lfr_)(gf.real()) - s * (*lfi_)(gf.imag()),
(*lfr_)(gf.imag()) + s * (*lfi_)(gf.real()));
}
SesquilinearForm::SesquilinearForm(FiniteElementSpace *f,
ComplexOperator::Convention convention)
: conv_(convention),
blfr_(new BilinearForm(f)),
blfi_(new BilinearForm(f))
{}
SesquilinearForm::~SesquilinearForm()
{
delete blfr_;
delete blfi_;
}
void SesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { blfr_->AddDomainIntegrator(bfi_real); }
if (bfi_imag) { blfi_->AddDomainIntegrator(bfi_imag); }
}
void
SesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { blfr_->AddBoundaryIntegrator(bfi_real); }
if (bfi_imag) { blfi_->AddBoundaryIntegrator(bfi_imag); }
}
void
SesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> & bdr_marker)
{
if (bfi_real) { blfr_->AddBoundaryIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { blfi_->AddBoundaryIntegrator(bfi_imag, bdr_marker); }
}
void
SesquilinearForm::Assemble(int skip_zeros)
{
blfr_->Assemble(skip_zeros);
blfi_->Assemble(skip_zeros);
}
void
SesquilinearForm::Finalize(int skip_zeros)
{
blfr_->Finalize(skip_zeros);
blfi_->Finalize(skip_zeros);
}
ComplexSparseMatrix *
SesquilinearForm::AssembleCompSpMat()
{
return new ComplexSparseMatrix(&blfr_->SpMat(),
&blfi_->SpMat(),
false, false, conv_);
}
void
SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B,
int ci)
{
FiniteElementSpace * fes = blfr_->FESpace();
int vsize = fes->GetVSize();
// int tvsize = pfes->GetTrueVSize();
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
// Allocate temporary vectors
Vector b_0(vsize); b_0 = 0.0;
// Vector B_0(tvsize); B_0 = 0.0;
// Extract the real and imaginary parts of the input vectors
MFEM_ASSERT(x.Size() == 2 * vsize, "Input GridFunction of incorrect size!");
Vector x_r(x.GetData(), vsize);
Vector x_i(&(x.GetData())[vsize], vsize);
MFEM_ASSERT(b.Size() == 2 * vsize, "Input LinearForm of incorrect size!");
Vector b_r(b.GetData(), vsize);
Vector b_i(&(b.GetData())[vsize], vsize);
b_i *= s;
/*
X.SetSize(2 * tvsize);
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
B.SetSize(2 * tvsize);
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
*/
SparseMatrix * A_r = new SparseMatrix;
SparseMatrix * A_i = new SparseMatrix;
Vector X_0, B_0;
b_0 = b_r;
blfr_->FormLinearSystem(ess_tdof_list, x_r, b_r, *A_r, X_0, B_0, ci);
int tvsize = B_0.Size();
X.SetSize(2 * tvsize);
B.SetSize(2 * tvsize);
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
X_r = X_0; B_r = B_0;
b_0 = 0.0;
blfi_->FormLinearSystem(ess_tdof_list, x_i, b_0, *A_i, X_0, B_0, false);
B_r -= B_0;
b_0 = b_i;
blfr_->FormLinearSystem(ess_tdof_list, x_i, b_0, *A_r, X_0, B_0, ci);
X_i = X_0; B_i = B_0;
b_0 = 0.0;
blfi_->FormLinearSystem(ess_tdof_list, x_r, b_0, *A_i, X_0, B_0, false);
B_i += B_0;
B_i *= s;
b_i *= s;
// A = A_r + i A_i
A.Clear();
ComplexSparseMatrix * A_sp =
new ComplexSparseMatrix(A_r, A_i, true, true, conv_);
A.Reset<ComplexSparseMatrix>(A_sp, true);
}
void
SesquilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x)
{
FiniteElementSpace * fes = blfr_->FESpace();
const SparseMatrix *P = fes->GetConformingProlongation();
int vsize = fes->GetVSize();
int tvsize = X.Size() / 2;
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
Vector x_r(x.GetData(), vsize);
Vector x_i(&(x.GetData())[vsize], vsize);
if (!P)
{
x = X;
}
else
{
// Apply conforming prolongation
P->Mult(X_r, x_r);
P->Mult(X_i, x_i);
}
}
void
SesquilinearForm::Update(FiniteElementSpace *nfes)
{
if ( blfr_ ) { blfr_->Update(nfes); }
if ( blfi_ ) { blfi_->Update(nfes); }
}
#ifdef MFEM_USE_MPI
ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pfes)
: Vector(2*(pfes->GetVSize()))
{
pgfr_ = new ParGridFunction(pfes, &data[0]);
pgfi_ = new ParGridFunction(pfes, &data[pfes->GetVSize()]);
}
void
ParComplexGridFunction::Update()
{
ParFiniteElementSpace * pfes = pgfr_->ParFESpace();
int vsize = pfes->GetVSize();
const Operator *T = pfes->GetUpdateOperator();
if (T)
{
// Update the individual GridFunction objects. This will allocate
// new data arrays for each GridFunction.
pgfr_->Update();
pgfi_->Update();
// Our data array now contains old data as well as being the wrong size
// so reallocate it.
this->SetSize(2 * vsize);
// Create temporary vectors which point to the new data array
Vector gf_r(&data[0], vsize);
Vector gf_i(&data[vsize], vsize);
// Copy the updated GridFunctions into the new data array
gf_r = *pgfr_;
gf_i = *pgfi_;
// Replace the individual data arrays with pointers into the new data array
pgfr_->NewDataAndSize(&data[0], vsize);
pgfi_->NewDataAndSize(&data[vsize], vsize);
}
else
{
// The existing data will not be transferred to the new GridFunctions
// so delete it a allocate a new array
this->SetSize(2 * vsize);
// Point the individual GridFunctions to the new data array
pgfr_->NewDataAndSize(&data[0], vsize);
pgfi_->NewDataAndSize(&data[vsize], vsize);
// These updates will only set the proper 'sequence' value within
// the individual GridFunction objects because their sizes are
// already correct
pgfr_->Update();
pgfi_->Update();
}
}
void
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff)
{
pgfr_->ProjectCoefficient(real_coeff);
pgfi_->ProjectCoefficient(imag_coeff);
}
void
ParComplexGridFunction::ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff)
{
pgfr_->ProjectCoefficient(real_vcoeff);
pgfi_->ProjectCoefficient(imag_vcoeff);
}
void
ParComplexGridFunction::ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
Array<int> &attr)
{
pgfr_->ProjectBdrCoefficient(real_coeff, attr);
pgfi_->ProjectBdrCoefficient(imag_coeff, attr);
}
void
ParComplexGridFunction::ProjectBdrCoefficientNormal(VectorCoefficient
&real_vcoeff,
VectorCoefficient
&imag_vcoeff,
Array<int> &attr)
{
pgfr_->ProjectBdrCoefficientNormal(real_vcoeff, attr);
pgfi_->ProjectBdrCoefficientNormal(imag_vcoeff, attr);
}
void
ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
&real_vcoeff,
VectorCoefficient
&imag_vcoeff,
Array<int> &attr)
{
pgfr_->ProjectBdrCoefficientTangent(real_vcoeff, attr);
pgfi_->ProjectBdrCoefficientTangent(imag_vcoeff, attr);
}
void
ParComplexGridFunction::Distribute(const Vector *tv)
{
ParFiniteElementSpace * pfes = pgfr_->ParFESpace();
HYPRE_Int size = pfes->GetTrueVSize();
double * tvd = tv->GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
pgfr_->Distribute(tvr);
pgfi_->Distribute(tvi);
}
void
ParComplexGridFunction::ParallelProject(Vector &tv) const
{
ParFiniteElementSpace * pfes = pgfr_->ParFESpace();
HYPRE_Int size = pfes->GetTrueVSize();
double * tvd = tv.GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
pgfr_->ParallelProject(tvr);
pgfi_->ParallelProject(tvi);
}
ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
ComplexOperator::Convention
convention)
: Vector(2*(pfes->GetVSize())),
conv_(convention)
{
plfr_ = new ParLinearForm(pfes, &data[0]);
plfi_ = new ParLinearForm(pfes, &data[pfes->GetVSize()]);
HYPRE_Int * tdof_offsets = pfes->GetTrueDofOffsets();
int n = (HYPRE_AssumedPartitionCheck()) ? 2 : pfes->GetNRanks();
tdof_offsets_ = new HYPRE_Int[n+1];
for (int i=0; i<=n; i++)
{
tdof_offsets_[i] = 2 * tdof_offsets[i];
}
}
ParComplexLinearForm::~ParComplexLinearForm()
{
delete plfr_;
delete plfi_;
delete [] tdof_offsets_;
}
void
ParComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
{
if ( lfi_real ) { plfr_->AddDomainIntegrator(lfi_real); }
if ( lfi_imag ) { plfi_->AddDomainIntegrator(lfi_imag); }
}
void
ParComplexLinearForm::Update(ParFiniteElementSpace *pf)
{
ParFiniteElementSpace *pfes = (pf!=NULL)?pf:plfr_->ParFESpace();
int vsize = pfes->GetVSize();
SetSize(2 * vsize);
Vector plfr(&data[0], vsize);
Vector plfi(&data[vsize], vsize);
plfr_->Update(pfes, plfr, 0);
plfi_->Update(pfes, plfi, 0);
}
void
ParComplexLinearForm::Assemble()
{
plfr_->Assemble();
plfi_->Assemble();
if (conv_ == ComplexOperator::BLOCK_SYMMETRIC)
{
*plfi_ *= -1.0;
}
}
void
ParComplexLinearForm::ParallelAssemble(Vector &tv)
{
HYPRE_Int size = plfr_->ParFESpace()->GetTrueVSize();
double * tvd = tv.GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
plfr_->ParallelAssemble(tvr);
plfi_->ParallelAssemble(tvi);
}
HypreParVector *
ParComplexLinearForm::ParallelAssemble()
{
const ParFiniteElementSpace * pfes = plfr_->ParFESpace();
HypreParVector * tv = new HypreParVector(pfes->GetComm(),
2*(pfes->GlobalTrueVSize()),
tdof_offsets_);
HYPRE_Int size = pfes->GetTrueVSize();
double * tvd = tv->GetData();
Vector tvr(tvd, size);
Vector tvi(&tvd[size], size);
plfr_->ParallelAssemble(tvr);
plfi_->ParallelAssemble(tvi);
return tv;
}
complex<double>
ParComplexLinearForm::operator()(const ParComplexGridFunction &gf) const
{
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
return complex<double>((*plfr_)(gf.real()) - s * (*plfi_)(gf.imag()),
(*plfr_)(gf.imag()) + s * (*plfi_)(gf.real()));
}
ParSesquilinearForm::ParSesquilinearForm(ParFiniteElementSpace *pf,
ComplexOperator::Convention
convention)
: conv_(convention),
pblfr_(new ParBilinearForm(pf)),
pblfi_(new ParBilinearForm(pf))
{}
ParSesquilinearForm::~ParSesquilinearForm()
{
delete pblfr_;
delete pblfi_;
}
void ParSesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { pblfr_->AddDomainIntegrator(bfi_real); }
if (bfi_imag) { pblfi_->AddDomainIntegrator(bfi_imag); }
}
void
ParSesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
{
if (bfi_real) { pblfr_->AddBoundaryIntegrator(bfi_real); }
if (bfi_imag) { pblfi_->AddBoundaryIntegrator(bfi_imag); }
}
void
ParSesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> & bdr_marker)
{
if (bfi_real) { pblfr_->AddBoundaryIntegrator(bfi_real, bdr_marker); }
if (bfi_imag) { pblfi_->AddBoundaryIntegrator(bfi_imag, bdr_marker); }
}
void
ParSesquilinearForm::Assemble(int skip_zeros)
{
pblfr_->Assemble(skip_zeros);
pblfi_->Assemble(skip_zeros);
}
void
ParSesquilinearForm::Finalize(int skip_zeros)
{
pblfr_->Finalize(skip_zeros);
pblfi_->Finalize(skip_zeros);
}
ComplexHypreParMatrix *
ParSesquilinearForm::ParallelAssemble()
{
return new ComplexHypreParMatrix(pblfr_->ParallelAssemble(),
pblfi_->ParallelAssemble(),
true, true, conv_);
}
void
ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B,
int ci)
{
ParFiniteElementSpace * pfes = pblfr_->ParFESpace();
int tvs = pfes->TrueVSize();
cout << "TrueVSize returns " << tvs << endl;
cout << "GetVSize returns " << pfes->GetVSize() << endl;
int vsize = x.Size() / 2;
// int vsize = pfes->GetVSize();
// int tvsize = pfes->GetTrueVSize();
cout << "x.Size/2 returns " << vsize << endl;
double s = (conv_ == ComplexOperator::HERMITIAN)?1.0:-1.0;
// Allocate temporary vectors
Vector b_0(vsize); b_0 = 0.0;
// Vector B_0(tvsize); B_0 = 0.0;
// Extract the real and imaginary parts of the input vectors
// MFEM_ASSERT(x.Size() == 2 * vsize, "Input GridFunction of incorrect size!");
Vector x_r(x.GetData(), vsize);
Vector x_i(&(x.GetData())[vsize], vsize);
MFEM_ASSERT(b.Size() == 2 * vsize, "Input LinearForm of incorrect size!");
Vector b_r(b.GetData(), vsize);
Vector b_i(&(b.GetData())[vsize], vsize);
b_i *= s;
/*
X.SetSize(2 * tvsize);
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
B.SetSize(2 * tvsize);
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
*/
OperatorHandle A_r, A_i;
Vector X_0, B_0;
cout << "pblfr fls 1" << endl << flush;
b_0 = b_r;
pblfr_->FormLinearSystem(ess_tdof_list, x_r, b_0, A_r, X_0, B_0, ci);
int tvsize = B_0.Size();
X.SetSize(2 * tvsize);
B.SetSize(2 * tvsize);
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
Vector B_r(B.GetData(), tvsize);
Vector B_i(&(B.GetData())[tvsize], tvsize);
X_r = X_0; B_r = B_0;
cout << "pblfi fls 1" << endl << flush;
b_0 = 0.0;
pblfi_->FormLinearSystem(ess_tdof_list, x_i, b_0, A_i, X_0, B_0, false);
B_r -= B_0;
cout << "pblfr fls 2" << endl << flush;
b_0 = b_i;
pblfr_->FormLinearSystem(ess_tdof_list, x_i, b_0, A_r, X_0, B_0, ci);
X_i = X_0; B_i = B_0;
cout << "pblfi fls 2" << endl << flush;
b_0 = 0.0;
pblfi_->FormLinearSystem(ess_tdof_list, x_r, b_0, A_i, X_0, B_0, false);
B_i += B_0;
B_i *= s;
b_i *= s;
// A = A_r + i A_i
A.Clear();
if ( A_r.Type() == Operator::Hypre_ParCSR &&
A_i.Type() == Operator::Hypre_ParCSR )
{
ComplexHypreParMatrix * A_hyp =
new ComplexHypreParMatrix(A_r.As<HypreParMatrix>(),
A_i.As<HypreParMatrix>(),
A_r.OwnsOperator(),
A_i.OwnsOperator(),
conv_);
A.Reset<ComplexHypreParMatrix>(A_hyp, true);
}
else
{
ComplexOperator * A_op =
new ComplexOperator(A_r.As<Operator>(),
A_i.As<Operator>(),
A_r.OwnsOperator(),
A_i.OwnsOperator(),
conv_);
A.Reset<ComplexOperator>(A_op, true);
}
}
void
ParSesquilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x)
{
ParFiniteElementSpace * pfes = pblfr_->ParFESpace();
const Operator &P = *pfes->GetProlongationMatrix();
int vsize = pfes->GetVSize();
int tvsize = X.Size() / 2;
Vector X_r(X.GetData(), tvsize);
Vector X_i(&(X.GetData())[tvsize], tvsize);
Vector x_r(x.GetData(), vsize);
Vector x_i(&(x.GetData())[vsize], vsize);
// Apply conforming prolongation
P.Mult(X_r, x_r);
P.Mult(X_i, x_i);
}
void
ParSesquilinearForm::Update(FiniteElementSpace *nfes)
{
if ( pblfr_ ) { pblfr_->Update(nfes); }
if ( pblfi_ ) { pblfi_->Update(nfes); }
}
#endif // MFEM_USE_MPI
}
-356
View File
@@ -1,356 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_COMPLEX_FEM
#define MFEM_COMPLEX_FEM
#include "../linalg/complex_operator.hpp"
#include "gridfunc.hpp"
#include "linearform.hpp"
#include "bilinearform.hpp"
#ifdef MFEM_USE_MPI
#include "pgridfunc.hpp"
#include "plinearform.hpp"
#include "pbilinearform.hpp"
#endif
#include <complex>
namespace mfem
{
/// Class for complex-valued grid function - Vector with associated FE space.
class ComplexGridFunction : public Vector
{
private:
GridFunction * gfr_;
GridFunction * gfi_;
protected:
void Destroy() { delete gfr_; delete gfi_; }
public:
/* @brief Construct a ComplexGridFunction associated with the
FiniteElementSpace @a *f. */
ComplexGridFunction(FiniteElementSpace *f);
void Update();
/// Assign constant values to the ComplexGridFunction data.
ComplexGridFunction &operator=(const std::complex<double> & value)
{ *gfr_ = value.real(); *gfi_ = value.imag(); return *this; }
virtual void ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff);
virtual void ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff);
virtual void ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
Array<int> &attr);
virtual void ProjectBdrCoefficientNormal(VectorCoefficient &real_coeff,
VectorCoefficient &imag_coeff,
Array<int> &attr);
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &real_coeff,
VectorCoefficient &imag_coeff,
Array<int> &attr);
FiniteElementSpace *FESpace() { return gfr_->FESpace(); }
const FiniteElementSpace *FESpace() const { return gfr_->FESpace(); }
GridFunction & real() { return *gfr_; }
GridFunction & imag() { return *gfi_; }
const GridFunction & real() const { return *gfr_; }
const GridFunction & imag() const { return *gfi_; }
/// Destroys grid function.
virtual ~ComplexGridFunction() { Destroy(); }
};
class ComplexLinearForm : public Vector
{
private:
ComplexOperator::Convention conv_;
protected:
LinearForm * lfr_;
LinearForm * lfi_;
// HYPRE_Int * tdof_offsets_;
public:
ComplexLinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
virtual ~ComplexLinearForm();
/// Adds new Domain Integrator.
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
FiniteElementSpace *FESpace() const { return lfr_->FESpace(); }
LinearForm & real() { return *lfr_; }
LinearForm & imag() { return *lfi_; }
const LinearForm & real() const { return *lfr_; }
const LinearForm & imag() const { return *lfi_; }
void Update();
void Update(FiniteElementSpace *f);
/// Assembles the linear form i.e. sums over all domain/bdr integrators.
void Assemble();
std::complex<double> operator()(const ComplexGridFunction &gf) const;
};
// Class for sesquilinear form
class SesquilinearForm
{
private:
ComplexOperator::Convention conv_;
//protected:
BilinearForm *blfr_;
BilinearForm *blfi_;
public:
SesquilinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
ComplexOperator::Convention GetConvention() const { return conv_; }
void SetConvention(const ComplexOperator::Convention &
convention) { conv_ = convention; }
BilinearForm & real() { return *blfr_; }
BilinearForm & imag() { return *blfi_; }
const BilinearForm & real() const { return *blfr_; }
const BilinearForm & imag() const { return *blfi_; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new Boundary Integrator, restricted to specific boundary attributes.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker);
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix has to be deleted by the caller. */
ComplexSparseMatrix *AssembleCompSpMat();
/// Return the parallel FE space associated with the ParBilinearForm.
FiniteElementSpace *FESpace() const { return blfr_->FESpace(); }
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
/** Call this method after solving a linear system constructed using the
FormLinearSystem method to recover the solution as a ParGridFunction-size
vector in x. Use the same arguments as in the FormLinearSystem call. */
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x);
virtual void Update(FiniteElementSpace *nfes = NULL);
virtual ~SesquilinearForm();
};
#ifdef MFEM_USE_MPI
/// Class for complex-valued grid function - Vector with associated FE space.
class ParComplexGridFunction : public Vector
{
private:
ParGridFunction * pgfr_;
ParGridFunction * pgfi_;
protected:
void Destroy() { delete pgfr_; delete pgfi_; }
public:
/* @brief Construct a ParComplexGridFunction associated with the
ParFiniteElementSpace @a *f. */
ParComplexGridFunction(ParFiniteElementSpace *pf);
void Update();
/// Assign constant values to the ParComplexGridFunction data.
ParComplexGridFunction &operator=(const std::complex<double> & value)
{ *pgfr_ = value.real(); *pgfi_ = value.imag(); return *this; }
virtual void ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff);
virtual void ProjectCoefficient(VectorCoefficient &real_vcoeff,
VectorCoefficient &imag_vcoeff);
virtual void ProjectBdrCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff,
Array<int> &attr);
virtual void ProjectBdrCoefficientNormal(VectorCoefficient &real_coeff,
VectorCoefficient &imag_coeff,
Array<int> &attr);
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &real_coeff,
VectorCoefficient &imag_coeff,
Array<int> &attr);
void Distribute(const Vector *tv);
void Distribute(const Vector &tv) { Distribute(&tv); }
/// Returns the vector restricted to the true dofs.
void ParallelProject(Vector &tv) const;
FiniteElementSpace *FESpace() { return pgfr_->FESpace(); }
const FiniteElementSpace *FESpace() const { return pgfr_->FESpace(); }
ParGridFunction & real() { return *pgfr_; }
ParGridFunction & imag() { return *pgfi_; }
const ParGridFunction & real() const { return *pgfr_; }
const ParGridFunction & imag() const { return *pgfi_; }
/// Destroys grid function.
virtual ~ParComplexGridFunction() { Destroy(); }
};
class ParComplexLinearForm : public Vector
{
private:
ComplexOperator::Convention conv_;
protected:
ParLinearForm * plfr_;
ParLinearForm * plfi_;
HYPRE_Int * tdof_offsets_;
public:
ParComplexLinearForm(ParFiniteElementSpace *pf,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
virtual ~ParComplexLinearForm();
/// Adds new Domain Integrator.
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
ParFiniteElementSpace *ParFESpace() const { return plfr_->ParFESpace(); }
ParLinearForm & real() { return *plfr_; }
ParLinearForm & imag() { return *plfi_; }
const ParLinearForm & real() const { return *plfr_; }
const ParLinearForm & imag() const { return *plfi_; }
void Update(ParFiniteElementSpace *pf = NULL);
/// Assembles the linear form i.e. sums over all domain/bdr integrators.
void Assemble();
/// Assemble the vector on the true dofs, i.e. P^t v.
void ParallelAssemble(Vector &tv);
/// Returns the vector assembled on the true dofs, i.e. P^t v.
HypreParVector *ParallelAssemble();
std::complex<double> operator()(const ParComplexGridFunction &gf) const;
};
// Class for parallel sesquilinear form
class ParSesquilinearForm
{
private:
ComplexOperator::Convention conv_;
//protected:
ParBilinearForm *pblfr_;
ParBilinearForm *pblfi_;
public:
ParSesquilinearForm(ParFiniteElementSpace *pf,
ComplexOperator::Convention
convention = ComplexOperator::HERMITIAN);
ComplexOperator::Convention GetConvention() const { return conv_; }
void SetConvention(const ComplexOperator::Convention &
convention) { conv_ = convention; }
ParBilinearForm & real() { return *pblfr_; }
ParBilinearForm & imag() { return *pblfi_; }
const ParBilinearForm & real() const { return *pblfr_; }
const ParBilinearForm & imag() const { return *pblfi_; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new Boundary Integrator, restricted to specific boundary attributes.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &bdr_marker);
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix has to be deleted by the caller. */
ComplexHypreParMatrix *ParallelAssemble();
/// Return the parallel FE space associated with the ParBilinearForm.
ParFiniteElementSpace *ParFESpace() const { return pblfr_->ParFESpace(); }
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
/** Call this method after solving a linear system constructed using the
FormLinearSystem method to recover the solution as a ParGridFunction-size
vector in x. Use the same arguments as in the FormLinearSystem call. */
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x);
virtual void Update(FiniteElementSpace *nfes = NULL);
virtual ~ParSesquilinearForm();
};
#endif // MFEM_USE_MPI
}
#endif // MFEM_COMPLEX_FEM
-1
View File
@@ -18,7 +18,6 @@
#include "fe_coll.hpp"
#include "eltrans.hpp"
#include "coefficient.hpp"
#include "complex_fem.hpp"
#include "lininteg.hpp"
#include "nonlininteg.hpp"
#include "bilininteg.hpp"
+3 -533
View File
@@ -652,9 +652,9 @@ void FiniteElementSpace::BuildConformingInterpolation() const
// create the conforming restriction matrix cR
int *cR_J;
{
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);
int *cR_I = new int[n_true_dofs+1];
double *cR_A = new double[n_true_dofs];
cR_J = new int[n_true_dofs];
for (int i = 0; i < n_true_dofs; i++)
{
cR_I[i] = i;
@@ -975,139 +975,6 @@ void FiniteElementSpace::RefinementOperator
}
}
FiniteElementSpace::DerefinementOperator::DerefinementOperator(
const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes,
BilinearFormIntegrator *mass_integ)
: Operator(c_fes->GetVSize(), f_fes->GetVSize()),
fine_fes(f_fes)
{
MFEM_VERIFY(c_fes->GetOrdering() == f_fes->GetOrdering() &&
c_fes->GetVDim() == f_fes->GetVDim(),
"incompatible coarse and fine FE spaces");
IsoparametricTransformation emb_tr;
Mesh *f_mesh = f_fes->GetMesh();
const CoarseFineTransformations &rtrans = f_mesh->GetRefinementTransforms();
Mesh::GeometryList elem_geoms(*f_mesh);
DenseTensor localP[Geometry::NumGeom], localM[Geometry::NumGeom];
for (int gi = 0; gi < elem_geoms.Size(); gi++)
{
const Geometry::Type geom = elem_geoms[gi];
DenseTensor &lP = localP[geom], &lM = localM[geom];
const FiniteElement *fine_fe =
f_fes->fec->FiniteElementForGeometry(geom);
const FiniteElement *coarse_fe =
c_fes->fec->FiniteElementForGeometry(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());
emb_tr.SetIdentityTransformation(geom);
for (int i = 0; i < pmats.SizeK(); i++)
{
emb_tr.GetPointMat() = pmats(i);
emb_tr.FinalizeTransformation();
// Get the local interpolation matrix for this refinement type
fine_fe->GetTransferMatrix(*coarse_fe, emb_tr, lP(i));
// Get the local mass matrix for this refinement type
mass_integ->AssembleElementMatrix(*fine_fe, emb_tr, lM(i));
}
}
Table ref_type_to_matrix;
rtrans.GetCoarseToFineMap(*f_mesh, coarse_to_fine, coarse_to_ref_type,
ref_type_to_matrix, ref_type_to_geom);
MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), "");
const int total_ref_types = ref_type_to_geom.Size();
int num_ref_types[Geometry::NumGeom], num_fine_elems[Geometry::NumGeom];
Array<int> ref_type_to_coarse_elem_offset(total_ref_types);
ref_type_to_fine_elem_offset.SetSize(total_ref_types);
std::fill(num_ref_types, num_ref_types+Geometry::NumGeom, 0);
std::fill(num_fine_elems, num_fine_elems+Geometry::NumGeom, 0);
for (int i = 0; i < total_ref_types; i++)
{
Geometry::Type g = ref_type_to_geom[i];
ref_type_to_coarse_elem_offset[i] = num_ref_types[g];
ref_type_to_fine_elem_offset[i] = num_fine_elems[g];
num_ref_types[g]++;
num_fine_elems[g] += ref_type_to_matrix.RowSize(i);
}
DenseTensor localPtMP[Geometry::NumGeom];
for (int g = 0; g < Geometry::NumGeom; g++)
{
if (num_ref_types[g] == 0) { continue; }
const int fine_dofs = localP[g].SizeI();
const int coarse_dofs = localP[g].SizeJ();
localPtMP[g].SetSize(coarse_dofs, coarse_dofs, num_ref_types[g]);
localR[g].SetSize(coarse_dofs, fine_dofs, num_fine_elems[g]);
}
for (int i = 0; i < total_ref_types; i++)
{
Geometry::Type g = ref_type_to_geom[i];
DenseMatrix &lPtMP = localPtMP[g](ref_type_to_coarse_elem_offset[i]);
int lR_offset = ref_type_to_fine_elem_offset[i]; // offset in localR[g]
const int *mi = ref_type_to_matrix.GetRow(i);
const int nm = ref_type_to_matrix.RowSize(i);
lPtMP = 0.0;
for (int s = 0; s < nm; s++)
{
DenseMatrix &lP = localP[g](mi[s]);
DenseMatrix &lM = localM[g](mi[s]);
DenseMatrix &lR = localR[g](lR_offset+s);
MultAtB(lP, lM, lR); // lR = lP^T lM
AddMult(lR, lP, lPtMP); // lPtMP += lP^T lM lP
}
DenseMatrixInverse lPtMP_inv(lPtMP);
for (int s = 0; s < nm; s++)
{
DenseMatrix &lR = localR[g](lR_offset+s);
lPtMP_inv.Mult(lR); // lR <- (P^T M P)^{-1} P^T M
}
}
// Make a copy of the coarse element-to-dof Table.
coarse_elem_dof = new Table(c_fes->GetElementToDofTable());
}
FiniteElementSpace::DerefinementOperator::~DerefinementOperator()
{
delete coarse_elem_dof;
}
void FiniteElementSpace::DerefinementOperator
::Mult(const Vector &x, Vector &y) const
{
Array<int> c_vdofs, f_vdofs;
Vector loc_x, loc_y;
DenseMatrix loc_x_mat, loc_y_mat;
const int vdim = fine_fes->GetVDim();
const int coarse_ndofs = height/vdim;
for (int coarse_el = 0; coarse_el < coarse_to_fine.Size(); coarse_el++)
{
coarse_elem_dof->GetRow(coarse_el, c_vdofs);
fine_fes->DofsToVDofs(c_vdofs, coarse_ndofs);
loc_y.SetSize(c_vdofs.Size());
loc_y = 0.0;
loc_y_mat.UseExternalData(loc_y.GetData(), c_vdofs.Size()/vdim, vdim);
const int ref_type = coarse_to_ref_type[coarse_el];
const Geometry::Type geom = ref_type_to_geom[ref_type];
const int *fine_elems = coarse_to_fine.GetRow(coarse_el);
const int num_fine_elems = coarse_to_fine.RowSize(coarse_el);
const int lR_offset = ref_type_to_fine_elem_offset[ref_type];
for (int s = 0; s < num_fine_elems; s++)
{
const DenseMatrix &lR = localR[geom](lR_offset+s);
fine_fes->GetElementVDofs(fine_elems[s], f_vdofs);
x.GetSubVector(f_vdofs, loc_x);
loc_x_mat.UseExternalData(loc_x.GetData(), f_vdofs.Size()/vdim, vdim);
AddMult(lR, loc_x_mat, loc_y_mat);
}
y.SetSubVector(c_vdofs, loc_y);
}
}
void FiniteElementSpace::GetLocalDerefinementMatrices(Geometry::Type geom,
DenseTensor &localR) const
{
@@ -2175,401 +2042,4 @@ void QuadratureSpace::Save(std::ostream &out) const
}
GridTransfer::GridTransfer(FiniteElementSpace &dom_fes_,
FiniteElementSpace &ran_fes_)
: dom_fes(dom_fes_), ran_fes(ran_fes_),
oper_type(Operator::ANY_TYPE),
fw_t_oper(), bw_t_oper()
{
#ifdef MFEM_USE_MPI
const bool par_dom = dynamic_cast<ParFiniteElementSpace*>(&dom_fes);
const bool par_ran = dynamic_cast<ParFiniteElementSpace*>(&ran_fes);
MFEM_VERIFY(par_dom == par_ran, "the domain and range FE spaces must both"
" be either serial or parallel");
parallel = par_dom;
#endif
}
const Operator &GridTransfer::MakeTrueOperator(
FiniteElementSpace &fes_in, FiniteElementSpace &fes_out,
const Operator &oper, OperatorHandle &t_oper)
{
if (t_oper.Ptr())
{
return *t_oper.Ptr();
}
if (!Parallel())
{
const SparseMatrix *in_cP = fes_in.GetConformingProlongation();
const SparseMatrix *out_cR = fes_out.GetConformingRestriction();
if (oper_type == Operator::MFEM_SPARSEMAT)
{
const SparseMatrix *mat = dynamic_cast<const SparseMatrix *>(&oper);
MFEM_VERIFY(mat != NULL, "Operator is not a SparseMatrix");
if (!out_cR)
{
t_oper.Reset(const_cast<SparseMatrix*>(mat), false);
}
else
{
t_oper.Reset(mfem::Mult(*out_cR, *mat));
}
if (in_cP)
{
t_oper.Reset(mfem::Mult(*t_oper.As<SparseMatrix>(), *in_cP));
}
}
else if (oper_type == Operator::ANY_TYPE)
{
const int RP_case = bool(out_cR) + 2*bool(in_cP);
switch (RP_case)
{
case 0:
t_oper.Reset(const_cast<Operator*>(&oper), false);
break;
case 1:
t_oper.Reset(
new ProductOperator(out_cR, &oper, false, false));
break;
case 2:
t_oper.Reset(
new ProductOperator(&oper, in_cP, false, false));
break;
case 3:
t_oper.Reset(
new TripleProductOperator(
out_cR, &oper, in_cP, false, false, false));
break;
}
}
else
{
MFEM_ABORT("Operator::Type is not supported: " << oper_type);
}
}
else // Parallel() == true
{
#ifdef MFEM_USE_MPI
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
if (oper_type == Operator::Hypre_ParCSR)
{
const ParFiniteElementSpace *pfes_in =
dynamic_cast<const ParFiniteElementSpace *>(&fes_in);
const ParFiniteElementSpace *pfes_out =
dynamic_cast<const ParFiniteElementSpace *>(&fes_out);
const SparseMatrix *sp_mat = dynamic_cast<const SparseMatrix *>(&oper);
const HypreParMatrix *hy_mat;
if (sp_mat)
{
SparseMatrix *RA = mfem::Mult(*out_R, *sp_mat);
t_oper.Reset(pfes_in->Dof_TrueDof_Matrix()->
LeftDiagMult(*RA, pfes_out->GetTrueDofOffsets()));
delete RA;
}
else if ((hy_mat = dynamic_cast<const HypreParMatrix *>(&oper)))
{
HypreParMatrix *RA =
hy_mat->LeftDiagMult(*out_R, pfes_out->GetTrueDofOffsets());
t_oper.Reset(mfem::ParMult(RA, pfes_in->Dof_TrueDof_Matrix()));
delete RA;
}
else
{
MFEM_ABORT("unknown Operator type");
}
}
else if (oper_type == Operator::ANY_TYPE)
{
t_oper.Reset(new TripleProductOperator(
out_R, &oper, fes_in.GetProlongationMatrix(),
false, false, false));
}
else
{
MFEM_ABORT("Operator::Type is not supported: " << oper_type);
}
#endif
}
return *t_oper.Ptr();
}
InterpolationGridTransfer::~InterpolationGridTransfer()
{
if (own_mass_integ) { delete mass_integ; }
}
void InterpolationGridTransfer::SetMassIntegrator(
BilinearFormIntegrator *mass_integ_, bool own_mass_integ_)
{
if (own_mass_integ) { delete mass_integ; }
mass_integ = mass_integ_;
own_mass_integ = own_mass_integ_;
}
const Operator &InterpolationGridTransfer::ForwardOperator()
{
if (F.Ptr())
{
return *F.Ptr();
}
// Costruct F
if (oper_type == Operator::ANY_TYPE)
{
F.Reset(new FiniteElementSpace::RefinementOperator(&ran_fes, &dom_fes));
}
else if (oper_type == Operator::MFEM_SPARSEMAT)
{
Mesh::GeometryList elem_geoms(*ran_fes.GetMesh());
DenseTensor localP[Geometry::NumGeom];
for (int i = 0; i < elem_geoms.Size(); i++)
{
ran_fes.GetLocalRefinementMatrices(dom_fes, elem_geoms[i],
localP[elem_geoms[i]]);
}
F.Reset(ran_fes.RefinementMatrix_main(
dom_fes.GetNDofs(), dom_fes.GetElementToDofTable(), localP));
}
else
{
MFEM_ABORT("Operator::Type is not supported: " << oper_type);
}
return *F.Ptr();
}
const Operator &InterpolationGridTransfer::BackwardOperator()
{
if (B.Ptr())
{
return *B.Ptr();
}
// 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);
const int map_type = f_fe_0->GetMapType();
if (map_type == FiniteElement::VALUE ||
map_type == FiniteElement::INTEGRAL)
{
mass_integ = new MassIntegrator;
}
else if (map_type == FiniteElement::H_DIV ||
map_type == FiniteElement::H_CURL)
{
mass_integ = new VectorFEMassIntegrator;
}
else
{
MFEM_ABORT("unknown type of FE space");
}
own_mass_integ = true;
}
if (oper_type == Operator::ANY_TYPE)
{
B.Reset(new FiniteElementSpace::DerefinementOperator(
&ran_fes, &dom_fes, mass_integ));
}
else
{
MFEM_ABORT("Operator::Type is not supported: " << oper_type);
}
return *B.Ptr();
}
L2ProjectionGridTransfer::L2Projection::L2Projection(
const FiniteElementSpace &fes_ho_, const FiniteElementSpace &fes_lor_)
: fes_ho(fes_ho_), fes_lor(fes_lor_)
{
Mesh *mesh_ho = fes_ho.GetMesh();
MFEM_VERIFY(mesh_ho->GetNumGeometries(mesh_ho->Dimension()) <= 1,
"mixed meshes are not supported");
// If the local mesh is empty, skip all computations
if (mesh_ho->GetNE() == 0) { return; }
const FiniteElement *fe_lor = fes_lor.GetFE(0);
const FiniteElement *fe_ho = fes_ho.GetFE(0);
ndof_lor = fe_lor->GetDof();
ndof_ho = fe_ho->GetDof();
const int nel_lor = fes_lor.GetNE();
const int nel_ho = fes_ho.GetNE();
nref = nel_lor/nel_ho;
// Construct the mapping from HO to LOR
// ho2lor.GetRow(iho) will give all the LOR elements contained in iho
ho2lor.SetSize(nel_ho, nref);
const CoarseFineTransformations &cf_tr =
fes_lor.GetMesh()->GetRefinementTransforms();
for (int ilor=0; ilor<nel_lor; ++ilor)
{
int iho = cf_tr.embeddings[ilor].parent;
ho2lor.AddConnection(iho, ilor);
}
ho2lor.ShiftUpI();
// R will contain the restriction (L^2 projection operator) defined on
// each coarse HO element (and corresponding patch of LOR elements)
R.SetSize(ndof_lor*nref, ndof_ho, nel_ho);
// P will contain the corresponding prolongation operator
P.SetSize(ndof_ho, ndof_lor*nref, nel_ho);
DenseMatrix Minv_lor(ndof_lor*nref, ndof_lor*nref);
DenseMatrix M_mixed(ndof_lor*nref, ndof_ho);
MassIntegrator mi;
DenseMatrix M_lor_el(ndof_lor, ndof_lor);
DenseMatrixInverse Minv_lor_el(&M_lor_el);
DenseMatrix M_lor(ndof_lor*nref, ndof_lor*nref);
DenseMatrix M_mixed_el(ndof_lor, ndof_ho);
Minv_lor = 0.0;
M_lor = 0.0;
DenseMatrix RtMlor(ndof_ho, ndof_lor*nref);
DenseMatrix RtMlorR(ndof_ho, ndof_ho);
DenseMatrixInverse RtMlorR_inv(&RtMlorR);
IntegrationPointTransformation ip_tr;
IsoparametricTransformation &emb_tr = ip_tr.Transf;
Vector shape_ho(ndof_ho);
Vector shape_lor(ndof_lor);
const Geometry::Type geom = fe_ho->GetGeomType();
const DenseTensor &pmats = cf_tr.GetPointMatrices(geom);
emb_tr.SetIdentityTransformation(geom);
for (int iho=0; iho<nel_ho; ++iho)
{
for (int iref=0; iref<nref; ++iref)
{
// Assemble the low-order refined mass matrix and invert locally
int ilor = ho2lor.GetRow(iho)[iref];
ElementTransformation *el_tr = fes_lor.GetElementTransformation(ilor);
mi.AssembleElementMatrix(*fe_lor, *el_tr, M_lor_el);
M_lor.CopyMN(M_lor_el, iref*ndof_lor, iref*ndof_lor);
Minv_lor_el.Factor();
Minv_lor_el.GetInverseMatrix(M_lor_el);
// Insert into the diagonal of the patch LOR mass matrix
Minv_lor.CopyMN(M_lor_el, iref*ndof_lor, iref*ndof_lor);
// Now assemble the block-row of the mixed mass matrix associated
// with integrating HO functions against LOR functions on the LOR
// sub-element.
// Create the transformation that embeds the fine low-order element
// within the coarse high-order element in reference space
emb_tr.GetPointMat() = pmats(iref);
emb_tr.FinalizeTransformation();
int order = fe_lor->GetOrder() + fe_ho->GetOrder() + el_tr->OrderW();
const IntegrationRule *ir = &IntRules.Get(geom, order);
M_mixed_el = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip_lor = ir->IntPoint(i);
IntegrationPoint ip_ho;
ip_tr.Transform(ip_lor, ip_ho);
fe_lor->CalcShape(ip_lor, shape_lor);
fe_ho->CalcShape(ip_ho, shape_ho);
el_tr->SetIntPoint(&ip_lor);
// For now we use the geometry information from the LOR space
// which means we won't be mass conservative if the mesh is curved
double w = el_tr->Weight()*ip_lor.weight;
shape_lor *= w;
AddMultVWt(shape_lor, shape_ho, M_mixed_el);
}
M_mixed.CopyMN(M_mixed_el, iref*ndof_lor, 0);
}
mfem::Mult(Minv_lor, M_mixed, R(iho));
mfem::MultAtB(R(iho), M_lor, RtMlor);
mfem::Mult(RtMlor, R(iho), RtMlorR);
RtMlorR_inv.Factor();
RtMlorR_inv.Mult(RtMlor, P(iho));
}
}
void L2ProjectionGridTransfer::L2Projection::Mult(
const Vector &x, Vector &y) const
{
int vdim = fes_ho.GetVDim();
Array<int> vdofs;
DenseMatrix xel_mat(ndof_ho, vdim);
DenseMatrix yel_mat(ndof_lor*nref, vdim);
for (int iho=0; iho<fes_ho.GetNE(); ++iho)
{
fes_ho.GetElementVDofs(iho, vdofs);
x.GetSubVector(vdofs, xel_mat.GetData());
mfem::Mult(R(iho), xel_mat, yel_mat);
// Place result correctly into low-order vector
for (int iref=0; iref<nref; ++iref)
{
int ilor = ho2lor.GetRow(iho)[iref];
for (int vd=0; vd<vdim; ++vd)
{
fes_lor.GetElementDofs(ilor, vdofs);
fes_lor.DofsToVDofs(vd, vdofs);
y.SetSubVector(vdofs, &yel_mat(iref*ndof_lor,vd));
}
}
}
}
void L2ProjectionGridTransfer::L2Projection::Prolongate(
const Vector &x, Vector &y) const
{
int vdim = fes_ho.GetVDim();
Array<int> vdofs;
DenseMatrix xel_mat(ndof_lor*nref, vdim);
DenseMatrix yel_mat(ndof_ho, vdim);
for (int iho=0; iho<fes_ho.GetNE(); ++iho)
{
// Extract the LOR DOFs
for (int iref=0; iref<nref; ++iref)
{
int ilor = ho2lor.GetRow(iho)[iref];
for (int vd=0; vd<vdim; ++vd)
{
fes_lor.GetElementDofs(ilor, vdofs);
fes_lor.DofsToVDofs(vd, vdofs);
x.GetSubVector(vdofs, &xel_mat(iref*ndof_lor, vd));
}
}
// Locally prolongate
mfem::Mult(P(iho), xel_mat, yel_mat);
// Place the result in the HO vector
fes_ho.GetElementVDofs(iho, vdofs);
y.SetSubVector(vdofs, yel_mat.GetData());
}
}
const Operator &L2ProjectionGridTransfer::ForwardOperator()
{
if (!F) { F = new L2Projection(dom_fes, ran_fes); }
return *F;
}
const Operator &L2ProjectionGridTransfer::BackwardOperator()
{
if (!B)
{
if (!F) { F = new L2Projection(dom_fes, ran_fes); }
B = new L2Prolongation(*F);
}
return *B;
}
} // namespace mfem
-233
View File
@@ -59,17 +59,12 @@ Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
}
// Forward declarations
class NURBSExtension;
class BilinearFormIntegrator;
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
mesh, mainly managing the set of degrees of freedom. */
class FiniteElementSpace
{
friend class InterpolationGridTransfer;
protected:
/// The mesh that FE space lives on (not owned).
Mesh *mesh;
@@ -155,25 +150,6 @@ protected:
virtual ~RefinementOperator();
};
// Derefinement operator, used by the friend class InterpolationGridTransfer.
class DerefinementOperator : public Operator
{
const FiniteElementSpace *fine_fes; // Not owned.
DenseTensor localR[Geometry::NumGeom];
Table *coarse_elem_dof; // Owned.
Table coarse_to_fine;
Array<int> coarse_to_ref_type;
Array<Geometry::Type> ref_type_to_geom;
Array<int> ref_type_to_fine_elem_offset;
public:
DerefinementOperator(const FiniteElementSpace *f_fes,
const FiniteElementSpace *c_fes,
BilinearFormIntegrator *mass_integ);
virtual void Mult(const Vector &x, Vector &y) const;
virtual ~DerefinementOperator();
};
// This method makes the same assumptions as the method:
// void GetLocalRefinementMatrices(
// const FiniteElementSpace &coarse_fes, Geometry::Type geom,
@@ -597,215 +573,6 @@ public:
void Save(std::ostream &out) const;
};
/** @brief Base class for transfer algorithms that construct transfer Operator%s
between two finite element (FE) spaces. */
/** Generally, the two FE spaces (domain and range) can be defined on different
meshes. */
class GridTransfer
{
protected:
FiniteElementSpace &dom_fes; ///< Domain FE space
FiniteElementSpace &ran_fes; ///< Range FE space
/** @brief Desired Operator::Type for the construction of all operators
defined by the underlying transfer algorithm. It can be ignored by
derived classes. */
Operator::Type oper_type;
OperatorHandle fw_t_oper; ///< Forward true-dof operator
OperatorHandle bw_t_oper; ///< Backward true-dof operator
#ifdef MFEM_USE_MPI
bool parallel;
#endif
bool Parallel() const
{
#ifndef MFEM_USE_MPI
return false;
#else
return parallel;
#endif
}
const Operator &MakeTrueOperator(FiniteElementSpace &fes_in,
FiniteElementSpace &fes_out,
const Operator &oper,
OperatorHandle &t_oper);
public:
/** Construct a transfer algorithm between the domain, @a dom_fes_, and
range, @a ran_fes_, FE spaces. */
GridTransfer(FiniteElementSpace &dom_fes_, FiniteElementSpace &ran_fes_);
/// Virtual destructor
virtual ~GridTransfer() { }
/** @brief Set the desired Operator::Type for the construction of all
operators defined by the underlying transfer algorithm. */
/** The default value is Operator::ANY_TYPE which typically corresponds to
a matrix-free operator representation. Note that derived classes are not
required to support this setting and can ignore it. */
void SetOperatorType(Operator::Type type) { oper_type = type; }
/** @brief Return an Operator that transfers GridFunction%s from the domain
FE space to GridFunction%s in the range FE space. */
virtual const Operator &ForwardOperator() = 0;
/** @brief Return an Operator that transfers GridFunction%s from the range
FE space back to GridFunction%s in the domain FE space. */
virtual const Operator &BackwardOperator() = 0;
/** @brief Return an Operator that transfers true-dof Vector%s from the
domain FE space to true-dof Vector%s in the range FE space. */
/** This method is implemented in the base class, based on ForwardOperator(),
however, derived classes can overload the construction, if necessary. */
virtual const Operator &TrueForwardOperator()
{
return MakeTrueOperator(dom_fes, ran_fes, ForwardOperator(), fw_t_oper);
}
/** @brief Return an Operator that transfers true-dof Vector%s from the
range FE space back to true-dof Vector%s in the domain FE space. */
/** This method is implemented in the base class, based on
BackwardOperator(), however, derived classes can overload the
construction, if necessary. */
virtual const Operator &TrueBackwardOperator()
{
return MakeTrueOperator(ran_fes, dom_fes, BackwardOperator(), bw_t_oper);
}
};
/** @brief Transfer data between a coarse mesh and an embedded refined mesh
using interpolation. */
/** The forward, coarse-to-fine, transfer uses nodal interpolation. The
backward, fine-to-coarse, transfer is defined locally (on a coarse element)
as B = (F^t M_f F)^{-1} F^t M_f, where F is the forward transfer matrix, and
M_f is a mass matrix on the union of all fine elements comprising the coarse
element. Note that the backward transfer operator, B, is a left inverse of
the forward transfer operator, F, i.e. B F = I. Both F and B are defined in
reference space and do not depend on the actual physical shape of the mesh
elements.
It is assumed that both the coarse and the fine FiniteElementSpace%s use
compatible types of elements, e.g. finite elements with the same map-type
(VALUE, INTEGRAL, H_DIV, H_CURL - see class FiniteElement). Generally, the
FE spaces can have different orders, however, in order for the backward
operator to be well-defined, the (local) number of the fine dofs should not
be smaller than the number of coarse dofs. */
class InterpolationGridTransfer : public GridTransfer
{
protected:
BilinearFormIntegrator *mass_integ; ///< Ownership depends on #own_mass_integ
bool own_mass_integ; ///< Ownership flag for #mass_integ
OperatorHandle F; ///< Forward, coarse-to-fine, operator
OperatorHandle B; ///< Backward, fine-to-coarse, operator
public:
InterpolationGridTransfer(FiniteElementSpace &coarse_fes,
FiniteElementSpace &fine_fes)
: GridTransfer(coarse_fes, fine_fes),
mass_integ(NULL), own_mass_integ(false)
{ }
virtual ~InterpolationGridTransfer();
/** @brief Assign a mass integrator to be used in the construction of the
backward, fine-to-coarse, transfer operator. */
void SetMassIntegrator(BilinearFormIntegrator *mass_integ_,
bool own_mass_integ_ = true);
virtual const Operator &ForwardOperator();
virtual const Operator &BackwardOperator();
};
/** @brief Transfer data between a coarse mesh and an embedded refined mesh
using L2 projection. */
/** The forward, coarse-to-fine, transfer uses L2 projection. The backward,
fine-to-coarse, transfer is defined locally (on a coarse element) as
B = (F^t M_f F)^{-1} F^t M_f, where F is the forward transfer matrix, and
M_f is the mass matrix on the union of all fine elements comprising the
coarse element. Note that the backward transfer operator, B, is a left
inverse of the forward transfer operator, F, i.e. B F = I. Both F and B are
defined in physical space and, generally, vary between different mesh
elements.
This class currently only fully supports L2 finite element spaces and fine
meshes that are a uniform refinement of the coarse mesh. Generally, the
coarse and fine FE spaces can have different orders, however, in order for
the backward operator to be well-defined, the number of the fine dofs (in a
coarse element) should not be smaller than the number of coarse dofs.
If used on H1 finite element spaces, the transfer will be performed locally,
and the value of shared (interface) degrees of freedom will be determined by
the value of the last transfer to be performed (according to the element
numbering in the finite element space). As a consequence, the mass
conservation properties for this operator from the L2 case do not carry over
to H1 spaces. */
class L2ProjectionGridTransfer : public GridTransfer
{
protected:
/** Class representing projection operator between a high-order L2 finite
element space on a coarse mesh, and a low-order L2 finite element space
on a refined mesh (LOR). We assume that the low-order space, fes_lor,
lives on a mesh obtained by refining the mesh of the high-order space,
fes_ho. */
class L2Projection : public Operator
{
const FiniteElementSpace &fes_ho;
const FiniteElementSpace &fes_lor;
int ndof_lor, ndof_ho, nref;
Table ho2lor;
DenseTensor R, P;
public:
L2Projection(const FiniteElementSpace &fes_ho_,
const FiniteElementSpace &fes_lor_);
/// Perform the L2 projection onto the LOR space
virtual void Mult(const Vector &x, Vector &y) const;
/// Perform the mass conservative left-inverse prolongation operation.
/// This functionality is also provided as an Operator by L2Prolongation.
void Prolongate(const Vector &x, Vector &y) const;
virtual ~L2Projection() { }
};
/** Mass-conservative prolongation operator going in the opposite direction
as L2Projection. This operator is a left inverse to the L2Projection. */
class L2Prolongation : public Operator
{
const L2Projection &l2proj;
public:
L2Prolongation(const L2Projection &l2proj_) : l2proj(l2proj_) { }
void Mult(const Vector &x, Vector &y) const
{
l2proj.Prolongate(x, y);
}
virtual ~L2Prolongation() { }
};
L2Projection *F; ///< Forward, coarse-to-fine, operator
L2Prolongation *B; ///< Backward, fine-to-coarse, operator
public:
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes,
FiniteElementSpace &fine_fes)
: GridTransfer(coarse_fes, fine_fes),
F(NULL), B(NULL)
{ }
virtual const Operator &ForwardOperator();
virtual const Operator &BackwardOperator();
};
}
#endif
+1 -3
View File
@@ -104,9 +104,7 @@ public:
GridFunction &operator=(const GridFunction &rhs)
{ return operator=((const Vector &)rhs); }
/// Make the GridFunction the owner of #fec and #fes.
/** If the new FiniteElementCollection, @a _fec, is NULL, ownership of #fec
and #fes is taken away. */
/// Make the GridFunction the owner of 'fec' and 'fes'
void MakeOwner(FiniteElementCollection *_fec) { fec = _fec; }
FiniteElementCollection *OwnFEC() { return fec; }
+7 -7
View File
@@ -886,7 +886,7 @@ const IntegrationRule &IntegrationRules::Get(int GeomType, int Order)
if (!HaveIntRule(*ir_array, Order))
{
#ifdef MFEM_USE_LEGACY_OPENMP
#ifdef MFEM_USE_OPENMP
#pragma omp critical
#endif
{
@@ -1595,20 +1595,20 @@ IntegrationRule *IntegrationRules::TetrahedronIntegrationRule(int Order)
// Integration rules for reference prism
IntegrationRule *IntegrationRules::PrismIntegrationRule(int Order)
{
const IntegrationRule & irt = Get(Geometry::TRIANGLE, Order);
const IntegrationRule & irs = Get(Geometry::SEGMENT, Order);
int nt = irt.GetNPoints();
int ns = irs.GetNPoints();
IntegrationRule * irt = GenerateIntegrationRule(Geometry::TRIANGLE, Order);
IntegrationRule * irs = GenerateIntegrationRule(Geometry::SEGMENT, Order);
int nt = irt->GetNPoints();
int ns = irs->GetNPoints();
AllocIntRule(PrismIntRules, Order);
PrismIntRules[Order] = new IntegrationRule(nt * ns);
for (int ks=0; ks<ns; ks++)
{
const IntegrationPoint & ips = irs.IntPoint(ks);
const IntegrationPoint & ips = irs->IntPoint(ks);
for (int kt=0; kt<nt; kt++)
{
int kp = ks * nt + kt;
const IntegrationPoint & ipt = irt.IntPoint(kt);
const IntegrationPoint & ipt = irt->IntPoint(kt);
IntegrationPoint & ipp = PrismIntRules[Order]->IntPoint(kp);
ipp.x = ipt.x;
ipp.y = ipt.y;
-3
View File
@@ -325,9 +325,6 @@ private:
return Order | 1; // valid for all quad_type's
}
/// The following methods allocate new IntegrationRule objects without
/// checking if they already exist. To avoid memory leaks use
/// IntegrationRules::Get(int GeomType, int Order) instead.
IntegrationRule *GenerateIntegrationRule(int GeomType, int Order);
IntegrationRule *PointIntegrationRule(int Order);
IntegrationRule *SegmentIntegrationRule(int Order);
-9
View File
@@ -81,15 +81,6 @@ public:
Update(FiniteElementSpace *, Vector &, int). */
LinearForm() { fes = NULL; extern_lfs = 0; }
/// Construct a LinearForm using previously allocated array @a data.
/** The LinearForm does not assume ownership of @a data which is assumed to
be of size at least `f->GetVSize()`. Similar to the Vector constructor
for externally allocated array, the pointer @a data can be NULL. The data
array can be replaced later using the method SetData().
*/
LinearForm(FiniteElementSpace *f, double *data) : Vector(data, f->GetVSize())
{ fes = f; }
/// 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
have the same size.
-999
View File
@@ -1,999 +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.
#if Q1D < D1D
# define M1D D1D
#else
# define M1D Q1D
#endif
#define M2D (M1D*M1D)
#define DQ1D (D1D*Q1D)
#define Q2D (Q1D*Q1D)
#define Q3D (Q1D*Q1D*Q1D)
#define M2_ELEMENT_BATCH 32
#define QUAD_2D_ID(X, Y) (X + ((Y) * Q1D))
#define QUAD_3D_ID(X, Y, Z) (X + ((Y) * Q1D) + ((Z) * Q2D))
typedef double* DofToQuad_t @dim(Q1D, D1D);
typedef double* QuadToDof_t @dim(D1D, Q1D);
typedef double* DLocal2D_t @dim(D1D, D1D, NE);
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(2, 2, Q2D, NE);
typedef double* Jacobian3D_t @dim(3, 3, Q3D, 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,
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(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)
}
}
}
@kernel void DiffusionSetup3D(const int NE,
@restrict const double *W,
@restrict const Jacobian3D_t J,
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(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 c_detJ = W[q] * COEFF / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J23 * J31) - (J21 * J33);
const double A13 = (J21 * J32) - (J22 * J31);
const double A21 = (J13 * J32) - (J12 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J12 * J31) - (J11 * J32);
const double A31 = (J12 * J23) - (J13 * J22);
const double A32 = (J13 * J21) - (J11 * J23);
const double A33 = (J11 * J22) - (J12 * J21);
// adj(J)^Tadj(J)
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)
}
}
}
@kernel void DiffusionApply2D_CPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const DofToQuad_t G,
@restrict const QuadToDof_t Bt,
@restrict const QuadToDof_t Gt,
@restrict const SymmOperator2D_t op,
@restrict const DLocal2D_t X,
@restrict DLocal2D_t Y) {
// Iterate over elements
for (int e = 0; e < NE; ++e; @outer) {
for (int dummy = 0; dummy < 1; ++dummy; @inner) {
double grad[Q1D][Q1D][2];
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
grad[qy][qx][0] = 0;
grad[qy][qx][1] = 0;
}
}
for (int dy = 0; dy < D1D; ++dy) {
double gradX[Q1D][2];
for (int qx = 0; qx < Q1D; ++qx) {
gradX[qx][0] = 0;
gradX[qx][1] = 0;
}
for (int dx = 0; dx < D1D; ++dx) {
const double s = X(dx, dy, e);
for (int qx = 0; qx < Q1D; ++qx) {
gradX[qx][0] += s * B(qx, dx);
gradX[qx][1] += s * G(qx, dx);
}
}
for (int qy = 0; qy < Q1D; ++qy) {
const double wy = B(qy, dy);
const double wDy = G(qy, dy);
for (int qx = 0; qx < Q1D; ++qx) {
grad[qy][qx][0] += gradX[qx][1] * wy;
grad[qy][qx][1] += gradX[qx][0] * wDy;
}
}
}
// Calculate Dxy, xDy in plane
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
const int q = QUAD_2D_ID(qx, qy);
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];
grad[qy][qx][0] = (O11 * gradX) + (O12 * gradY);
grad[qy][qx][1] = (O12 * gradX) + (O22 * gradY);
}
}
for (int qy = 0; qy < Q1D; ++qy) {
double gradX[D1D][2];
for (int dx = 0; dx < D1D; ++dx) {
gradX[dx][0] = 0;
gradX[dx][1] = 0;
}
for (int qx = 0; qx < Q1D; ++qx) {
const double gX = grad[qy][qx][0];
const double gY = grad[qy][qx][1];
for (int dx = 0; dx < D1D; ++dx) {
const double wx = Bt(dx, qx);
const double wDx = Gt(dx, qx);
gradX[dx][0] += gX * wDx;
gradX[dx][1] += gY * wx;
}
}
for (int dy = 0; dy < D1D; ++dy) {
const double wy = Bt(dy, qy);
const double wDy = Gt(dy, qy);
for (int dx = 0; dx < D1D; ++dx) {
Y(dx, dy, e) += ((gradX[dx][0] * wy) +
(gradX[dx][1] * wDy));
}
}
}
}
}
}
@kernel void DiffusionApply2D_GPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const DofToQuad_t G,
@restrict const QuadToDof_t Bt,
@restrict const QuadToDof_t Gt,
@restrict const SymmOperator2D_t op,
@restrict const DLocal2D_t X,
@restrict DLocal2D_t Y) {
// Iterate over elements
for (int eOff = 0; eOff < NE; eOff += M2_ELEMENT_BATCH; @outer) {
// Store dof <--> quad mappings
@shared double s_B[DQ1D] @dim(Q1D, D1D);
@shared double s_G[DQ1D] @dim(Q1D, D1D);
@shared double s_Bt[DQ1D] @dim(D1D, Q1D);
@shared double s_Gt[DQ1D] @dim(D1D, Q1D);
// Store xy planes in @shared memory
@shared double s_xy[DQ1D] @dim(D1D, Q1D);
@shared double s_xDy[DQ1D] @dim(D1D, Q1D);
@shared double s_grad[2 * Q2D] @dim(2, Q1D, Q1D);
@exclusive double r_x[M1D];
@exclusive double r_y[Q1D];
for (int x = 0; x < M1D; ++x; @inner) {
for (int id = x; id < DQ1D; id += M1D) {
s_B[id] = B[id];
s_G[id] = G[id];
s_Bt[id] = Bt[id];
s_Gt[id] = Gt[id];
}
}
for (int e = eOff; e < (eOff + M2_ELEMENT_BATCH); ++e) {
if (e < NE) {
for (int dx = 0; dx < D1D; ++dx; @inner) {
if (dx < D1D) {
for (int qy = 0; qy < Q1D; ++qy) {
s_xy(dx, qy) = 0;
s_xDy(dx, qy) = 0;
}
for (int dy = 0; dy < D1D; ++dy) {
r_x[dy] = X(dx, dy, e);
}
for (int qy = 0; qy < Q1D; ++qy) {
double xy = 0;
double xDy = 0;
for (int dy = 0; dy < D1D; ++dy) {
xy += r_x[dy] * s_B(qy, dy);
xDy += r_x[dy] * s_G(qy, dy);
}
s_xy(dx, qy) = xy;
s_xDy(dx, qy) = xDy;
}
}
}
for (int qy = 0; qy < M1D; ++qy; @inner) {
if (qy < Q1D) {
for (int qx = 0; qx < Q1D; ++qx) {
double gradX = 0, gradY = 0;
for (int dx = 0; dx < D1D; ++dx) {
gradX += s_xy(dx, qy) * s_G(qx, dx);
gradY += s_xDy(dx, qy) * s_B(qx, dx);
}
const int q = QUAD_2D_ID(qx, qy);
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);
}
}
}
for (int qx = 0; qx < Q1D; ++qx; @inner) {
if (qx < Q1D) {
for (int dy = 0; dy < D1D; ++dy) {
s_xy(dy, qx) = 0;
s_xDy(dy, qx) = 0;
}
for (int qy = 0; qy < Q1D; ++qy) {
r_x[qy] = s_grad(0, qx, qy);
r_y[qy] = s_grad(1, qx, qy);
}
for (int dy = 0; dy < D1D; ++dy) {
double xy = 0;
double xDy = 0;
for (int qy = 0; qy < Q1D; ++qy) {
xy += r_x[qy] * s_Bt(dy, qy);
xDy += r_y[qy] * s_Gt(dy, qy);
}
s_xy(dy, qx) = xy;
s_xDy(dy, qx) = xDy;
}
}
}
for (int dx = 0; dx < D1D; ++dx; @inner) {
if (dx < D1D) {
for (int dy = 0; dy < D1D; ++dy) {
double s = 0;
for (int qx = 0; qx < Q1D; ++qx) {
s += ((s_xy(dy, qx) * s_Gt(dx, qx)) +
(s_xDy(dy, qx) * s_Bt(dx, qx)));
}
Y(dx, dy, e) += s;
}
}
}
}
}
}
}
@kernel void DiffusionApply3D_CPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const DofToQuad_t G,
@restrict const QuadToDof_t Bt,
@restrict const QuadToDof_t Gt,
@restrict const SymmOperator3D_t op,
@restrict const DLocal3D_t X,
@restrict DLocal3D_t Y) {
// Iterate over elements
for (int e = 0; e < NE; ++e; @outer) {
for (int dummy = 0; dummy < 1; ++dummy; @inner) {
double grad[Q1D][Q1D][Q1D][4];
for (int qz = 0; qz < Q1D; ++qz) {
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
grad[qz][qy][qx][0] = 0;
grad[qz][qy][qx][1] = 0;
grad[qz][qy][qx][2] = 0;
}
}
}
for (int dz = 0; dz < D1D; ++dz) {
double gradXY[Q1D][Q1D][4];
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
gradXY[qy][qx][0] = 0;
gradXY[qy][qx][1] = 0;
gradXY[qy][qx][2] = 0;
}
}
for (int dy = 0; dy < D1D; ++dy) {
double gradX[Q1D][2];
for (int qx = 0; qx < Q1D; ++qx) {
gradX[qx][0] = 0;
gradX[qx][1] = 0;
}
for (int dx = 0; dx < D1D; ++dx) {
const double s = X(dx, dy, dz, e);
for (int qx = 0; qx < Q1D; ++qx) {
gradX[qx][0] += s * B(qx, dx);
gradX[qx][1] += s * G(qx, dx);
}
}
for (int qy = 0; qy < Q1D; ++qy) {
const double wy = B(qy, dy);
const double wDy = G(qy, dy);
for (int qx = 0; qx < Q1D; ++qx) {
const double wx = gradX[qx][0];
const double wDx = gradX[qx][1];
gradXY[qy][qx][0] += wDx * wy;
gradXY[qy][qx][1] += wx * wDy;
gradXY[qy][qx][2] += wx * wy;
}
}
}
for (int qz = 0; qz < Q1D; ++qz) {
const double wz = B(qz, dz);
const double wDz = G(qz, dz);
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
grad[qz][qy][qx][0] += gradXY[qy][qx][0] * wz;
grad[qz][qy][qx][1] += gradXY[qy][qx][1] * wz;
grad[qz][qy][qx][2] += gradXY[qy][qx][2] * wDz;
}
}
}
}
// Calculate Dxyz, xDyz, xyDz in plane
for (int qz = 0; qz < Q1D; ++qz) {
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
const int q = QUAD_3D_ID(qx, qy, qz);
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];
const double gradZ = grad[qz][qy][qx][2];
grad[qz][qy][qx][0] = (O11 * gradX) + (O12 * gradY) + (O13 * gradZ);
grad[qz][qy][qx][1] = (O12 * gradX) + (O22 * gradY) + (O23 * gradZ);
grad[qz][qy][qx][2] = (O13 * gradX) + (O23 * gradY) + (O33 * gradZ);
}
}
}
for (int qz = 0; qz < Q1D; ++qz) {
double gradXY[D1D][D1D][4];
for (int dy = 0; dy < D1D; ++dy) {
for (int dx = 0; dx < D1D; ++dx) {
gradXY[dy][dx][0] = 0;
gradXY[dy][dx][1] = 0;
gradXY[dy][dx][2] = 0;
}
}
for (int qy = 0; qy < Q1D; ++qy) {
double gradX[D1D][4];
for (int dx = 0; dx < D1D; ++dx) {
gradX[dx][0] = 0;
gradX[dx][1] = 0;
gradX[dx][2] = 0;
}
for (int qx = 0; qx < Q1D; ++qx) {
const double gX = grad[qz][qy][qx][0];
const double gY = grad[qz][qy][qx][1];
const double gZ = grad[qz][qy][qx][2];
for (int dx = 0; dx < D1D; ++dx) {
const double wx = Bt(dx, qx);
const double wDx = Gt(dx, qx);
gradX[dx][0] += gX * wDx;
gradX[dx][1] += gY * wx;
gradX[dx][2] += gZ * wx;
}
}
for (int dy = 0; dy < D1D; ++dy) {
const double wy = Bt(dy, qy);
const double wDy = Gt(dy, qy);
for (int dx = 0; dx < D1D; ++dx) {
gradXY[dy][dx][0] += gradX[dx][0] * wy;
gradXY[dy][dx][1] += gradX[dx][1] * wDy;
gradXY[dy][dx][2] += gradX[dx][2] * wy;
}
}
}
for (int dz = 0; dz < D1D; ++dz) {
const double wz = Bt(dz, qz);
const double wDz = Gt(dz, qz);
for (int dy = 0; dy < D1D; ++dy) {
for (int dx = 0; dx < D1D; ++dx) {
Y(dx, dy, dz, e) += ((gradXY[dy][dx][0] * wz) +
(gradXY[dy][dx][1] * wz) +
(gradXY[dy][dx][2] * wDz));
}
}
}
}
}
}
}
@kernel void DiffusionApply3D_GPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const DofToQuad_t G,
@restrict const QuadToDof_t Bt,
@restrict const QuadToDof_t Gt,
@restrict const SymmOperator3D_t op,
@restrict const DLocal3D_t X,
@restrict DLocal3D_t Y) {
// Iterate over elements
for (int e = 0; e < NE; ++e; @outer) {
// Store dof <--> quad mappings
@shared double s_B[DQ1D] @dim(Q1D, D1D);
@shared double s_G[DQ1D] @dim(Q1D, D1D);
@shared double s_Bt[DQ1D] @dim(D1D, Q1D);
@shared double s_Gt[DQ1D] @dim(D1D, Q1D);
// Store xy planes in @shared memory
@shared double s_z[M2D] @dim(M1D, M1D);
@shared double s_Dz[M2D] @dim(M1D, M1D);
@shared double s_xyDz[Q2D] @dim(Q1D, Q1D);
// Store z axis as registers
@exclusive double r_qz[Q1D];
@exclusive double r_qDz[Q1D];
@exclusive double r_dDxyz[D1D];
@exclusive double r_dxDyz[D1D];
@exclusive double r_dxyDz[D1D];
for (int y = 0; y < M1D; ++y; @inner) {
for (int x = 0; x < M1D; ++x; @inner) {
const int id = (y * M1D) + x;
// Fetch Q <--> D maps
if (id < DQ1D) {
s_B[id] = B[id];
s_G[id] = G[id];
s_Bt[id] = Bt[id];
s_Gt[id] = Gt[id];
}
// Initialize our Z axis
for (int qz = 0; qz < Q1D; ++qz) {
r_qz[qz] = 0;
r_qDz[qz] = 0;
}
// Initialize our solution updates in the Z axis
for (int dz = 0; dz < D1D; ++dz) {
r_dDxyz[dz] = 0;
r_dxDyz[dz] = 0;
r_dxyDz[dz] = 0;
}
}
}
for (int dy = 0; dy < M1D; ++dy; @inner) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if ((dx < D1D) && (dy < D1D)) {
for (int dz = 0; dz < D1D; ++dz) {
const double s = X(dx, dy, dz, e);
// Calculate D -> Q in the Z axis
for (int qz = 0; qz < Q1D; ++qz) {
r_qz[qz] += s * s_B(qz, dz);
r_qDz[qz] += s * s_G(qz, dz);
}
}
}
}
}
// For each xy plane
for (int qz = 0; qz < Q1D; ++qz) {
// Fill xy plane at given z position
for (int dy = 0; dy < M1D; ++dy; @inner) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if ((dx < D1D) && (dy < D1D)) {
s_z(dx, dy) = r_qz[qz];
s_Dz(dx, dy) = r_qDz[qz];
}
}
}
// Calculate Dxyz, xDyz, xyDz in plane
for (int qy = 0; qy < M1D; ++qy; @inner) {
for (int qx = 0; qx < M1D; ++qx; @inner) {
if ((qx < Q1D) && (qy < Q1D)) {
double Dxyz = 0;
double xDyz = 0;
double xyDz = 0;
for (int dy = 0; dy < D1D; ++dy) {
const double wy = s_B(qy, dy);
const double wDy = s_G(qy, dy);
for (int dx = 0; dx < D1D; ++dx) {
const double wx = s_B(qx, dx);
const double wDx = s_G(qx, dx);
const double z = s_z(dx, dy);
const double Dz = s_Dz(dx, dy);
Dxyz += wDx * wy * z;
xDyz += wx * wDy * z;
xyDz += wx * wy * Dz;
}
}
const int q = QUAD_3D_ID(qx, qy, qz);
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);
const double qxyDz = (O13 * Dxyz) + (O23 * xDyz) + (O33 * xyDz);
for (int dz = 0; dz < D1D; ++dz) {
const double wz = s_Bt(dz, qz);
const double wDz = s_Gt(dz, qz);
r_dDxyz[dz] += wz * qDxyz;
r_dxDyz[dz] += wz * qxDyz;
r_dxyDz[dz] += wDz * qxyDz;
}
}
}
}
@barrier("s_z_s_Dz_sync_1");
}
// Iterate over xy planes to compute solution
for (int dz = 0; dz < D1D; ++dz) {
// Place xy plane in @shared memory
for (int qy = 0; qy < M1D; ++qy; @inner) {
for (int qx = 0; qx < M1D; ++qx; @inner) {
if ((qx < Q1D) && (qy < Q1D)) {
s_z(qx, qy) = r_dDxyz[dz];
s_Dz(qx, qy) = r_dxDyz[dz];
s_xyDz(qx, qy) = r_dxyDz[dz];
}
}
}
// Finalize solution in xy plane
for (int dy = 0; dy < M1D; ++dy; @inner) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if ((dx < D1D) && (dy < D1D)) {
double solZ = 0;
for (int qy = 0; qy < Q1D; ++qy) {
const double wy = s_Bt(dy, qy);
const double wDy = s_Gt(dy, qy);
for (int qx = 0; qx < Q1D; ++qx) {
const double wx = s_Bt(dx, qx);
const double wDx = s_Gt(dx, qx);
const double Dxyz = s_z(qx, qy);
const double xDyz = s_Dz(qx, qy);
const double xyDz = s_xyDz(qx, qy);
solZ += ((wDx * wy * Dxyz) +
(wx * wDy * xDyz) +
(wx * wy * xyDz));
}
}
Y(dx, dy, dz, e) += solZ;
}
}
}
@barrier("s_z_s_Dz_s_xyDz_sync_1");
}
}
}
@kernel void MassApply2D_CPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const QuadToDof_t Bt,
@restrict const QLocal2D_t op,
@restrict const DLocal2D_t X,
@restrict DLocal2D_t Y) {
for (int e = 0; e < NE; ++e; @outer) {
for (int dummy = 0; dummy < 1; ++dummy; @inner) {
double sol_xy[Q1D][Q1D];
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
sol_xy[qy][qx] = 0;
}
}
for (int dy = 0; dy < D1D; ++dy) {
double sol_x[Q1D];
for (int qy = 0; qy < Q1D; ++qy) {
sol_x[qy] = 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[D1D];
for (int dx = 0; dx < D1D; ++dx) {
sol_x[dx] = 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];
}
}
}
}
}
}
@kernel void MassApply3D_CPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const QuadToDof_t Bt,
@restrict const QLocal3D_t op,
@restrict const DLocal3D_t X,
@restrict DLocal3D_t Y) {
// Iterate over elements
for (int e = 0; e < NE; ++e; @outer) {
for (int dummy = 0; dummy < 1; ++dummy; @inner) {
double sol_xyz[Q1D][Q1D][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;
}
}
}
for (int dz = 0; dz < D1D; ++dz) {
double sol_xy[Q1D][Q1D];
for (int qy = 0; qy < Q1D; ++qy) {
for (int qx = 0; qx < Q1D; ++qx) {
sol_xy[qy][qx] = 0;
}
}
for (int dy = 0; dy < D1D; ++dy) {
double sol_x[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[D1D][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[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];
}
}
}
}
}
}
}
@kernel void MassApply2D_GPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const QuadToDof_t Bt,
@restrict const QLocal2D_t op,
@restrict const DLocal2D_t X,
@restrict DLocal2D_t Y) {
// Iterate over elements
for (int eOff = 0; eOff < NE; eOff += M2_ELEMENT_BATCH; @outer) {
// Store dof <--> quad mappings
@shared double s_B[DQ1D] @dim(Q1D, D1D);
@shared double s_Bt[DQ1D] @dim(D1D, Q1D);
// Store xy planes in @shared memory
@shared double s_xy[DQ1D] @dim(D1D, Q1D);
@shared double s_xy2[Q2D] @dim(Q1D, Q1D);
@exclusive double r_x[M1D];
for (int x = 0; x < M1D; ++x; @inner) {
for (int id = x; id < DQ1D; id += M1D) {
s_B[id] = B[id];
s_Bt[id] = Bt[id];
}
}
for (int e = eOff; e < (eOff + M2_ELEMENT_BATCH); ++e) {
if (e < NE) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if (dx < D1D) {
for (int qy = 0; qy < Q1D; ++qy) {
s_xy(dx, qy) = 0;
}
for (int dy = 0; dy < D1D; ++dy) {
r_x[dy] = X(dx, dy, e);
}
for (int qy = 0; qy < Q1D; ++qy) {
double xy = 0;
for (int dy = 0; dy < D1D; ++dy) {
xy += r_x[dy] * s_B(qy, dy);
}
s_xy(dx, qy) = xy;
}
}
}
for (int qy = 0; qy < M1D; ++qy; @inner) {
if (qy < Q1D) {
for (int qx = 0; qx < Q1D; ++qx) {
double s = 0;
for (int dx = 0; dx < D1D; ++dx) {
s += s_xy(dx, qy) * s_B(qx, dx);
}
s_xy2(qx, qy) = s * op(qx, qy, e);
}
}
}
for (int qx = 0; qx < M1D; ++qx; @inner) {
if (qx < Q1D) {
for (int dy = 0; dy < D1D; ++dy) {
s_xy(dy, qx) = 0;
}
for (int qy = 0; qy < Q1D; ++qy) {
r_x[qy] = s_xy2(qx, qy);
}
for (int dy = 0; dy < D1D; ++dy) {
double s = 0;
for (int qy = 0; qy < Q1D; ++qy) {
s += r_x[qy] * s_Bt(dy, qy);
}
s_xy(dy, qx) = s;
}
}
}
for (int dx = 0; dx < M1D; ++dx; @inner) {
if (dx < D1D) {
for (int dy = 0; dy < D1D; ++dy) {
double s = 0;
for (int qx = 0; qx < Q1D; ++qx) {
s += (s_xy(dy, qx) * s_Bt(dx, qx));
}
Y(dx, dy, e) += s;
}
}
}
}
}
}
}
@kernel void MassApply3D_GPU(const int NE,
@restrict const DofToQuad_t B,
@restrict const QuadToDof_t Bt,
@restrict const QLocal3D_t op,
@restrict const DLocal3D_t X,
@restrict DLocal3D_t Y) {
// Iterate over elements
for (int e = 0; e < NE; ++e; @outer) {
// Store dof <--> quad mappings
@shared double s_B[DQ1D] @dim(Q1D, D1D);
@shared double s_Bt[DQ1D] @dim(D1D, Q1D);
// Store xy planes in @shared memory
@shared double s_xy[M2D] @dim(M1D, M1D);
// Store z axis as registers
@exclusive double r_z[Q1D];
@exclusive double r_z2[D1D];
for (int y = 0; y < M1D; ++y; @inner) {
for (int x = 0; x < M1D; ++x; @inner) {
const int id = (y * M1D) + x;
// Fetch Q <--> D maps
if (id < DQ1D) {
s_B[id] = B[id];
s_Bt[id] = Bt[id];
}
// Initialize our Z axis
for (int qz = 0; qz < Q1D; ++qz) {
r_z[qz] = 0;
}
for (int dz = 0; dz < D1D; ++dz) {
r_z2[dz] = 0;
}
}
}
for (int dy = 0; dy < M1D; ++dy; @inner) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if ((dx < D1D) && (dy < D1D)) {
for (int dz = 0; dz < D1D; ++dz) {
const double s = X(dx, dy, dz, e);
// Calculate D -> Q in the Z axis
for (int qz = 0; qz < Q1D; ++qz) {
r_z[qz] += s * s_B(qz, dz);
}
}
}
}
}
// For each xy plane
for (int qz = 0; qz < Q1D; ++qz) {
// Fill xy plane at given z position
for (int dy = 0; dy < M1D; ++dy; @inner) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if ((dx < D1D) && (dy < D1D)) {
s_xy(dx, dy) = r_z[qz];
}
}
}
// Calculate Dxyz, xDyz, xyDz in plane
for (int qy = 0; qy < M1D; ++qy; @inner) {
for (int qx = 0; qx < M1D; ++qx; @inner) {
if ((qx < Q1D) && (qy < Q1D)) {
double s = 0;
for (int dy = 0; dy < D1D; ++dy) {
const double wy = s_B(qy, dy);
for (int dx = 0; dx < D1D; ++dx) {
const double wx = s_B(qx, dx);
s += wx * wy * s_xy(dx, dy);
}
}
s *= op(qx, qy, qz, e);
for (int dz = 0; dz < D1D; ++dz) {
const double wz = s_Bt(dz, qz);
r_z2[dz] += wz * s;
}
}
}
}
@barrier("s_xy_sync_1");
}
// Iterate over xy planes to compute solution
for (int dz = 0; dz < D1D; ++dz) {
// Place xy plane in @shared memory
for (int qy = 0; qy < M1D; ++qy; @inner) {
for (int qx = 0; qx < M1D; ++qx; @inner) {
if ((qx < Q1D) && (qy < Q1D)) {
s_xy(qx, qy) = r_z2[dz];
}
}
}
// Finalize solution in xy plane
for (int dy = 0; dy < M1D; ++dy; @inner) {
for (int dx = 0; dx < M1D; ++dx; @inner) {
if ((dx < D1D) && (dy < D1D)) {
double solZ = 0;
for (int qy = 0; qy < Q1D; ++qy) {
const double wy = s_Bt(dy, qy);
for (int qx = 0; qx < Q1D; ++qx) {
const double wx = s_Bt(dx, qx);
solZ += wx * wy * s_xy(qx, qy);
}
}
Y(dx, dy, dz, e) += solZ;
}
}
}
@barrier("s_xy_sync_2");
}
}
}
+3 -21
View File
@@ -284,12 +284,6 @@ void ParBilinearForm::FormLinearSystem(
const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B, int copy_interior)
{
if (ext)
{
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
return;
}
// Finish the matrix assembly and perform BC elimination, storing the
// eliminated part of the matrix.
FormSystemMatrix(ess_tdof_list, A);
@@ -333,12 +327,6 @@ void ParBilinearForm::FormLinearSystem(
void ParBilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
if (ext)
{
ext->FormSystemMatrix(ess_tdof_list, A);
return;
}
// Finish the matrix assembly and perform BC elimination, storing the
// eliminated part of the matrix.
if (static_cond)
@@ -381,12 +369,6 @@ void ParBilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
void ParBilinearForm::RecoverFEMSolution(
const Vector &X, const Vector &b, Vector &x)
{
if (ext)
{
ext->RecoverFEMSolution(X, b, x);
return;
}
const Operator &P = *pfes->GetProlongationMatrix();
if (static_cond)
@@ -485,8 +467,8 @@ void ParMixedBilinearForm::TrueAddMult(const Vector &x, Vector &y,
HypreParMatrix* ParDiscreteLinearOperator::ParallelAssemble() const
{
MFEM_ASSERT(mat, "Matrix is not assembled");
MFEM_ASSERT(mat->Finalized(), "Matrix is not finalized");
MFEM_ASSERT(mat, "matrix is not assembled");
MFEM_ASSERT(mat->Finalized(), "matrix is not finalized");
SparseMatrix* RA = mfem::Mult(*range_fes->GetRestrictionMatrix(), *mat);
HypreParMatrix* P = domain_fes->Dof_TrueDof_Matrix();
HypreParMatrix* RAP = P->LeftDiagMult(*RA, range_fes->GetTrueDofOffsets());
@@ -497,7 +479,7 @@ HypreParMatrix* ParDiscreteLinearOperator::ParallelAssemble() const
void ParDiscreteLinearOperator::GetParBlocks(Array2D<HypreParMatrix *> &blocks)
const
{
MFEM_VERIFY(mat->Finalized(), "Local matrix needs to be finalized for "
MFEM_VERIFY(mat->Finalized(), "local matrix needs to be finalized for "
"GetParBlocks");
HypreParMatrix* RLP = ParallelAssemble();
+58 -9
View File
@@ -75,8 +75,7 @@ public:
those rows. Must be called before the first Assemble call. */
void KeepNbrBlock(bool knb = true) { keep_nbr_block = knb; }
/** @brief Set the operator type id for the parallel matrix/operator when
using AssemblyLevel::FULL. */
/// Set the operator type id for the parallel matrix/operator.
/** If using static condensation or hybridization, call this method *after*
enabling it. */
void SetOperatorType(Operator::Type tid)
@@ -164,15 +163,65 @@ public:
virtual const Operator *GetRestriction() const
{ return pfes->GetRestrictionMatrix(); }
using BilinearForm::FormLinearSystem;
using BilinearForm::FormSystemMatrix;
/** Form the linear system A X = B, corresponding to the current bilinear
form and b(.), by applying any necessary transformations such as:
eliminating boundary conditions; applying conforming constraints for
non-conforming AMR; parallel assembly; static condensation;
hybridization.
virtual void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
Vector &b, OperatorHandle &A, Vector &X,
Vector &B, int copy_interior = 0);
The ParGridFunction-size vector x must contain the essential b.c. The
ParBilinearForm and the ParLinearForm-size vector b must be assembled.
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A);
The vector X is initialized with a suitable initial guess: when using
hybridization, the vector X is set to zero; otherwise, the essential
entries of X are set to the corresponding b.c. and all other entries are
set to zero (copy_interior == 0) or copied from x (copy_interior != 0).
This method can be called multiple times (with the same ess_tdof_list
array) to initialize different right-hand sides and boundary condition
values.
After solving the linear system, the finite element solution x can be
recovered by calling RecoverFEMSolution (with the same vectors X, b, and
x). */
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
/** Version of the method FormLinearSystem() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called. */
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OpType &A, Vector &X, Vector &B,
int copy_interior = 0)
{
OperatorHandle Ah;
FormLinearSystem(ess_tdof_list, x, b, Ah, X, B, copy_interior);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
/// Form the linear system matrix @a A, see FormLinearSystem() for details.
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
/** Version of the method FormSystemMatrix() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called. */
template <typename OpType>
void FormSystemMatrix(const Array<int> &ess_tdof_list, OpType &A)
{
OperatorHandle Ah;
FormSystemMatrix(ess_tdof_list, Ah);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
/** Call this method after solving a linear system constructed using the
FormLinearSystem method to recover the solution as a ParGridFunction-size
+16 -20
View File
@@ -613,15 +613,15 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
int ldof = GetVSize();
int ltdof = TrueVSize();
HYPRE_Int *i_diag = mfem::New<HYPRE_Int>(ldof+1);
HYPRE_Int *j_diag = mfem::New<HYPRE_Int>(ltdof);
HYPRE_Int *i_diag = new HYPRE_Int[ldof+1];
HYPRE_Int *j_diag = new HYPRE_Int[ltdof];
int diag_counter;
HYPRE_Int *i_offd = mfem::New<HYPRE_Int>(ldof+1);
HYPRE_Int *j_offd = mfem::New<HYPRE_Int>(ldof-ltdof);
HYPRE_Int *i_offd = new HYPRE_Int[ldof+1];
HYPRE_Int *j_offd = new HYPRE_Int[ldof-ltdof];
int offd_counter;
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(ldof-ltdof);
HYPRE_Int *cmap = new HYPRE_Int[ldof-ltdof];
HYPRE_Int *col_starts = GetTrueDofOffsets();
HYPRE_Int *row_starts = GetDofOffsets();
@@ -2249,7 +2249,7 @@ HypreParMatrix* ParFiniteElementSpace
}
// create offd column mapping
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(col_map.size());
HYPRE_Int *cmap = 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)
@@ -2258,14 +2258,14 @@ HypreParMatrix* ParFiniteElementSpace
it->second = offd_col++;
}
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 *I_diag = new HYPRE_Int[vdim*local_rows + 1];
HYPRE_Int *I_offd = new HYPRE_Int[vdim*local_rows + 1];
HYPRE_Int *J_diag = mfem::New<HYPRE_Int>(nnz_diag);
HYPRE_Int *J_offd = mfem::New<HYPRE_Int>(nnz_offd);
HYPRE_Int *J_diag = new HYPRE_Int[nnz_diag];
HYPRE_Int *J_offd = new HYPRE_Int[nnz_offd];
double *A_diag = mfem::New<double>(nnz_diag);
double *A_offd = mfem::New<double>(nnz_offd);
double *A_diag = new double[nnz_diag];
double *A_offd = new double[nnz_offd];
int vdim1 = bynodes ? vdim : 1;
int vdim2 = bynodes ? 1 : vdim;
@@ -2316,7 +2316,7 @@ HypreParMatrix* ParFiniteElementSpace
static HYPRE_Int* make_i_array(int nrows)
{
HYPRE_Int *I = mfem::New<HYPRE_Int>(nrows+1);
HYPRE_Int *I = new HYPRE_Int[nrows+1];
for (int i = 0; i <= nrows; i++) { I[i] = -1; }
return I;
}
@@ -2328,7 +2328,7 @@ static HYPRE_Int* make_j_array(HYPRE_Int* I, int nrows)
{
if (I[i] >= 0) { nnz++; }
}
HYPRE_Int *J = mfem::New<HYPRE_Int>(nnz);
HYPRE_Int *J = new HYPRE_Int[nnz];
I[nrows] = -1;
for (int i = 0, k = 0; i <= nrows; i++)
@@ -2427,7 +2427,7 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
}
SortPairs<HYPRE_Int, int>(cmap_offd, offd_cols);
HYPRE_Int* cmap = mfem::New<HYPRE_Int>(offd_cols);
HYPRE_Int* cmap = new HYPRE_Int[offd_cols];
for (int i = 0; i < offd_cols; i++)
{
cmap[i] = cmap_offd[i].one;
@@ -2623,7 +2623,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
offd->SetWidth(col_map.size());
// create offd column mapping for use by hypre
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(offd->Width());
HYPRE_Int *cmap = new HYPRE_Int[offd->Width()];
for (std::map<HYPRE_Int, int>::iterator
it = col_map.begin(); it != col_map.end(); ++it)
{
@@ -2865,7 +2865,6 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
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
@@ -2882,7 +2881,6 @@ 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(
@@ -2893,7 +2891,6 @@ void ConformingProlongationOperator::MultTranspose(
const double *xdata = x.GetData();
double *ydata = y.GetData();
x.Pull();
const int m = external_ldofs.Size();
gc.ReduceBegin(xdata);
@@ -2909,7 +2906,6 @@ void ConformingProlongationOperator::MultTranspose(
const int out_layout = 2; // 2 - output is an array on all ltdofs
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
y.Push();
}
} // namespace mfem
-10
View File
@@ -45,16 +45,6 @@ public:
/** The pointer @a pf is not owned by the newly constructed object. */
ParLinearForm(ParFiniteElementSpace *pf) : LinearForm(pf) { pfes = pf; }
/// Construct a ParLinearForm using previously allocated array @a data.
/** The ParLinearForm does not assume ownership of @a data which is assumed
to be of size at least `pf->GetVSize()`. Similar to the LinearForm and
Vector constructors for externally allocated array, the pointer @a data
can be NULL. The data array can be replaced later using the method
SetData().
*/
ParLinearForm(ParFiniteElementSpace *pf, double *data) :
LinearForm(pf, data), pfes(pf) { }
/** @brief Create a ParLinearForm on the ParFiniteElementSpace @a *pf, using
the same integrators as the ParLinearForm @a *plf.
-10
View File
@@ -11,14 +11,10 @@
list(APPEND SRCS
array.cpp
cuda.cpp
device.cpp
error.cpp
globals.cpp
gzstream.cpp
isockstream.cpp
mem_manager.cpp
occa.cpp
optparser.cpp
osockstream.cpp
sets.cpp
@@ -31,18 +27,12 @@ list(APPEND SRCS
list(APPEND HDRS
array.hpp
binaryio.hpp
cuda.hpp
device.hpp
error.hpp
globals.hpp
gzstream.hpp
hash.hpp
isockstream.hpp
mem_alloc.hpp
mem_manager.hpp
occa.hpp
forall.hpp
optparser.hpp
osockstream.hpp
sets.hpp
+5 -6
View File
@@ -13,7 +13,6 @@
// Abstract array data type
#include "array.hpp"
#include "../general/forall.hpp"
#include <fstream>
namespace mfem
@@ -23,7 +22,7 @@ BaseArray::BaseArray(int asize, int ainc, int elementsize)
{
if (asize > 0)
{
data = mfem::New<char>(asize * elementsize);
data = new char[asize * elementsize];
size = allocsize = asize;
}
else
@@ -38,7 +37,7 @@ BaseArray::~BaseArray()
{
if (allocsize > 0)
{
mfem::Delete((char*)data);
delete [] (char*)data;
}
}
@@ -48,14 +47,14 @@ void BaseArray::GrowSize(int minsize, int elementsize)
int nsize = (inc > 0) ? abs(allocsize) + inc : 2 * abs(allocsize);
if (nsize < minsize) { nsize = minsize; }
p = mfem::New<char>(nsize * elementsize);
p = new char[nsize * elementsize];
if (size > 0)
{
mfem::Memcpy(p, data, size * elementsize);
memcpy(p, data, size * elementsize);
}
if (allocsize > 0)
{
mfem::Delete((char*)data);
delete [] (char*)data;
}
data = p;
allocsize = nsize;
+5 -6
View File
@@ -13,7 +13,6 @@
#define MFEM_ARRAY
#include "../config/config.hpp"
#include "mem_manager.hpp"
#include "error.hpp"
#include "globals.hpp"
@@ -83,7 +82,7 @@ public:
/// Copy constructor: deep copy
Array(const Array<T> &src)
: BaseArray(src.size, 0, sizeof(T))
{ mfem::Memcpy(data, src.data, size*sizeof(T)); }
{ std::memcpy(data, src.data, size*sizeof(T)); }
/// Copy constructor (deep copy) from an Array of convertable type
template <typename CT>
@@ -191,7 +190,7 @@ public:
inline void Copy(Array &copy) const
{
copy.SetSize(Size());
mfem::Memcpy(copy.GetData(), data, Size()*sizeof(T));
std::memcpy(copy.GetData(), data, Size()*sizeof(T));
}
/// Make this Array a reference to a pointer
@@ -714,7 +713,7 @@ inline void Array<T>::DeleteAll()
{
if (allocsize > 0)
{
mfem::Delete((char*)data);
delete [] (char*)data;
}
data = NULL;
size = allocsize = 0;
@@ -725,7 +724,7 @@ inline void Array<T>::MakeRef(T *p, int s)
{
if (allocsize > 0)
{
mfem::Delete((char*)data);
delete [] (char*)data;
}
data = p;
size = s;
@@ -737,7 +736,7 @@ inline void Array<T>::MakeRef(const Array &master)
{
if (allocsize > 0)
{
mfem::Delete((char*)data);
delete [] (char*)data;
}
data = master.data;
size = master.size;
-110
View File
@@ -1,110 +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 "cuda.hpp"
namespace mfem
{
void* CuMemAlloc(void** dptr, size_t bytes)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS != ::cuMemAlloc((CUdeviceptr*)dptr, bytes))
{
mfem_error("Error in CuMemAlloc");
}
#endif
return *dptr;
}
void* CuMemFree(void *dptr)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS != ::cuMemFree((CUdeviceptr)dptr))
{
mfem_error("Error in CuMemFree");
}
#endif
return dptr;
}
void* CuMemcpyHtoD(void* dst, const void* src, size_t bytes)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS != ::cuMemcpyHtoD((CUdeviceptr)dst, src, bytes))
{
mfem_error("Error in CuMemcpyHtoD");
}
#endif
return dst;
}
void* CuMemcpyHtoDAsync(void* dst, const void* src, size_t bytes, void *s)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS !=
::cuMemcpyHtoDAsync((CUdeviceptr)dst, src, bytes, (CUstream)s))
{
mfem_error("Error in CuMemcpyHtoDAsync");
}
#endif
return dst;
}
void* CuMemcpyDtoD(void* dst, void* src, size_t bytes)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS !=
::cuMemcpyDtoD((CUdeviceptr)dst, (CUdeviceptr)src, bytes))
{
mfem_error("Error in CuMemcpyDtoD");
}
#endif
return dst;
}
void* CuMemcpyDtoDAsync(void* dst, void* src, size_t bytes, void *s)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS !=
::cuMemcpyDtoDAsync((CUdeviceptr)dst, (CUdeviceptr)src,
bytes, (CUstream)s))
{
mfem_error("Error in CuMemcpyDtoDAsync");
}
#endif
return dst;
}
void* CuMemcpyDtoH(void *dst, void *src, size_t bytes)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS != ::cuMemcpyDtoH(dst, (CUdeviceptr)src, bytes))
{
mfem_error("Error in CuMemcpyDtoH");
}
#endif
return dst;
}
void* CuMemcpyDtoHAsync(void* dst, void* src, size_t bytes, void *s)
{
#ifdef MFEM_USE_CUDA
if (CUDA_SUCCESS !=
::cuMemcpyDtoHAsync(dst, (CUdeviceptr)src, bytes, (CUstream)s))
{
mfem_error("Error in CuMemcpyDtoHAsync");
}
#endif
return dst;
}
} // namespace mfem
-140
View File
@@ -1,140 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_CUDA_HPP
#define MFEM_CUDA_HPP
#include "../config/config.hpp"
#include "error.hpp"
#ifdef MFEM_USE_CUDA
#include <cuda_runtime.h>
#include <cuda.h>
#endif
// CUDA block size used by MFEM.
#define MFEM_CUDA_BLOCKS 256
#ifdef MFEM_USE_CUDA
#define MFEM_ATTR_DEVICE __device__
#define MFEM_ATTR_HOST_DEVICE __host__ __device__
// Define the CUDA debug macros:
// - MFEM_CUDA_CHECK_DRV(x) where 'x' returns/is type 'CUresult'
// - MFEM_CUDA_CHECK_RT(x) where 'x' returns/is type 'cudaError_t'
#ifdef MFEM_DEBUG
#define MFEM_CUDA_CHECK_DRV(x) \
do \
{ \
CUresult err = (x); \
if (err != CUDA_SUCCESS) \
{ \
const char *error_string; \
cuGetErrorString(err, &error_string); \
_MFEM_MESSAGE("CUDA error: (" << #x \
<< ") failed with error:\n --> " \
<< error_string, 0); \
} \
} \
while (0)
#define MFEM_CUDA_CHECK_RT(x) \
do \
{ \
cudaError_t err = (x); \
if (err != cudaSuccess) \
{ \
_MFEM_MESSAGE("CUDA error: (" << #x \
<< ") failed with error:\n --> " \
<< cudaGetErrorString(err), 0); \
} \
} \
while (0)
#else
#define MFEM_CUDA_CHECK_DRV(x) x
#define MFEM_CUDA_CHECK_RT(x) x
#endif
#else // MFEM_USE_CUDA
#define MFEM_ATTR_DEVICE
#define MFEM_ATTR_HOST_DEVICE
typedef int CUdevice;
typedef int CUcontext;
typedef void* CUstream;
#endif // MFEM_USE_CUDA
namespace mfem
{
// Define 'atomicAdd' function.
#ifdef __CUDA_ARCH__
#if __CUDA_ARCH__ < 600
static __device__ inline double atomicAdd(double* address, double val)
{
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do
{
assumed = old;
old =
atomicCAS(address_as_ull, assumed,
__double_as_longlong(val +
__longlong_as_double(assumed)));
// Note: uses integer comparison to avoid hang in case of NaN
// (since NaN != NaN)
}
while (assumed != old);
return __longlong_as_double(old);
}
#endif // __CUDA_ARCH__ < 600
template<typename T> MFEM_ATTR_DEVICE
inline T AtomicAdd(T volatile *address, T val)
{
return atomicAdd((T *)address, val);
}
#else // __CUDA_ARCH__
template<typename T> inline T AtomicAdd(T volatile *address, T val)
{
#ifdef MFEM_USE_OPENMP
#pragma omp atomic
#endif
*address += val;
return *address;
}
#endif // __CUDA_ARCH__
/// Allocates device memory
void* CuMemAlloc(void **d_ptr, size_t bytes);
/// Frees device memory
void* CuMemFree(void *d_ptr);
/// Copies memory from Host to Device
void* CuMemcpyHtoD(void *d_dst, const void *h_src, size_t bytes);
/// Copies memory from Host to Device
void* CuMemcpyHtoDAsync(void *d_dst, const void *h_src,
size_t bytes, void *stream);
/// Copies memory from Device to Device
void* CuMemcpyDtoD(void *d_dst, void *d_src, size_t bytes);
/// Copies memory from Device to Device
void* CuMemcpyDtoDAsync(void *d_dst, void *d_src, size_t bytes, void *stream);
/// Copies memory from Device to Host
void* CuMemcpyDtoH(void *h_dst, void *d_src, size_t bytes);
/// Copies memory from Device to Host
void* CuMemcpyDtoHAsync(void *h_dst, void *d_src, size_t bytes, void *stream);
} // namespace mfem
#endif // MFEM_CUDA_HPP
-218
View File
@@ -1,218 +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 "forall.hpp"
#include "cuda.hpp"
#include "occa.hpp"
#include <string>
#include <map>
namespace mfem
{
// Place the following variables in the mfem::internal namespace, so that they
// will not be included in the doxygen documentation.
namespace internal
{
CUstream *cuStream = NULL;
static CUdevice cuDevice;
static CUcontext cuContext;
OccaDevice occaDevice;
// Backends listed by priority, high to low:
static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
{
Backend::OCCA_CUDA, Backend::RAJA_CUDA, Backend::CUDA,
Backend::OCCA_OMP, Backend::RAJA_OMP, Backend::OMP,
Backend::OCCA_CPU, Backend::RAJA_CPU, Backend::CPU
};
// Backend names listed by priority, high to low:
static const char *backend_name[Backend::NUM_BACKENDS] =
{
"occa-cuda", "raja-cuda", "cuda", "occa-omp", "raja-omp", "omp",
"occa-cpu", "raja-cpu", "cpu"
};
} // namespace mfem::internal
void Device::Configure(const std::string &device, const int dev)
{
std::map<std::string, Backend::Id> bmap;
for (int i = 0; i < Backend::NUM_BACKENDS; i++)
{
bmap[internal::backend_name[i]] = internal::backend_list[i];
}
std::string::size_type beg = 0, end;
while (1)
{
end = device.find(',', beg);
end = (end != std::string::npos) ? end : device.size();
const std::string bname = device.substr(beg, end - beg);
std::map<std::string, Backend::Id>::iterator it = bmap.find(bname);
MFEM_VERIFY(it != bmap.end(), "invalid backend name: '" << bname << '\'');
Get().MarkBackend(it->second);
if (end == device.size()) { break; }
beg = end + 1;
}
// OCCA_CUDA needs CUDA or RAJA_CUDA:
Get().allowed_backends = Get().backends;
if (Allows(Backend::OCCA_CUDA) && !Allows(Backend::RAJA_CUDA))
{
Get().MarkBackend(Backend::CUDA);
}
// Activate all backends for Setup().
Get().allowed_backends = Get().backends;
Get().Setup(dev);
// Enable only the default host CPU backend.
Get().allowed_backends = Backend::CPU;
}
void Device::Print(std::ostream &out)
{
out << "Device configuration: ";
bool add_comma = false;
for (int i = 0; i < Backend::NUM_BACKENDS; i++)
{
if (Get().backends & internal::backend_list[i])
{
if (add_comma) { out << ','; }
add_comma = true;
out << internal::backend_name[i];
}
}
out << '\n';
}
#ifdef MFEM_USE_CUDA
static void DeviceSetup(const int dev, int &ngpu)
{
cudaGetDeviceCount(&ngpu);
MFEM_VERIFY(ngpu>0, "No CUDA device found!");
cuInit(0);
cuDeviceGet(&internal::cuDevice, dev);
cuCtxCreate(&internal::cuContext, CU_CTX_SCHED_AUTO, internal::cuDevice);
internal::cuStream = new CUstream;
MFEM_VERIFY(internal::cuStream, "CUDA stream could not be created!");
cuStreamCreate(internal::cuStream, CU_STREAM_DEFAULT);
}
#endif
static void CudaDeviceSetup(const int dev, int &ngpu)
{
#ifdef MFEM_USE_CUDA
DeviceSetup(dev, ngpu);
#endif
}
static void RajaDeviceSetup(const int dev, int &ngpu)
{
#ifdef MFEM_USE_CUDA
if (ngpu <= 0) { DeviceSetup(dev, ngpu); }
#endif
}
static void OccaDeviceSetup(CUdevice cu_dev, CUcontext cu_ctx)
{
#ifdef MFEM_USE_OCCA
const int cpu = Device::Allows(Backend::OCCA_CPU);
const int omp = Device::Allows(Backend::OCCA_OMP);
const int cuda = Device::Allows(Backend::OCCA_CUDA);
if (cpu + omp + cuda > 1)
{
MFEM_ABORT("Only one OCCA backend can be configured at a time!");
}
if (cuda)
{
#if OCCA_CUDA_ENABLED
internal::occaDevice = occa::cuda::wrapDevice(cu_dev, cu_ctx);
#else
MFEM_ABORT("the OCCA CUDA backend requires OCCA built with CUDA!");
#endif
}
else if (omp)
{
#if OCCA_OPENMP_ENABLED
internal::occaDevice.setup("mode: 'OpenMP'");
#else
MFEM_ABORT("the OCCA OpenMP backend requires OCCA built with OpenMP!");
#endif
}
else
{
internal::occaDevice.setup("mode: 'Serial'");
}
std::string mfemDir;
if (occa::io::exists(MFEM_INSTALL_DIR "/include/mfem/"))
{
mfemDir = MFEM_INSTALL_DIR "/include/mfem/";
}
else if (occa::io::exists(MFEM_SOURCE_DIR))
{
mfemDir = MFEM_SOURCE_DIR;
}
else
{
MFEM_ABORT("Cannot find OCCA kernels in MFEM_INSTALL_DIR or MFEM_SOURCE_DIR");
}
occa::io::addLibraryPath("mfem", mfemDir);
occa::loadKernels("mfem");
#else
MFEM_ABORT("the OCCA backends require MFEM built with MFEM_USE_OCCA=YES");
#endif
}
void Device::Setup(const int device)
{
MFEM_VERIFY(ngpu == -1, "the mfem::Device is already configured!");
ngpu = 0;
dev = device;
#ifndef MFEM_USE_CUDA
MFEM_VERIFY(!Allows(Backend::CUDA_MASK),
"the CUDA backends require MFEM built with MFEM_USE_CUDA=YES");
#endif
#ifndef MFEM_USE_RAJA
MFEM_VERIFY(!Allows(Backend::RAJA_MASK),
"the RAJA backends require MFEM built with MFEM_USE_RAJA=YES");
#endif
#ifndef MFEM_USE_OPENMP
MFEM_VERIFY(!Allows(Backend::OMP|Backend::RAJA_OMP),
"the OpenMP and RAJA OpenMP backends require MFEM built with"
" MFEM_USE_OPENMP=YES");
#endif
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
// We initialize CUDA and/or RAJA_CUDA first so OccaDeviceSetup() can reuse
// the same initialized cuDevice and cuContext objects when OCCA_CUDA is
// enabled.
if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
if (Allows(Backend::RAJA_CUDA)) { RajaDeviceSetup(dev, ngpu); }
if (Allows(Backend::OCCA_MASK))
{
OccaDeviceSetup(internal::cuDevice, internal::cuContext);
}
}
Device::~Device()
{
delete internal::cuStream;
}
} // mfem
-190
View File
@@ -1,190 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_DEVICE_HPP
#define MFEM_DEVICE_HPP
#include "globals.hpp"
namespace mfem
{
/// MFEM backends.
/** Individual backends will generally implement only a subset of the kernels
implemented by the default CPU backend. The goal of the backends is to
accelerate data-parallel portions of the code and they can use a device
memory space (e.g. GPUs) or share the memory space of the host (OpenMP). */
struct Backend
{
/** @brief In the documentation below, we use square brackets to indicate the
type of the backend: host or device. */
enum Id
{
/// [host] Default CPU backend: sequential execution on each MPI rank.
CPU = 1 << 0,
/// [host] OpenMP backend. Enabled when MFEM_USE_OPENMP = YES.
OMP = 1 << 1,
/// [device] CUDA backend. Enabled when MFEM_USE_CUDA = YES.
CUDA = 1 << 2,
/** @brief [host] RAJA CPU backend: sequential execution on each MPI rank.
Enabled when MFEM_USE_RAJA = YES. */
RAJA_CPU = 1 << 3,
/** @brief [host] RAJA OpenMP backend. Enabled when MFEM_USE_RAJA = YES
and MFEM_USE_OPENMP = YES. */
RAJA_OMP = 1 << 4,
/** @brief [device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES
and MFEM_USE_CUDA = YES. */
RAJA_CUDA = 1 << 5,
/** @brief [host] OCCA CPU backend: sequential execution on each MPI rank.
Enabled when MFEM_USE_OCCA = YES. */
OCCA_CPU = 1 << 6,
/// [host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
OCCA_OMP = 1 << 7,
/** @brief [device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES
and MFEM_USE_CUDA = YES. */
OCCA_CUDA = 1 << 8
};
/** @brief Additional useful constants. For example, the *_MASK constants can
be used with Device::Allows(). */
enum
{
/// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
NUM_BACKENDS = 9,
/// Biwise-OR of all CUDA backends
CUDA_MASK = CUDA | RAJA_CUDA | OCCA_CUDA,
/// Biwise-OR of all RAJA backends
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA,
/// Biwise-OR of all OCCA backends
OCCA_MASK = OCCA_CPU | OCCA_OMP | OCCA_CUDA,
/// Biwise-OR of all OpenMP backends
OMP_MASK = OMP | RAJA_OMP | OCCA_OMP,
/// Biwise-OR of all device backends
DEVICE_MASK = CUDA_MASK
};
};
/** @brief The MFEM Device class abstracts hardware devices, such as GPUs, as
well as programming models, such as CUDA, OCCA, RAJA and OpenMP. */
/** This class represents a "virtual device" with the following properties:
- There a single object of this class which is controlled by its static
methods.
- Once configured, the object cannot be re-configured during the program
lifetime.
- MFEM classes use this object to determine where (host or device) to
perform an operation and which backend implementation to use.
- Multiple backends can be configured at the same time; currently, a fixed
priority order is used to select a specific backend from the list of
configured backends. See the Backend class and the Configure() method in
this class for details.
- The device can be disabled to restrict the backend selection to only the
default host CPU backend, see the methods Enable() and Disable(). */
class Device
{
private:
enum MODES {SEQUENTIAL, ACCELERATED};
MODES mode;
int dev = 0; ///< Device ID of the configured device.
int ngpu = -1; ///< Number of detected devices; -1: not initialized.
unsigned long backends; ///< Bitwise-OR of all configured backends.
/** Bitwise-OR mask of all allowed backends. All backends are active when the
Device is enabled. When the Device is disabled, only the host CPU backend
is allowed. */
unsigned long allowed_backends;
Device()
: mode(Device::SEQUENTIAL),
backends(Backend::CPU),
allowed_backends(backends) { }
Device(Device const&);
void operator=(Device const&);
static Device& Get() { static Device singleton; return singleton; }
/// Setup switcher based on configuration settings
void Setup(const int dev = 0);
void MarkBackend(Backend::Id b) { backends |= b; }
public:
/// Configure the Device backends.
/** The string parameter @a device must be a comma-separated list of backend
string names (see below). The @a dev argument specifies the ID of the
actual devices (e.g. GPU) to use.
* The available backends are described by the Backend class.
* The string name of a backend is the lowercase version of the
Backend::Id enumeration constant with '_' replaced by '-', e.g. the
string name of 'RAJA_CPU' is 'raja-cpu'.
* The 'cpu' backend is always enabled with lowest priority.
* The current backend priority from highest to lowest is: 'occa-cuda',
'raja-cuda', 'cuda', 'occa-omp', 'raja-omp', 'omp', 'occa-cpu',
'raja-cpu', 'cpu'.
* Multiple backends can be configured at the same time.
* Only one 'occa-*' backend can be configured at a time.
* The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
is already enabled.
* After this call, the Device will be disabled. */
static void Configure(const std::string &device, const int dev = 0);
/// Print the configuration of the MFEM virtual device object.
static void Print(std::ostream &out = mfem::out);
/// Return true if Configure() has been called previously.
static inline bool IsConfigured() { return Get().ngpu >= 0; }
/// Return true if an actual device (e.g. GPU) has been configured.
static inline bool IsAvailable() { return Get().ngpu > 0; }
/// Enable the use of the configured device in the code that follows.
/** After this call MFEM classes will use the backend kernels whenever
possible, transferring data automatically to the device, if necessary.
If the only configured backend is the default host CPU one, the device
will remain disabled. */
static inline void Enable()
{
if (Get().backends & ~Backend::CPU)
{
Get().mode = Device::ACCELERATED;
Get().allowed_backends = Get().backends;
}
}
/// Disable the use of the configured device in the code that follows.
/** After this call MFEM classes will only use default CPU kernels,
transferring data automatically from the device, if necessary. */
static inline void Disable()
{
Get().mode = Device::SEQUENTIAL;
Get().allowed_backends = Backend::CPU;
}
/// Return true if the Device is enabled.
static inline bool IsEnabled() { return Get().mode == ACCELERATED; }
/// The opposite of IsEnabled().
static inline bool IsDisabled() { return !IsEnabled(); }
/** @brief Return true if any of the backends in the backend mask, @a b_mask,
are allowed. The allowed backends are all configured backends minus the
device backends when the Device is disabled. */
/** This method can be used with any of the Backend::Id constants, the
Backend::*_MASK, or combinations of those. */
static inline bool Allows(unsigned long b_mask)
{ return Get().allowed_backends & b_mask; }
~Device();
};
} // mfem
#endif // MFEM_DEVICE_HPP
-142
View File
@@ -1,142 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_FORALL_HPP
#define MFEM_FORALL_HPP
#include "../config/config.hpp"
#include "error.hpp"
#include "cuda.hpp"
#include "occa.hpp"
#include "device.hpp"
#include "mem_manager.hpp"
#include "../linalg/dtensor.hpp"
#ifdef MFEM_USE_RAJA
#include "RAJA/RAJA.hpp"
#endif
namespace mfem
{
// Implementation of MFEM's "parallel for" (forall) device/host kernel
// interfaces supporting RAJA, CUDA, OpenMP, and sequential backends.
// The MFEM_FORALL wrapper
#define MFEM_FORALL(i,N,...) \
ForallWrap(N, \
[=] MFEM_ATTR_DEVICE (int i) {__VA_ARGS__}, \
[&] (int i) {__VA_ARGS__})
/// OpenMP backend
template <typename HBODY>
void OmpWrap(const int N, HBODY &&h_body)
{
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
for (int k = 0; k < N; k++)
{
h_body(k);
}
#else
MFEM_ABORT("OpenMP requested for MFEM but OpenMP is not enabled!");
#endif
}
/// RAJA Cuda backend
template <int BLOCKS, typename DBODY>
void RajaCudaWrap(const int N, DBODY &&d_body)
{
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
RAJA::forall<RAJA::cuda_exec<BLOCKS>>(RAJA::RangeSegment(0,N),d_body);
#else
MFEM_ABORT("RAJA::Cuda requested but RAJA::Cuda is not enabled!");
#endif
}
/// RAJA OpenMP backend
template <typename HBODY>
void RajaOmpWrap(const int N, HBODY &&h_body)
{
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
RAJA::forall<RAJA::omp_parallel_for_exec>(RAJA::RangeSegment(0,N), h_body);
#else
MFEM_ABORT("RAJA::OpenMP requested but RAJA::OpenMP is not enabled!");
#endif
}
/// RAJA sequential loop backend
template <typename HBODY>
void RajaSeqWrap(const int N, HBODY &&h_body)
{
#ifdef MFEM_USE_RAJA
RAJA::forall<RAJA::loop_exec>(RAJA::RangeSegment(0,N), h_body);
#else
MFEM_ABORT("RAJA requested but RAJA is not enabled!");
#endif
}
/// CUDA backend
#ifdef MFEM_USE_CUDA
template <typename BODY> __global__ static
void CuKernel(const int N, BODY body)
{
const int k = blockDim.x*blockIdx.x + threadIdx.x;
if (k >= N) { return; }
body(k);
}
template <int BLOCKS, typename DBODY>
void CuWrap(const int N, DBODY &&d_body)
{
if (N==0) { return; }
const int GRID = (N+BLOCKS-1)/BLOCKS;
CuKernel<<<GRID,BLOCKS>>>(N,d_body);
const cudaError_t last = cudaGetLastError();
MFEM_VERIFY(last == cudaSuccess, cudaGetErrorString(last));
}
#else // MFEM_USE_CUDA
template <int BLOCKS, typename DBODY>
void CuWrap(const int N, DBODY &&d_body) {}
#endif
/// The forall kernel body wrapper
template <typename DBODY, typename HBODY>
void ForallWrap(const int N, DBODY &&d_body, HBODY &&h_body)
{
if (Device::Allows(Backend::RAJA_CUDA))
{ return RajaCudaWrap<MFEM_CUDA_BLOCKS>(N, d_body); }
if (Device::Allows(Backend::CUDA))
{ return CuWrap<MFEM_CUDA_BLOCKS>(N, d_body); }
if (Device::Allows(Backend::RAJA_OMP)) { return RajaOmpWrap(N, h_body); }
if (Device::Allows(Backend::OMP)) { return OmpWrap(N, h_body); }
if (Device::Allows(Backend::RAJA_CPU)) { return RajaSeqWrap(N, h_body); }
for (int k = 0; k < N; k++) { h_body(k); }
}
} // namespace mfem
#endif // MFEM_FORALL_HPP
-364
View File
@@ -1,364 +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 <cstring> // std::memcpy
#include <list>
#include <unordered_map>
namespace mfem
{
namespace internal
{
/// Forward declaration of the Alias structure
struct Alias;
/// Memory class that holds:
/// - a boolean telling which memory space is being used
/// - the size in bytes of this memory region,
/// - the host and the device pointer,
/// - a list of all aliases seen using this region (used only to free them).
struct Memory
{
bool host;
const std::size_t bytes;
void *const h_ptr;
void *d_ptr;
std::list<const void*> aliases;
Memory(void* const h, const std::size_t size):
host(true), bytes(size), h_ptr(h), d_ptr(nullptr), aliases() {}
};
/// Alias class that holds the base memory region and the offset
struct Alias
{
Memory *const mem;
const long offset;
};
typedef std::unordered_map<const void*, Memory> MemoryMap;
typedef std::unordered_map<const void*, const Alias*> AliasMap;
struct Ledger
{
MemoryMap memories;
AliasMap aliases;
};
} // namespace mfem::internal
static internal::Ledger *maps;
MemoryManager::MemoryManager()
{
exists = true;
enabled = true;
maps = new internal::Ledger();
}
MemoryManager::~MemoryManager()
{
delete maps;
exists = false;
}
void* MemoryManager::Insert(void *ptr, const std::size_t bytes)
{
if (!UsingMM()) { return ptr; }
const bool known = IsKnown(ptr);
if (known)
{
mfem_error("Trying to add an already present address!");
}
maps->memories.emplace(ptr, internal::Memory(ptr, bytes));
return ptr;
}
void *MemoryManager::Erase(void *ptr)
{
if (!UsingMM()) { return ptr; }
if (!ptr) { return ptr; }
const bool known = IsKnown(ptr);
if (!known)
{
mfem_error("Trying to erase an unknown pointer!");
}
internal::Memory &mem = maps->memories.at(ptr);
if (mem.d_ptr) { CuMemFree(mem.d_ptr); }
for (const void *alias : mem.aliases)
{
maps->aliases.erase(maps->aliases.find(alias));
}
mem.aliases.clear();
maps->memories.erase(maps->memories.find(ptr));
return ptr;
}
void MemoryManager::SetHostDevicePtr(void *h_ptr, void *d_ptr, const bool host)
{
internal::Memory &base = maps->memories.at(h_ptr);
base.d_ptr = d_ptr;
base.host = host;
}
bool MemoryManager::IsKnown(const void *ptr)
{
return maps->memories.find(ptr) != maps->memories.end();
}
bool MemoryManager::IsOnHost(const void *ptr)
{
return maps->memories.at(ptr).host;
}
std::size_t MemoryManager::Bytes(const void *ptr)
{
return maps->memories.at(ptr).bytes;
}
void *MemoryManager::GetDevicePtr(const void *ptr)
{
internal::Memory &base = maps->memories.at(ptr);
const size_t bytes = base.bytes;
if (!base.d_ptr)
{
CuMemAlloc(&base.d_ptr, bytes);
CuMemcpyHtoD(base.d_ptr, ptr, bytes);
base.host = false;
}
return base.d_ptr;
}
// Looks if ptr is an alias of one memory
static const void* AliasBaseMemory(const internal::Ledger *maps,
const void *ptr)
{
for (internal::MemoryMap::const_iterator mem = maps->memories.begin();
mem != maps->memories.end(); mem++)
{
const void *b_ptr = mem->first;
if (b_ptr > ptr) { continue; }
const void *end = static_cast<const char*>(b_ptr) + mem->second.bytes;
if (ptr < end) { return b_ptr; }
}
return nullptr;
}
bool MemoryManager::IsAlias(const void *ptr)
{
const internal::AliasMap::const_iterator found = maps->aliases.find(ptr);
if (found != maps->aliases.end()) { return true; }
MFEM_ASSERT(!IsKnown(ptr), "Ptr is an already known address!");
const void *base = AliasBaseMemory(maps, ptr);
if (!base) { return false; }
internal::Memory &mem = maps->memories.at(base);
const long offset = static_cast<const char*>(ptr) -
static_cast<const char*> (base);
const internal::Alias *alias = new internal::Alias{&mem, offset};
maps->aliases.emplace(ptr, alias);
mem.aliases.push_back(ptr);
return true;
}
static inline bool MmDeviceIniFilter(void)
{
if (!mm.UsingMM()) { return true; }
if (!mm.IsEnabled()) { return true; }
if (!Device::IsAvailable()) { return true; }
if (!Device::IsConfigured()) { return true; }
return false;
}
// Turn a known address into the right host or device address. Alloc, Push, or
// Pull it if necessary.
static void *PtrKnown(internal::Ledger *maps, void *ptr)
{
internal::Memory &base = maps->memories.at(ptr);
const bool ptr_on_host = base.host;
const std::size_t bytes = base.bytes;
const bool run_on_device = Device::Allows(Backend::DEVICE_MASK);
if (ptr_on_host && !run_on_device) { return ptr; }
if (bytes==0) { mfem_error("PtrKnown bytes==0"); }
if (!base.d_ptr) { CuMemAlloc(&base.d_ptr, bytes); }
if (!base.d_ptr) { mfem_error("PtrKnown !base->d_ptr"); }
if (!ptr_on_host && run_on_device) { return base.d_ptr; }
if (!ptr) { mfem_error("PtrKnown !ptr"); }
if (!ptr_on_host && !run_on_device) // Pull
{
CuMemcpyDtoH(ptr, base.d_ptr, bytes);
base.host = true;
return ptr;
}
// Push
if (!(ptr_on_host && run_on_device)) { mfem_error("PtrKnown !(host && gpu)"); }
CuMemcpyHtoD(base.d_ptr, ptr, bytes);
base.host = false;
return base.d_ptr;
}
// Turn an alias into the right host or device address. Alloc, Push, or Pull it
// if necessary.
static void *PtrAlias(internal::Ledger *maps, void *ptr)
{
const bool gpu = Device::Allows(Backend::DEVICE_MASK);
const internal::Alias *alias = maps->aliases.at(ptr);
const internal::Memory *base = alias->mem;
const bool host = base->host;
const bool device = !base->host;
const std::size_t bytes = base->bytes;
if (host && !gpu) { return ptr; }
if (bytes==0) { mfem_error("PtrAlias bytes==0"); }
if (!base->d_ptr) { CuMemAlloc(&(alias->mem->d_ptr), bytes); }
if (!base->d_ptr) { mfem_error("PtrAlias !base->d_ptr"); }
void *a_ptr = static_cast<char*>(base->d_ptr) + alias->offset;
if (device && gpu) { return a_ptr; }
if (!base->h_ptr) { mfem_error("PtrAlias !base->h_ptr"); }
if (device && !gpu) // Pull
{
CuMemcpyDtoH(base->h_ptr, base->d_ptr, bytes);
alias->mem->host = true;
return ptr;
}
// Push
if (!(host && gpu)) { mfem_error("PtrAlias !(host && gpu)"); }
CuMemcpyHtoD(base->d_ptr, base->h_ptr, bytes);
alias->mem->host = false;
return a_ptr;
}
void *MemoryManager::Ptr(void *ptr)
{
if (ptr==NULL) { return NULL; };
if (MmDeviceIniFilter()) { return ptr; }
if (IsKnown(ptr)) { return PtrKnown(maps, ptr); }
if (IsAlias(ptr)) { return PtrAlias(maps, ptr); }
if (Device::Allows(Backend::DEVICE_MASK))
{
mfem_error("Trying to use unknown pointer on the DEVICE!");
}
return ptr;
}
const void *MemoryManager::Ptr(const void *ptr)
{
return static_cast<const void*>(Ptr(const_cast<void*>(ptr)));
}
static void PushKnown(internal::Ledger *maps,
const void *ptr, const std::size_t bytes)
{
internal::Memory &base = maps->memories.at(ptr);
if (!base.d_ptr) { CuMemAlloc(&base.d_ptr, base.bytes); }
CuMemcpyHtoD(base.d_ptr, ptr, bytes == 0 ? base.bytes : bytes);
}
static void PushAlias(const internal::Ledger *maps,
const void *ptr, const std::size_t bytes)
{
const internal::Alias *alias = maps->aliases.at(ptr);
void *dst = static_cast<char*>(alias->mem->d_ptr) + alias->offset;
CuMemcpyHtoD(dst, ptr, bytes);
}
void MemoryManager::Push(const void *ptr, const std::size_t bytes)
{
if (MmDeviceIniFilter()) { return; }
if (IsKnown(ptr)) { return PushKnown(maps, ptr, bytes); }
if (IsAlias(ptr)) { return PushAlias(maps, ptr, bytes); }
if (Device::Allows(Backend::DEVICE_MASK))
{ mfem_error("Unknown pointer to push to!"); }
}
static void PullKnown(const internal::Ledger *maps,
const void *ptr, const std::size_t bytes)
{
const internal::Memory &base = maps->memories.at(ptr);
const bool host = base.host;
if (host) { return; }
CuMemcpyDtoH(base.h_ptr, base.d_ptr, bytes == 0 ? base.bytes : bytes);
}
static void PullAlias(const internal::Ledger *maps,
const void *ptr, const std::size_t bytes)
{
const internal::Alias *alias = maps->aliases.at(ptr);
const bool host = alias->mem->host;
if (host) { return; }
if (!ptr) { mfem_error("PullAlias !ptr"); }
if (!alias->mem->d_ptr) { mfem_error("PullAlias !alias->mem->d_ptr"); }
CuMemcpyDtoH(const_cast<void*>(ptr),
static_cast<char*>(alias->mem->d_ptr) + alias->offset,
bytes);
}
void MemoryManager::Pull(const void *ptr, const std::size_t bytes)
{
if (MmDeviceIniFilter()) { return; }
if (IsKnown(ptr)) { return PullKnown(maps, ptr, bytes); }
if (IsAlias(ptr)) { return PullAlias(maps, ptr, bytes); }
if (Device::Allows(Backend::DEVICE_MASK))
{ mfem_error("Unknown pointer to pull from!"); }
}
namespace internal { extern CUstream *cuStream; }
void* MemoryManager::Memcpy(void *dst, const void *src,
const std::size_t bytes, const bool async)
{
void *d_dst = Ptr(dst);
void *d_src = const_cast<void*>(Ptr(src));
if (bytes == 0) { return dst; }
const bool run_on_host = !Device::Allows(Backend::DEVICE_MASK);
if (run_on_host) { return std::memcpy(dst, src, bytes); }
if (!async) { return CuMemcpyDtoD(d_dst, d_src, bytes); }
return CuMemcpyDtoDAsync(d_dst, d_src, bytes, internal::cuStream);
}
void MemoryManager::RegisterCheck(void *ptr)
{
if (ptr != NULL && UsingMM())
{
if (!IsKnown(ptr))
{
mfem_error("Pointer is not registered!");
}
}
}
void MemoryManager::PrintPtrs(void)
{
for (const auto& n : maps->memories)
{
const internal::Memory &mem = n.second;
mfem::out << std::endl
<< "key " << n.first << ", "
<< "host " << mem.host << ", "
<< "h_ptr " << mem.h_ptr << ", "
<< "d_ptr " << mem.d_ptr;
}
}
void MemoryManager::GetAll(void)
{
for (const auto& n : maps->memories)
{
const void *ptr = n.first;
Ptr(ptr);
}
}
MemoryManager mm;
bool MemoryManager::exists = false;
} // namespace mfem
-186
View File
@@ -1,186 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_MEM_MANAGER_HPP
#define MFEM_MEM_MANAGER_HPP
#include "globals.hpp"
namespace mfem
{
// Implementation of MFEM's lightweight device/host memory manager designed to
// work seamlessly with the OCCA, RAJA, and other kernels supported by MFEM.
/// The memory manager class
class MemoryManager
{
private:
/// Allow to enable/disable the Ptr, Pull and Push functionalities
/// New and Delete will still continue to register the pointers
bool enabled;
/// Allow to detect if a global memory manager instance exists
static bool exists;
public:
MemoryManager();
~MemoryManager();
/// Adds an address in the map
void *Insert(void *ptr, const std::size_t bytes);
/// Remove the address from the map, as well as all its aliases
void *Erase(void *ptr);
/// Return true if the memory manager is used: pointers seen by mfem::New and
/// mfem::Delete will be inserted in the ledger and erased from it
static inline bool UsingMM()
{
#ifdef MFEM_USE_MM
return true;
#else
return false;
#endif
}
/// Disable the memory manager: Ptr, Push and Pull will be no-op
void Disable() { enabled = false; }
/// Enable the memory manager: Ptr, Push and Pull wont be no-op
void Enable() { enabled = true; }
/// Return true if the memory manager is used and enabled
bool IsEnabled() { return UsingMM() && enabled; }
/// The opposite of IsEnabled().
bool IsDisabled() { return !IsEnabled(); }
/// Return true if a global memory manager instance exists
static bool Exists() { return exists; }
/** @brief Translates ptr to host or device address, depending on what
backends are currently allowed by the Device class and on the ptr
state. */
void *Ptr(void *ptr);
const void *Ptr(const void *ptr);
/// Data will be pushed/pulled before the copy happens on the H or the D
void* Memcpy(void *dst, const void *src,
std::size_t bytes, const bool async = false);
/// Return the bytes of the memory region which base address is ptr
std::size_t Bytes(const void *ptr);
/// Return true if the registered pointer is on the host side
bool IsOnHost(const void *ptr);
/// Return true if the pointer has been registered
bool IsKnown(const void *ptr);
/// Return true if the pointer is an alias inside a registered memory region
bool IsAlias(const void *ptr);
/// Push the data to the device
void Push(const void *ptr, const std::size_t bytes =0);
/// Pull the data from the device
void Pull(const void *ptr, const std::size_t bytes =0);
/// Return the corresponding device pointer of ptr, allocating and moving the
/// data if needed (used in OccaPtr)
void *GetDevicePtr(const void *ptr);
/// Registers external host pointer in the memory manager which will manage
/// the corresponding device pointer, but not the provided host pointer.
template<class T>
void RegisterHostPtr(T *ptr_host, const std::size_t size)
{
Insert(ptr_host, size*sizeof(T));
#ifdef MFEM_DEBUG
RegisterCheck(ptr_host);
#endif
}
/// Registers external host and device pointers in the memory manager.
template<class T>
void RegisterHostAndDevicePtr(T *ptr_host, T *ptr_device,
const std::size_t size, const bool host)
{
RegisterHostPtr(ptr_host, size);
SetHostDevicePtr(ptr_host, ptr_device, host);
}
/// Set the host h_ptr, device d_ptr and mode host of the memory region just
/// been registered with h_ptr (see RegisterHostAndDevicePtr)
void SetHostDevicePtr(void *h_ptr, void *d_ptr, const bool host);
/// Unregisters the host pointer from the memory manager. To be used with
/// memory not allocated by the memory manager.
template<class T>
void UnregisterHostPtr(T *ptr) { Erase(ptr); }
/// Check if pointer has been registered in the memory manager
void RegisterCheck(void *ptr);
/// Prints all pointers known by the memory manager
void PrintPtrs(void);
/// Copies all memory to the current memory space
void GetAll(void);
};
/// The (single) global memory manager object
extern MemoryManager mm;
/// Main memory allocation template function. Allocates n*size bytes and returns
/// a pointer to the allocated memory.
template<class T>
inline T *New(const std::size_t n)
{
T *ptr = new T[n];
if (!MemoryManager::Exists()) { return ptr; }
return static_cast<T*>(mm.Insert(ptr, n*sizeof(T)));
}
/// Frees the memory space pointed to by ptr, which must have been returned by a
/// previous call to mfem::New.
template<class T>
inline void Delete(T *ptr)
{
static_assert(!std::is_void<T>::value, "Cannot Delete a void pointer. "
"Explicitly provide the correct type as a template parameter.");
if (!ptr) { return; }
delete [] ptr;
if (!MemoryManager::Exists()) { return; }
mm.Erase(ptr);
}
/// Return a host or device address corresponding to current memory space
template <class T>
inline T *Ptr(T *a) { return static_cast<T*>(mm.Ptr(a)); }
/// Data will be pushed/pulled before the copy happens on the host or the device
inline void* Memcpy(void *dst, const void *src,
std::size_t bytes, const bool async = false)
{ return mm.Memcpy(dst, src, bytes, async); }
/// Push the data to the device
inline void Push(const void *ptr, const std::size_t bytes = 0)
{ return mm.Push(ptr, bytes); }
/// Pull the data from the device
inline void Pull(const void *ptr, const std::size_t bytes = 0)
{ return mm.Pull(ptr, bytes); }
} // namespace mfem
#endif // MFEM_MEM_MANAGER_HPP
-61
View File
@@ -1,61 +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 "forall.hpp"
namespace mfem
{
// This variable is defined in device.cpp:
namespace internal { extern OccaDevice occaDevice; }
static OccaMemory OccaWrapMemory(const OccaDevice dev, const void *d_adrs,
const size_t bytes)
{
// This function is called when an OCCA kernel is going to be used.
#ifdef MFEM_USE_OCCA
void *adrs = const_cast<void*>(d_adrs);
#if defined(MFEM_USE_CUDA) && OCCA_CUDA_ENABLED
// If OCCA_CUDA is allowed, it will be used since it has the highest priority
if (Device::Allows(Backend::OCCA_CUDA))
{
return occa::cuda::wrapMemory(dev, adrs, bytes);
}
#endif // MFEM_USE_CUDA && OCCA_CUDA_ENABLED
// otherwise, fallback to occa::cpu address space
return occa::cpu::wrapMemory(dev, adrs, bytes);
#else // MFEM_USE_OCCA
return (void*)NULL;
#endif
}
OccaMemory OccaPtr(const void *ptr)
{
// This function is called when 'ptr' needs to be passed to an OCCA kernel.
OccaDevice dev = internal::occaDevice;
if (!mm.UsingMM()) { return OccaWrapMemory(dev, ptr, 0); }
const bool known = mm.IsKnown(ptr);
if (!known) { mfem_error("OccaPtr: Unknown address!"); }
const bool ptr_on_host = mm.IsOnHost(ptr);
const size_t bytes = mm.Bytes(ptr);
const bool run_on_host = !Device::Allows(Backend::DEVICE_MASK);
// If the priority of a host OCCA backend is higher than all device OCCA
// backends, then we will need to run-on-host even if the Device allows a
// device backend.
if (ptr_on_host && run_on_host) { return OccaWrapMemory(dev, ptr, bytes); }
if (run_on_host) { mfem_error("OccaPtr: !ptr_on_host && run_on_host"); }
void *d_ptr = mm.GetDevicePtr(ptr);
return OccaWrapMemory(dev, d_ptr, bytes);
}
OccaDevice OccaDev() { return internal::occaDevice; }
} // namespace mfem
-44
View File
@@ -1,44 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_OCCA_HPP
#define MFEM_OCCA_HPP
#include "../config/config.hpp"
#include "cuda.hpp" // for CUdevice, CUcontext
#ifdef MFEM_USE_OCCA
#include <occa.hpp>
#if defined(MFEM_USE_CUDA) && OCCA_CUDA_ENABLED
#include <occa/modes/cuda/utils.hpp>
#endif
typedef occa::device OccaDevice;
typedef occa::memory OccaMemory;
#else // MFEM_USE_OCCA
typedef void* OccaDevice;
typedef void* OccaMemory;
#endif // MFEM_USE_OCCA
namespace mfem
{
// Function called when the pointer 'a' needs to be passed to an OCCA kernel.
OccaMemory OccaPtr(const void *a);
OccaDevice OccaDev();
} // namespace mfem
#endif // MFEM_OCCA_HPP
+27 -29
View File
@@ -15,7 +15,6 @@
#include "table.hpp"
#include "error.hpp"
#include "../general/forall.hpp"
#include <iostream>
#include <iomanip>
@@ -30,8 +29,8 @@ Table::Table(const Table &table)
if (size >= 0)
{
const int nnz = table.I[size];
I = mfem::New<int>(size+1);
J = mfem::New<int>(nnz);
I = new int[size+1];
J = new int[nnz];
memcpy(I, table.I, sizeof(int)*(size+1));
memcpy(J, table.J, sizeof(int)*nnz);
}
@@ -56,8 +55,8 @@ Table::Table (int dim, int connections_per_row)
int i, j, sum = dim * connections_per_row;
size = dim;
I = mfem::New<int>(size+1);
J = mfem::New<int>(sum);
I = new int[size+1];
J = new int[sum];
I[0] = 0;
for (i = 1; i <= size; i++)
@@ -71,8 +70,8 @@ Table::Table (int nrows, int *partitioning)
{
size = nrows;
I = mfem::New<int>(size+1);
J = mfem::New<int>(size);
I = new int[size+1];
J = new int[size];
for (int i = 0; i < size; i++)
{
@@ -101,8 +100,7 @@ void Table::MakeJ()
j = I[i], I[i] = k, k += j;
}
if (J) { mfem::Delete(J); }
J = mfem::New<int>(I[size]=k);
J = new int[I[size]=k];
}
void Table::AddConnections (int r, const int *c, int nc)
@@ -149,14 +147,14 @@ void Table::SetDims(int rows, int nnz)
if (size != rows)
{
size = rows;
if (I) { mfem::Delete(I); }
I = (rows >= 0) ? (mfem::New<int>(rows+1)) : (NULL);
if (I) { delete [] I; }
I = (rows >= 0) ? (new int[rows+1]) : (NULL);
}
if (j != nnz)
{
if (J) { mfem::Delete(J); }
J = (nnz > 0) ? (mfem::New<int>(nnz)) : (NULL);
if (J) { delete [] J; }
J = (nnz > 0) ? (new int[nnz]) : (NULL);
}
if (size >= 0)
@@ -207,8 +205,8 @@ void Table::SortRows()
void Table::SetIJ(int *newI, int *newJ, int newsize)
{
mfem::Delete(I);
mfem::Delete(J);
delete [] I;
delete [] J;
I = newI;
J = newJ;
if (newsize >= 0)
@@ -250,7 +248,7 @@ void Table::Finalize()
if (sum != I[size])
{
int *NewJ = mfem::New<int>(sum);
int *NewJ = new int[sum];
for (i=0; i<size; i++)
{
@@ -265,7 +263,7 @@ void Table::Finalize()
}
I[size] = sum;
mfem::Delete(J);
delete [] J;
J = NewJ;
@@ -280,8 +278,8 @@ void Table::MakeFromList(int nrows, const Array<Connection> &list)
size = nrows;
int nnz = list.Size();
I = mfem::New<int>(size+1);
J = mfem::New<int>(nnz);
I = new int[size+1];
J = new int[nnz];
for (int i = 0, k = 0; i <= size; i++)
{
@@ -355,17 +353,17 @@ void Table::Save(std::ostream &out) const
void Table::Load(std::istream &in)
{
mfem::Delete(I);
mfem::Delete(J);
delete [] I;
delete [] J;
in >> size;
I = mfem::New<int>(size+1);
I = new int[size+1];
for (int i = 0; i <= size; i++)
{
in >> I[i];
}
int nnz = I[size];
J =mfem::New<int>(nnz);
J = new int[nnz];
for (int j = 0; j < nnz; j++)
{
in >> J[j];
@@ -374,8 +372,8 @@ void Table::Load(std::istream &in)
void Table::Clear()
{
mfem::Delete(I);
mfem::Delete(J);
delete [] I;
delete [] J;
size = -1;
I = J = NULL;
}
@@ -384,8 +382,8 @@ void Table::Copy(Table & copy) const
{
if (size >= 0)
{
int * i_copy = mfem::New<int>(size+1);
int * j_copy = mfem::New<int>(I[size]);
int * i_copy = new int[size+1];
int * j_copy = new int[I[size]];
memcpy(i_copy, I, sizeof(int)*(size+1));
memcpy(j_copy, J, sizeof(int)*I[size]);
@@ -413,8 +411,8 @@ long Table::MemoryUsage() const
Table::~Table ()
{
if (I) { mfem::Delete(I); }
if (J) { mfem::Delete(J); }
if (I) { delete [] I; }
if (J) { delete [] J; }
}
void Transpose (const Table &A, Table &At, int _ncols_A)
+2 -14
View File
@@ -94,8 +94,8 @@ const char *GetConfigStr()
#ifdef MFEM_THREAD_SAFE
"MFEM_THREAD_SAFE\n"
#endif
#ifdef MFEM_USE_LEGACY_OPENMP
"MFEM_USE_LEGACY_OPENMP\n"
#ifdef MFEM_USE_OPENMP
"MFEM_USE_OPENMP\n"
#endif
#ifdef MFEM_USE_MEMALLOC
"MFEM_USE_MEMALLOC\n"
@@ -135,18 +135,6 @@ const char *GetConfigStr()
#endif
#ifdef MFEM_USE_SIDRE
"MFEM_USE_SIDRE\n"
#endif
#ifdef MFEM_USE_PUMI
"MFEM_USE_PUMI\n"
#endif
#ifdef MFEM_USE_CUDA
"MFEM_USE_CUDA\n"
#endif
#ifdef MFEM_USE_RAJA
"MFEM_USE_RAJA\n"
#endif
#ifdef MFEM_USE_OCCA
"MFEM_USE_OCCA\n"
#endif
"MFEM_TIMER_TYPE = " EXPAND_AND_QUOTE(MFEM_TIMER_TYPE)
;
-1
View File
@@ -31,7 +31,6 @@ list(APPEND HDRS
blockvector.hpp
complex_operator.hpp
densemat.hpp
dtensor.hpp
handle.hpp
invariants.hpp
linalg.hpp
+3 -3
View File
@@ -455,9 +455,9 @@ SparseMatrix * BlockMatrix::CreateMonolithic() const
{
int nnz = NumNonZeroElems();
int * i_amono = mfem::New<int>(row_offsets[nRowBlocks]+2);
int * j_amono = mfem::New<int>(nnz);
double * data = mfem::New<double>(nnz);
int * i_amono = new int[ row_offsets[nRowBlocks]+2 ];
int * j_amono = new int[ nnz ];
double * data = new double[ nnz ];
for (int i = 0; i < row_offsets[nRowBlocks]+2; i++)
{
+1 -1
View File
@@ -85,7 +85,7 @@ public:
//! Get the i-th vector in the block
void GetBlockView(int i, Vector & blockView);
int BlockSize(int i) { return blockOffsets[i+1] - blockOffsets[i]; }
int BlockSize(int i) { return blockOffsets[i+1] - blockOffsets[i];}
//! Update method
/**
-404
View File
@@ -10,8 +10,6 @@
// Software Foundation) version 2.1 dated February 1999.
#include "complex_operator.hpp"
#include <set>
#include <map>
namespace mfem
{
@@ -41,30 +39,6 @@ ComplexOperator::~ComplexOperator()
delete v_;
}
Operator & ComplexOperator::real()
{
MFEM_ASSERT(Op_Real_, "ComplexOperator has no real part!");
return *Op_Real_;
}
Operator & ComplexOperator::imag()
{
MFEM_ASSERT(Op_Imag_, "ComplexOperator has no imaginary part!");
return *Op_Imag_;
}
const Operator & ComplexOperator::real() const
{
MFEM_ASSERT(Op_Real_, "ComplexOperator has no real part!");
return *Op_Real_;
}
const Operator & ComplexOperator::imag() const
{
MFEM_ASSERT(Op_Imag_, "ComplexOperator has no imaginary part!");
return *Op_Imag_;
}
void ComplexOperator::Mult(const Vector &x, Vector &y) const
{
double * x_data = x.GetData();
@@ -146,30 +120,6 @@ void ComplexOperator::MultTranspose(const Vector &x_r, const Vector &x_i,
}
SparseMatrix & ComplexSparseMatrix::real()
{
MFEM_ASSERT(Op_Real_, "ComplexSparseMatrix has no real part!");
return dynamic_cast<SparseMatrix &>(*Op_Real_);
}
SparseMatrix & ComplexSparseMatrix::imag()
{
MFEM_ASSERT(Op_Imag_, "ComplexSparseMatrix has no imaginary part!");
return dynamic_cast<SparseMatrix &>(*Op_Imag_);
}
const SparseMatrix & ComplexSparseMatrix::real() const
{
MFEM_ASSERT(Op_Real_, "ComplexSparseMatrix has no real part!");
return dynamic_cast<const SparseMatrix &>(*Op_Real_);
}
const SparseMatrix & ComplexSparseMatrix::imag() const
{
MFEM_ASSERT(Op_Imag_, "ComplexSparseMatrix has no imaginary part!");
return dynamic_cast<const SparseMatrix &>(*Op_Imag_);
}
SparseMatrix * ComplexSparseMatrix::GetSystemMatrix() const
{
SparseMatrix * A_r = dynamic_cast<SparseMatrix*>(Op_Real_);
@@ -234,358 +184,4 @@ SparseMatrix * ComplexSparseMatrix::GetSystemMatrix() const
return new SparseMatrix(I, J, D, this->Height(), this->Width());
}
#ifdef MFEM_USE_MPI
ComplexHypreParMatrix::ComplexHypreParMatrix(HypreParMatrix * A_Real,
HypreParMatrix * A_Imag,
bool ownReal, bool ownImag,
Convention convention)
: ComplexOperator(A_Real, A_Imag, ownReal, ownImag, convention)
{
comm_ = (A_Real) ? A_Real->GetComm() :
((A_Imag) ? A_Imag->GetComm() : MPI_COMM_WORLD);
MPI_Comm_rank(comm_, &myid_);
MPI_Comm_size(comm_, &nranks_);
}
HypreParMatrix & ComplexHypreParMatrix::real()
{
MFEM_ASSERT(Op_Real_, "ComplexHypreParMatrix has no real part!");
return dynamic_cast<HypreParMatrix &>(*Op_Real_);
}
HypreParMatrix & ComplexHypreParMatrix::imag()
{
MFEM_ASSERT(Op_Imag_, "ComplexHypreParMatrix has no imaginary part!");
return dynamic_cast<HypreParMatrix &>(*Op_Imag_);
}
const HypreParMatrix & ComplexHypreParMatrix::real() const
{
MFEM_ASSERT(Op_Real_, "ComplexHypreParMatrix has no real part!");
return dynamic_cast<const HypreParMatrix &>(*Op_Real_);
}
const HypreParMatrix & ComplexHypreParMatrix::imag() const
{
MFEM_ASSERT(Op_Imag_, "ComplexHypreParMatrix has no imaginary part!");
return dynamic_cast<const HypreParMatrix &>(*Op_Imag_);
}
HypreParMatrix * ComplexHypreParMatrix::GetSystemMatrix() const
{
HypreParMatrix * A_r = dynamic_cast<HypreParMatrix*>(Op_Real_);
HypreParMatrix * A_i = dynamic_cast<HypreParMatrix*>(Op_Imag_);
if ( A_r == NULL && A_i == NULL ) { return NULL; }
HYPRE_Int global_num_rows_r = (A_r) ? A_r->GetGlobalNumRows() : 0;
HYPRE_Int global_num_rows_i = (A_i) ? A_i->GetGlobalNumRows() : 0;
HYPRE_Int global_num_rows = std::max(global_num_rows_r, global_num_rows_i);
HYPRE_Int global_num_cols_r = (A_r) ? A_r->GetGlobalNumCols() : 0;
HYPRE_Int global_num_cols_i = (A_i) ? A_i->GetGlobalNumCols() : 0;
HYPRE_Int global_num_cols = std::max(global_num_cols_r, global_num_cols_i);
int row_starts_size = (HYPRE_AssumedPartitionCheck()) ? 2 : nranks_ + 1;
HYPRE_Int * row_starts = hypre_CTAlloc(HYPRE_Int, row_starts_size);
HYPRE_Int * col_starts = hypre_CTAlloc(HYPRE_Int, row_starts_size);
const HYPRE_Int * row_starts_z = (A_r) ? A_r->RowPart() :
((A_i) ? A_i->RowPart() : NULL);
const HYPRE_Int * col_starts_z = (A_r) ? A_r->ColPart() :
((A_i) ? A_i->ColPart() : NULL);
for (int i = 0; i < row_starts_size; i++)
{
row_starts[i] = 2 * row_starts_z[i];
col_starts[i] = 2 * col_starts_z[i];
}
SparseMatrix diag_r, diag_i, offd_r, offd_i;
HYPRE_Int * cmap_r, * cmap_i;
int nrows_r = 0, nrows_i = 0, ncols_r = 0, ncols_i = 0;
int ncols_offd_r = 0, ncols_offd_i = 0;
if (A_r)
{
A_r->GetDiag(diag_r);
A_r->GetOffd(offd_r, cmap_r);
nrows_r = diag_r.Height();
ncols_r = diag_r.Width();
ncols_offd_r = offd_r.Width();
}
if (A_i)
{
A_i->GetDiag(diag_i);
A_i->GetOffd(offd_i, cmap_i);
nrows_i = diag_i.Height();
ncols_i = diag_i.Width();
ncols_offd_i = offd_i.Width();
}
int nrows = std::max(nrows_r, nrows_i);
int ncols = std::max(ncols_r, ncols_i);
// Determine the unique set of off-diagonal columns global indices
std::set<int> cset;
for (int i=0; i<ncols_offd_r; i++)
{
cset.insert(cmap_r[i]);
}
for (int i=0; i<ncols_offd_i; i++)
{
cset.insert(cmap_i[i]);
}
int num_cols_offd = (int)cset.size();
// Exatract pointers to the various CSR arrays of the diagonal blocks
const int * diag_r_I = (A_r) ? diag_r.GetI() : NULL;
const int * diag_i_I = (A_i) ? diag_i.GetI() : NULL;
const int * diag_r_J = (A_r) ? diag_r.GetJ() : NULL;
const int * diag_i_J = (A_i) ? diag_i.GetJ() : NULL;
const double * diag_r_D = (A_r) ? diag_r.GetData() : NULL;
const double * diag_i_D = (A_i) ? diag_i.GetData() : NULL;
int diag_r_nnz = (diag_r_I) ? diag_r_I[nrows] : 0;
int diag_i_nnz = (diag_i_I) ? diag_i_I[nrows] : 0;
int diag_nnz = 2 * (diag_r_nnz + diag_i_nnz);
// Exatract pointers to the various CSR arrays of the off-diagonal blocks
const int * offd_r_I = (A_r) ? offd_r.GetI() : NULL;
const int * offd_i_I = (A_i) ? offd_i.GetI() : NULL;
const int * offd_r_J = (A_r) ? offd_r.GetJ() : NULL;
const int * offd_i_J = (A_i) ? offd_i.GetJ() : NULL;
const double * offd_r_D = (A_r) ? offd_r.GetData() : NULL;
const double * offd_i_D = (A_i) ? offd_i.GetData() : NULL;
int offd_r_nnz = (offd_r_I) ? offd_r_I[nrows] : 0;
int offd_i_nnz = (offd_i_I) ? offd_i_I[nrows] : 0;
int offd_nnz = 2 * (offd_r_nnz + offd_i_nnz);
// Allocate CSR arrays for the combined matrix
HYPRE_Int * diag_I = hypre_CTAlloc(HYPRE_Int, 2 * nrows + 1);
HYPRE_Int * diag_J = hypre_CTAlloc(HYPRE_Int, diag_nnz);
double * diag_D = hypre_CTAlloc(double, diag_nnz);
HYPRE_Int * offd_I = hypre_CTAlloc(HYPRE_Int, 2 * nrows + 1);
HYPRE_Int * offd_J = hypre_CTAlloc(HYPRE_Int, offd_nnz);
double * offd_D = hypre_CTAlloc(double, offd_nnz);
HYPRE_Int * cmap = hypre_CTAlloc(HYPRE_Int, 2 * num_cols_offd);
// Fill the CSR arrays for the diagonal portion of the matrix
const double factor = (convention_ == HERMITIAN) ? 1.0 : -1.0;
diag_I[0] = 0;
diag_I[nrows] = diag_r_nnz + diag_i_nnz;
for (int i=0; i<nrows; i++)
{
diag_I[i + 1] = ((diag_r_I)?diag_r_I[i+1]:0) +
((diag_i_I)?diag_i_I[i+1]:0);
diag_I[i + nrows + 1] = diag_I[i+1] + diag_r_nnz + diag_i_nnz;
if (diag_r_I)
{
for (int j=0; j<diag_r_I[i+1] - diag_r_I[i]; j++)
{
diag_J[diag_I[i] + j] = diag_r_J[diag_r_I[i] + j];
diag_D[diag_I[i] + j] = diag_r_D[diag_r_I[i] + j];
diag_J[diag_I[i+nrows] + j] =
diag_r_J[diag_r_I[i] + j] + ncols;
diag_D[diag_I[i+nrows] + j] =
factor * diag_r_D[diag_r_I[i] + j];
}
}
if (diag_i_I)
{
const int off_r = (diag_r_I)?(diag_r_I[i+1] - diag_r_I[i]):0;
for (int j=0; j<diag_i_I[i+1] - diag_i_I[i]; j++)
{
diag_J[diag_I[i] + off_r + j] = diag_i_J[diag_i_I[i] + j] + ncols;
diag_D[diag_I[i] + off_r + j] = -diag_i_D[diag_i_I[i] + j];
diag_J[diag_I[i+nrows] + off_r + j] = diag_i_J[diag_i_I[i] + j];
diag_D[diag_I[i+nrows] + off_r + j] =
factor * diag_i_D[diag_i_I[i] + j];
}
}
}
// Determine the mappings describing the layout of off-diagonal columns
int num_recv_procs = 0;
HYPRE_Int * offd_col_start_stop = NULL;
this->getColStartStop(A_r, A_i, num_recv_procs, offd_col_start_stop);
std::set<int>::iterator sit;
std::map<int,int> cmapa, cmapb, cinvmap;
for (sit=cset.begin(); sit!=cset.end(); sit++)
{
int col_orig = *sit;
int col_2x2 = -1;
int col_size = 0;
for (int i=0; i<num_recv_procs; i++)
{
if (offd_col_start_stop[2*i] <= col_orig &&
col_orig < offd_col_start_stop[2*i+1])
{
col_2x2 = offd_col_start_stop[2*i] + col_orig;
col_size = offd_col_start_stop[2*i+1] - offd_col_start_stop[2*i];
break;
}
}
cmapa[*sit] = col_2x2;
cmapb[*sit] = col_2x2 + col_size;
cinvmap[col_2x2] = -1;
cinvmap[col_2x2 + col_size] = -1;
}
delete [] offd_col_start_stop;
std::map<int, int>::iterator mit;
int i = 0;
for (mit=cinvmap.begin(); mit!=cinvmap.end(); mit++, i++)
{
mit->second = i;
cmap[i] = mit->first;
}
// Fill the CSR arrays for the off-diagonal portion of the matrix
offd_I[0] = 0;
offd_I[nrows] = offd_r_nnz + offd_i_nnz;
for (int i=0; i<nrows; i++)
{
offd_I[i + 1] = ((offd_r_I)?offd_r_I[i+1]:0) +
((offd_i_I)?offd_i_I[i+1]:0);
offd_I[i + nrows + 1] = offd_I[i+1] + offd_r_nnz + offd_i_nnz;
if (offd_r_I)
{
const int off_i = (offd_i_I)?(offd_i_I[i+1] - offd_i_I[i]):0;
for (int j=0; j<offd_r_I[i+1] - offd_r_I[i]; j++)
{
offd_J[offd_I[i] + j] =
cinvmap[cmapa[cmap_r[offd_r_J[offd_r_I[i] + j]]]];
offd_D[offd_I[i] + j] = offd_r_D[offd_r_I[i] + j];
offd_J[offd_I[i+nrows] + off_i + j] =
cinvmap[cmapb[cmap_r[offd_r_J[offd_r_I[i] + j]]]];
offd_D[offd_I[i+nrows] + off_i + j] =
factor * offd_r_D[offd_r_I[i] + j];
}
}
if (offd_i_I)
{
const int off_r = (offd_r_I)?(offd_r_I[i+1] - offd_r_I[i]):0;
for (int j=0; j<offd_i_I[i+1] - offd_i_I[i]; j++)
{
offd_J[offd_I[i] + off_r + j] =
cinvmap[cmapb[cmap_i[offd_i_J[offd_i_I[i] + j]]]];
offd_D[offd_I[i] + off_r + j] = -offd_i_D[offd_i_I[i] + j];
offd_J[offd_I[i+nrows] + j] =
cinvmap[cmapa[cmap_i[offd_i_J[offd_i_I[i] + j]]]];
offd_D[offd_I[i+nrows] + j] = factor * offd_i_D[offd_i_I[i] + j];
}
}
}
// Construct the combined matrix
HypreParMatrix * A = new HypreParMatrix(comm_,
2 * global_num_rows,
2 * global_num_cols,
row_starts, col_starts,
diag_I, diag_J, diag_D,
offd_I, offd_J, offd_D,
2 * num_cols_offd, cmap);
// Give the new matrix ownership of its interanl arrays
A->SetOwnerFlags(-1,-1,-1);
hypre_CSRMatrixSetDataOwner(((hypre_ParCSRMatrix*)(*A))->diag,1);
hypre_CSRMatrixSetDataOwner(((hypre_ParCSRMatrix*)(*A))->offd,1);
hypre_ParCSRMatrixSetRowStartsOwner((hypre_ParCSRMatrix*)(*A),1);
hypre_ParCSRMatrixSetColStartsOwner((hypre_ParCSRMatrix*)(*A),1);
return A;
}
void
ComplexHypreParMatrix::getColStartStop(const HypreParMatrix * A_r,
const HypreParMatrix * A_i,
int & num_recv_procs,
HYPRE_Int *& offd_col_start_stop) const
{
hypre_ParCSRCommPkg * comm_pkg_r =
(A_r) ? hypre_ParCSRMatrixCommPkg((hypre_ParCSRMatrix*)(*A_r)) : NULL;
hypre_ParCSRCommPkg * comm_pkg_i =
(A_i) ? hypre_ParCSRMatrixCommPkg((hypre_ParCSRMatrix*)(*A_i)) : NULL;
std::set<HYPRE_Int> send_procs, recv_procs;
if ( comm_pkg_r )
{
for (HYPRE_Int i=0; i<comm_pkg_r->num_sends; i++)
{
send_procs.insert(comm_pkg_r->send_procs[i]);
}
for (HYPRE_Int i=0; i<comm_pkg_r->num_recvs; i++)
{
recv_procs.insert(comm_pkg_r->recv_procs[i]);
}
}
if ( comm_pkg_i )
{
for (HYPRE_Int i=0; i<comm_pkg_i->num_sends; i++)
{
send_procs.insert(comm_pkg_i->send_procs[i]);
}
for (HYPRE_Int i=0; i<comm_pkg_i->num_recvs; i++)
{
recv_procs.insert(comm_pkg_i->recv_procs[i]);
}
}
num_recv_procs = (int)recv_procs.size();
HYPRE_Int loc_start_stop[2];
offd_col_start_stop = new HYPRE_Int[2 * num_recv_procs];
const HYPRE_Int * row_part = (A_r) ? A_r->RowPart() :
((A_i) ? A_i->RowPart() : NULL);
int row_part_ind = (HYPRE_AssumedPartitionCheck()) ? 0 : myid_;
loc_start_stop[0] = row_part[row_part_ind];
loc_start_stop[1] = row_part[row_part_ind+1];
MPI_Request * req = new MPI_Request[send_procs.size()+recv_procs.size()];
MPI_Status * stat = new MPI_Status[send_procs.size()+recv_procs.size()];
int send_count = 0;
int recv_count = 0;
int tag = 0;
std::set<HYPRE_Int>::iterator sit;
for (sit=send_procs.begin(); sit!=send_procs.end(); sit++)
{
MPI_Isend(loc_start_stop, 2, HYPRE_MPI_INT,
*sit, tag, comm_, &req[send_count]);
send_count++;
}
for (sit=recv_procs.begin(); sit!=recv_procs.end(); sit++)
{
MPI_Irecv(&offd_col_start_stop[2*recv_count], 2, HYPRE_MPI_INT,
*sit, tag, comm_, &req[send_count+recv_count]);
recv_count++;
}
MPI_Waitall(send_count+recv_count, req, stat);
delete [] req;
delete [] stat;
}
#endif // MFEM_USE_MPI
}
+2 -94
View File
@@ -14,9 +14,6 @@
#include "operator.hpp"
#include "sparsemat.hpp"
#ifdef MFEM_USE_MPI
#include "hypre.hpp"
#endif
namespace mfem
{
@@ -30,8 +27,7 @@ namespace mfem
ComplexOperator allows one to choose a convention upon construction, which
facilitates symmetry.
If we let (y_r + i y_i) = (Op_r + i Op_i)(x_r + i x_i) then Matrix-vector
products are then computed as:
Matrix-vector products are then computed as:
1. When Convention::HERMITIAN is used (default)
/ y_r \ / Op_r -Op_i \ / x_r \
@@ -42,8 +38,6 @@ namespace mfem
/ y_r \ / Op_r -Op_i \ / x_r \
| | = | | | |
\-y_i / \-Op_i -Op_r / \ x_i /
In other words, Matrix-vector products with Convention::BLOCK_SYMMETRIC
compute the complex conjugate of Op*x.
Either convention can be used with a given complex operator,
however, each of them is best suited for certain classes of
@@ -88,30 +82,9 @@ public:
virtual ~ComplexOperator();
/** @brief Check for existence of real or imaginary part of the operator
These methods do not check that the operators are non-zero but
only that the operators have been set.
*/
bool hasRealPart() const { return Op_Real_ != NULL; }
bool hasImagPart() const { return Op_Imag_ != NULL; }
/** @brief Real or imaginary part accessor methods
The following accessor methods should only be called if the
requested part of the opertor is known to exist. This
can be checked with hasRealPart() or hasImagPart().
*/
virtual Operator & real();
virtual Operator & imag();
virtual const Operator & real() const;
virtual const Operator & imag() const;
virtual void Mult(const Vector &x, Vector &y) const;
virtual void MultTranspose(const Vector &x, Vector &y) const;
virtual Type GetType() const { return Complex_Operator; }
protected:
// Let this be hidden from the public interface since the implementation
// depends on internal members
@@ -154,74 +127,9 @@ public:
: ComplexOperator(A_Real, A_Imag, ownReal, ownImag, convention)
{}
virtual SparseMatrix & real();
virtual SparseMatrix & imag();
virtual const SparseMatrix & real() const;
virtual const SparseMatrix & imag() const;
/** Combine the blocks making up this complex operator into a
single SparseMatrix. The resulting matrix can be passed to
solvers which require access to the matrix entries themselves,
such as sparse direct solvers, rather than simply the action of
the opertor. Note that this combined operator requires roughly
twice the memory of the block structured operator. */
SparseMatrix * GetSystemMatrix() const;
virtual Type GetType() const { return MFEM_ComplexSparseMat; }
};
#ifdef MFEM_USE_MPI
/** @brief Specialization of the ComplexOperator built from a pair of
HypreParMatrices.
The purpose of this specialization is to construct a single
HypreParMatrix object which is equivalent to the 2x2 block system
that the ComplexOperator mimics. The resulting HypreParMatrix can
then be passed along to solvers which require access to the CSR
matrix data such as SuperLU, STRUMPACK, or similar sparse linear
solvers.
See ComplexOperator documentation in operator.hpp for more information.
*/
class ComplexHypreParMatrix : public ComplexOperator
{
public:
ComplexHypreParMatrix(HypreParMatrix * A_Real, HypreParMatrix * A_Imag,
bool ownReal, bool ownImag,
Convention convention = HERMITIAN);
virtual HypreParMatrix & real();
virtual HypreParMatrix & imag();
virtual const HypreParMatrix & real() const;
virtual const HypreParMatrix & imag() const;
/** Combine the blocks making up this complex operator into a
single HypreParMatrix. The resulting matrix can be passed to
solvers which require access to the matrix entries themselves,
such as sparse direct solvers or Hypre preconditioners, rather
than simply the action of the opertor. Note that this combined
operator requires roughly twice the memory of the block
structured operator. */
HypreParMatrix * GetSystemMatrix() const;
virtual Type GetType() const { return Complex_Hypre_ParCSR; }
private:
void getColStartStop(const HypreParMatrix * A_r,
const HypreParMatrix * A_i,
int & num_recv_procs,
HYPRE_Int *& offd_col_start_stop) const;
MPI_Comm comm_;
int myid_;
int nranks_;
};
#endif // MFEM_USE_MPI
}
#endif // MFEM_COMPLEX_OPERATOR
#endif
+117 -156
View File
@@ -16,8 +16,6 @@
#include "vector.hpp"
#include "matrix.hpp"
#include "densemat.hpp"
#include "dtensor.hpp"
#include "../general/forall.hpp"
#include "../general/table.hpp"
#include "../general/globals.hpp"
@@ -78,9 +76,9 @@ DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
if (hw > 0)
{
MFEM_ASSERT(m.data, "invalid source matrix");
data = mfem::New<double>(hw);
data = new double[hw];
capacity = hw;
mfem::Memcpy(data, m.data, sizeof(double)*hw);
std::memcpy(data, m.data, sizeof(double)*hw);
}
else
{
@@ -89,20 +87,13 @@ DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
}
}
static void Set(const double d, const int size, double *data)
{
DeviceVector d_data(data);
MFEM_FORALL(i, size, d_data[i] = d;);
}
DenseMatrix::DenseMatrix(int s) : Matrix(s)
{
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
capacity = s*s;
if (capacity > 0)
{
data = mfem::New<double>(capacity);
mfem::Set(0.0, capacity, data);
data = new double[capacity](); // init with zeroes
}
else
{
@@ -117,8 +108,7 @@ DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
capacity = m*n;
if (capacity > 0)
{
data = mfem::New<double>(capacity);
mfem::Set(0.0, capacity, data);
data = new double[capacity](); // init with zeroes
}
else
{
@@ -126,28 +116,19 @@ DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
}
}
static void Transpose(const int height, const int width,
double *data, const double *mdata)
{
DeviceVector d_data(data);
const DeviceVector d_mdata(mdata);
MFEM_FORALL(i, height,
{
for (int j = 0; j < width; j++)
{
d_data[i+j*height] = d_mdata[j+i*height];
}
});
}
DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
: Matrix(mat.width, mat.height)
{
capacity = height*width;
if (capacity > 0)
{
data = mfem::New<double>(capacity);
mfem::Transpose(height, width, data, mat.Data());
data = new double[capacity];
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++)
{
(*this)(i,j) = mat(j,i);
}
}
else
{
@@ -155,13 +136,6 @@ DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
}
}
DenseMatrix::DenseMatrix(double *d, int h, int w)
: Matrix(h, w)
{
data = d;
capacity = -h*w;
}
void DenseMatrix::SetSize(int h, int w)
{
MFEM_ASSERT(h >= 0 && w >= 0,
@@ -177,11 +151,10 @@ void DenseMatrix::SetSize(int h, int w)
{
if (capacity > 0)
{
mfem::Delete(data);
delete [] data;
}
capacity = hw;
data = mfem::New<double>(capacity);
mfem::Set(0.0, capacity, data);
data = new double[hw](); // init with zeroes
}
}
@@ -195,31 +168,32 @@ const double &DenseMatrix::Elem(int i, int j) const
return (*this)(i,j);
}
static void Mult(const int height, const int width,
const double *data, const double *x, double *y)
{
const DeviceVector d_data(data);
const DeviceVector d_x(x);
DeviceVector d_y(y);
MFEM_FORALL(i, height,
{
double sum = 0.0;
for (int j = 0; j < width; j++)
{
sum += d_x[j]*d_data[i+j*height];
}
d_y[i] = sum;
});
}
void DenseMatrix::Mult(const double *x, double *y) const
{
if (width == 0)
{
mfem::Set(0.0, height, y);
for (int row = 0; row < height; row++)
{
y[row] = 0.0;
}
return;
}
mfem::Mult(height, width, data, x, y);
double *d_col = data;
double x_col = x[0];
for (int row = 0; row < height; row++)
{
y[row] = x_col*d_col[row];
}
d_col += height;
for (int col = 1; col < width; col++)
{
x_col = x[col];
for (int row = 0; row < height; row++)
{
y[row] += x_col*d_col[row];
}
d_col += height;
}
}
void DenseMatrix::Mult(const Vector &x, Vector &y) const
@@ -590,8 +564,11 @@ void DenseMatrix::Add(const double c, const DenseMatrix &A)
DenseMatrix &DenseMatrix::operator=(double c)
{
const int s = Height()*Width();
mfem::Set(c, s, data);
int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
@@ -610,9 +587,11 @@ DenseMatrix &DenseMatrix::operator=(const DenseMatrix &m)
SetSize(m.height, m.width);
const int hw = height * width;
const DeviceVector d_m(m.GetData());
DeviceVector d_data(data);
MFEM_FORALL(i, hw, d_data[i] = d_m[i];);
for (int i = 0; i < hw; i++)
{
data[i] = m.data[i];
}
return *this;
}
@@ -2463,11 +2442,16 @@ void DenseMatrix::GetRowSums(Vector &l) const
void DenseMatrix::Diag(double c, int n)
{
SetSize(n);
const int N = n*n;
DeviceVector d_data(data);
MFEM_FORALL(i, N, d_data[i] = 0.0;);
MFEM_FORALL(i, n, d_data[i*(n+1)] = c;);
int i, N = n*n;
for (i = 0; i < N; i++)
{
data[i] = 0.0;
}
for (i = 0; i < n; i++)
{
data[i*(n+1)] = c;
}
}
void DenseMatrix::Diag(double *diag, int n)
@@ -2610,6 +2594,7 @@ void DenseMatrix::GradToCurl(DenseMatrix &curl)
void DenseMatrix::GradToDiv(Vector &div)
{
#ifdef MFEM_DEBUG
if (Width()*Height() != div.Size())
{
@@ -2619,11 +2604,13 @@ void DenseMatrix::GradToDiv(Vector &div)
// div(dof*j+i) <-- (*this)(i,j)
const int n = height * width;
int n = height * width;
double *ddata = div.GetData();
const DeviceVector d_data(data);
DeviceVector d_ddata(ddata);
MFEM_FORALL(i, n, d_ddata[i] = d_data[i];);
for (int i = 0; i < n; i++)
{
ddata[i] = data[i];
}
}
void DenseMatrix::CopyRows(const DenseMatrix &A, int row1, int row2)
@@ -3017,7 +3004,7 @@ DenseMatrix::~DenseMatrix()
{
if (capacity > 0)
{
mfem::Delete(data);
delete [] data;
}
}
@@ -3074,20 +3061,20 @@ void Mult(const DenseMatrix &b, const DenseMatrix &c, DenseMatrix &a)
double *ad = a.Data();
const double *bd = b.Data();
const double *cd = c.Data();
const DeviceVector d_bd(bd);
const DeviceVector d_cd(cd);
DeviceVector d_ad(ad);
MFEM_FORALL(i, ah*aw, d_ad[i] = 0.0;);
MFEM_FORALL(j, aw,
for (int i = 0; i < ah*aw; i++)
{
ad[i] = 0.0;
}
for (int j = 0; j < aw; j++)
{
for (int k = 0; k < bw; k++)
{
for (int i = 0; i < ah; i++)
{
d_ad[i+j*ah] += d_bd[i+k*ah] * d_cd[k+j*bw];
ad[i+j*ah] += bd[i+k*ah] * cd[k+j*bw];
}
}
});
}
#endif
}
@@ -3381,22 +3368,16 @@ void CalcOrtho(const DenseMatrix &J, Vector &n)
void MultAAt(const DenseMatrix &a, DenseMatrix &aat)
{
const int height = a.Height();
const int width = a.Width();
const DeviceVector d_a(a.GetData());
DeviceVector d_aat(aat.GetData());
MFEM_FORALL(i, height,
{
for (int j=0; j<=i; j++)
for (int i = 0; i < a.Height(); i++)
for (int j = 0; j <= i; j++)
{
double temp = 0.0;
for (int k=0; k<width; k++)
double temp = 0.;
for (int k = 0; k < a.Width(); k++)
{
temp += d_a[i+k*height] * d_a[j+k*height];
temp += a(i,k) * a(j,k);
}
d_aat[j+i*height] = d_aat[i+j*height] = temp;
aat(j,i) = aat(i,j) = temp;
}
});
}
void AddMultADAt(const DenseMatrix &A, const Vector &D, DenseMatrix &ADAt)
@@ -3922,20 +3903,18 @@ void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt)
mfem_error("AddMult_a_VVt(...)");
}
#endif
const int height = VVt.Height();
const DeviceVector d_v(v.GetData());
DeviceVector d_VVt(VVt.GetData());
MFEM_FORALL(i, n,
for (int i = 0; i < n; i++)
{
double avi = a * d_v[i];
double avi = a * v(i);
for (int j = 0; j < i; j++)
{
double avivj = avi * d_v[j];
d_VVt[i+j*height] += avivj;
d_VVt[j+i*height] += avivj;
double avivj = avi * v(j);
VVt(i, j) += avivj;
VVt(j, i) += avivj;
}
d_VVt[i+i*height] += avi * d_v[i];
});
VVt(i, i) += avi * v(i);
}
}
@@ -3947,49 +3926,47 @@ void LUFactors::Factor(int m)
MFEM_VERIFY(!info, "LAPACK: error in DGETRF");
#else
// compiling without LAPACK
DeviceArray d_ipiv(ipiv);
DeviceVector d_data(data);
MFEM_FORALL(i, m,
double *data = this->data;
for (int i = 0; i < m; i++)
{
// pivoting
{
int piv = i;
double a = fabs(d_data[piv+i*m]);
double a = std::abs(data[piv+i*m]);
for (int j = i+1; j < m; j++)
{
const double b = fabs(d_data[j+i*m]);
const double b = std::abs(data[j+i*m]);
if (b > a)
{
a = b;
piv = j;
}
}
d_ipiv[i] = piv;
ipiv[i] = piv;
if (piv != i)
{
// swap rows i and piv in both L and U parts
for (int j = 0; j < m; j++)
{
const double tmp = d_data[i+j*m];
d_data[i+j*m] = d_data[piv+j*m];
d_data[piv+j*m] = tmp;
Swap<double>(data[i+j*m], data[piv+j*m]);
}
}
}
const double a_ii_inv = 1.0/d_data[i+i*m];
MFEM_ASSERT(data[i+i*m] != 0.0, "division by zero");
const double a_ii_inv = 1.0/data[i+i*m];
for (int j = i+1; j < m; j++)
{
d_data[j+i*m] *= a_ii_inv;
data[j+i*m] *= a_ii_inv;
}
for (int k = i+1; k < m; k++)
{
const double a_ik = d_data[i+k*m];
const double a_ik = data[i+k*m];
for (int j = i+1; j < m; j++)
{
d_data[j+k*m] -= a_ik * d_data[j+i*m];
data[j+k*m] -= a_ik * data[j+i*m];
}
}
});
}
#endif
}
@@ -4051,29 +4028,24 @@ void LUFactors::LSolve(int m, int n, double *X) const
const double *data = this->data;
const int *ipiv = this->ipiv;
double *x = X;
const DeviceVector d_data(data);
const DeviceArray d_ipiv(ipiv);
DeviceVector d_x(x);
MFEM_FORALL(k, n,
for (int k = 0; k < n; k++)
{
double *d_mx = &d_x[k*m];
// X <- P X
for (int i = 0; i < m; i++)
{
const double tmp = d_mx[i];
d_mx[i] = d_mx[d_ipiv[i]];
d_mx[d_ipiv[i]] = tmp;
Swap<double>(x[i], x[ipiv[i]-ipiv_base]);
}
// X <- L^{-1} X
for (int j = 0; j < m; j++)
{
const double d_mx_j = d_mx[j];
const double x_j = x[j];
for (int i = j+1; i < m; i++)
{
d_mx[i] -= d_data[i+j*m] * d_mx_j;
x[i] -= data[i+j*m] * x_j;
}
}
});
x += m;
}
}
void LUFactors::USolve(int m, int n, double *X) const
@@ -4081,20 +4053,18 @@ void LUFactors::USolve(int m, int n, double *X) const
const double *data = this->data;
double *x = X;
// X <- U^{-1} X
const DeviceVector d_data(data);
DeviceVector d_x(x);
MFEM_FORALL(k, n,
for (int k = 0; k < n; k++)
{
double *d_mx = &d_x[k*m];
for (int j = m-1; j >= 0; j--)
{
const double x_j = ( d_mx[j] /= d_data[j+j*m] );
const double x_j = ( x[j] /= data[j+j*m] );
for (int i = 0; i < j; i++)
{
d_mx[i] -= d_data[i+j*m] * x_j;
x[i] -= data[i+j*m] * x_j;
}
}
});
x += m;
}
}
void LUFactors::Solve(int m, int n, double *X) const
@@ -4244,8 +4214,8 @@ DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix &mat)
{
MFEM_ASSERT(height == width, "not a square matrix");
a = &mat;
lu.data = mfem::New<double>(width*width);
lu.ipiv = mfem::New<int>(width);
lu.data = new double[width*width];
lu.ipiv = new int[width];
Factor();
}
@@ -4254,40 +4224,31 @@ DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix *mat)
{
MFEM_ASSERT(height == width, "not a square matrix");
a = mat;
lu.data = mfem::New<double>(width*width);
lu.ipiv = mfem::New<int>(width);
lu.data = new double[width*width];
lu.ipiv = new int[width];
}
void DenseMatrixInverse::Factor()
{
MFEM_ASSERT(a, "DenseMatrix is not given");
const double *adata = a->data;
const int N = width*width;
const DeviceVector d_data(adata);
DeviceVector d_ludata(lu.data);
MFEM_FORALL(i, N,
for (int i = 0, s = width*width; i < s; i++)
{
d_ludata[i] = d_data[i];
});
lu.data[i] = adata[i];
}
lu.Factor(width);
}
void DenseMatrixInverse::GetInverseMatrix(DenseMatrix &Ainv) const
{
Ainv.SetSize(width);
lu.GetInverseMatrix(width, Ainv.Data());
}
void DenseMatrixInverse::Factor(const DenseMatrix &mat)
{
MFEM_VERIFY(mat.height == mat.width, "DenseMatrix is not square!");
if (width != mat.width)
{
height = width = mat.width;
mfem::Delete(lu.data);
lu.data = mfem::New<double>(width*width);
mfem::Delete(lu.ipiv);
lu.ipiv = mfem::New<int>(width);
delete [] lu.data;
lu.data = new double[width*width];
delete [] lu.ipiv;
lu.ipiv = new int[width];
}
a = &mat;
Factor();
@@ -4325,8 +4286,8 @@ void DenseMatrixInverse::TestInversion()
DenseMatrixInverse::~DenseMatrixInverse()
{
mfem::Delete(lu.data);
mfem::Delete(lu.ipiv);
delete [] lu.data;
delete [] lu.ipiv;
}
+16 -16
View File
@@ -56,7 +56,8 @@ public:
/** Construct a DenseMatrix using existing data array. The DenseMatrix does
not assume ownership of the data array, i.e. it will not delete the
array. */
DenseMatrix(double *d, int h, int w);
DenseMatrix(double *d, int h, int w) : Matrix(h, w)
{ data = d; capacity = -h*w; }
/// Change the data array and the size of the DenseMatrix.
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
@@ -70,7 +71,7 @@ public:
not delete the new array @a d. This method will delete the current data
array, if owned. */
void Reset(double *d, int h, int w)
{ if (OwnsData()) { mfem::Delete(data); } UseExternalData(d, h, w); }
{ if (OwnsData()) { delete [] data; } UseExternalData(d, h, w); }
/** Clear the data array and the dimensions of the DenseMatrix. This method
should not be used with DenseMatrix that owns its current data array. */
@@ -78,7 +79,7 @@ public:
/// Delete the matrix data array (if owned) and reset the matrix state.
void Clear()
{ if (OwnsData()) { mfem::Delete(data); } ClearExternalData(); }
{ if (OwnsData()) { delete [] data; } ClearExternalData(); }
/// For backward compatibility define Size to be synonym of Width()
int Size() const { return Width(); }
@@ -579,11 +580,12 @@ public:
/// Multiply the inverse matrix by another matrix: X = A^{-1} B.
void Mult(const DenseMatrix &B, DenseMatrix &X) const;
/// Multiply the inverse matrix by another matrix: X <- A^{-1} X.
void Mult(DenseMatrix &X) const { lu.Solve(width, X.Width(), X.Data()); }
/// Compute and return the inverse matrix in Ainv.
void GetInverseMatrix(DenseMatrix &Ainv) const;
void GetInverseMatrix(DenseMatrix &Ainv) const
{
Ainv.SetSize(width);
lu.GetInverseMatrix(width, Ainv.Data());
}
/// Compute the determinant of the original DenseMatrix using the LU factors.
double Det() const { return lu.Det(width); }
@@ -672,7 +674,7 @@ public:
: Mk(NULL, i, j)
{
nk = k;
tdata = mfem::New<double>(i*j*k);
tdata = new double[i*j*k];
own_data = true;
}
@@ -683,8 +685,8 @@ public:
const int size = Mk.Height()*Mk.Width()*nk;
if (size > 0)
{
tdata = mfem::New<double>(size);
mfem::Memcpy(tdata, other.tdata, sizeof(double) * size);
tdata = new double[size];
std::memcpy(tdata, other.tdata, sizeof(double) * size);
}
else
{
@@ -698,16 +700,16 @@ public:
void SetSize(int i, int j, int k)
{
if (own_data) { mfem::Delete(tdata); }
if (own_data) { delete [] tdata; }
Mk.UseExternalData(NULL, i, j);
nk = k;
tdata = mfem::New<double>(i*j*k);
tdata = new double[i*j*k];
own_data = true;
}
void UseExternalData(double *ext_data, int i, int j, int k)
{
if (own_data) { mfem::Delete(tdata); }
if (own_data) { delete [] tdata; }
Mk.UseExternalData(NULL, i, j);
nk = k;
tdata = ext_data;
@@ -730,8 +732,6 @@ public:
double *Data() { return tdata; }
const double *Data() const { return tdata; }
/** Matrix-vector product from unassembled element matrices, assuming both
'x' and 'y' use the same elem_dof table. */
void AddMult(const Table &elem_dof, const Vector &x, Vector &y) const;
@@ -743,7 +743,7 @@ public:
~DenseTensor()
{
if (own_data) { mfem::Delete(tdata); }
if (own_data) { delete [] tdata; }
}
};
-155
View File
@@ -1,155 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_DTENSOR
#define MFEM_DTENSOR
#include "../general/cuda.hpp"
#include "../general/mem_manager.hpp"
namespace mfem
{
/// A Class to compute the real index from the multi-indices of a tensor
template <int N, int Dim, typename T, typename... Args>
class TensorInd
{
public:
MFEM_ATTR_HOST_DEVICE
static inline int result(const int* sizes, T first, Args... args)
{
#ifndef MFEM_USE_CUDA
MFEM_ASSERT(first<sizes[N-1],"Trying to access out of boundary.");
#endif
return first + sizes[N - 1] * TensorInd < N + 1, Dim, Args... >
::result(sizes, args...);
}
};
// Terminal case
template <int Dim, typename T, typename... Args>
class TensorInd<Dim, Dim, T, Args...>
{
public:
MFEM_ATTR_HOST_DEVICE
static inline int result(const int* sizes, T first, Args... args)
{
#ifndef MFEM_USE_CUDA
MFEM_ASSERT(first<sizes[Dim-1],"Trying to access out of boundary.");
#endif
return first;
}
};
/// A class to initialize the size of a Tensor
template <int N, int Dim, typename T, typename... Args>
class Init
{
public:
static inline int result(int* sizes, T first, Args... args)
{
sizes[N - 1] = first;
return first * Init < N + 1, Dim, Args... >::result(sizes, args...);
}
};
// Terminal case
template <int Dim, typename T, typename... Args>
class Init<Dim, Dim, T, Args...>
{
public:
static inline int result(int* sizes, T first, Args... args)
{
sizes[Dim - 1] = first;
return first;
}
};
/// A basic generic Tensor class, appropriate for use on the GPU
template<int Dim, typename Scalar = double>
class DeviceTensor
{
protected:
int capacity;
Scalar *data;
int sizes[Dim];
public:
/// Default constructor
DeviceTensor() = delete;
/// Constructor to initialize a tensor from the Scalar array _data
template <typename... Args>
DeviceTensor(Scalar* _data, Args... args)
{
static_assert(sizeof...(args) == Dim, "Wrong number of arguments");
// Initialize sizes, and compute the number of values
const long int nb = Init<1, Dim, Args...>::result(sizes, args...);
capacity = nb;
data = (capacity > 0) ? mfem::Ptr(_data) : NULL;
}
/// Constructor to initialize a tensor from the Scalar array _data
DeviceTensor(const Scalar* _data)
{
data = (Scalar*) mfem::Ptr(_data);
}
/// Constructor to initialize a tensor from the Scalar array _data
DeviceTensor(Scalar* _data) { data = mfem::Ptr(_data); }
/// Constructor to initialize a tensor from the const Scalar array _data
template <typename... Args>
DeviceTensor(const Scalar* _data, Args... args)
{
static_assert(sizeof...(args) == Dim, "Wrong number of arguments");
// Initialize sizes, and compute the number of values
const long int nb = Init<1, Dim, Args...>::result(sizes, args...);
capacity = nb;
data = (capacity > 0) ? (Scalar*)mfem::Ptr(_data) : NULL;
}
/// Copy constructor
MFEM_ATTR_HOST_DEVICE DeviceTensor(const DeviceTensor& t)
{
for (int i = 0; i < Dim; ++i)
{
sizes[i] = t.sizes[i];
}
data = t.data;
}
/// Conversion to `Scalar *`.
inline operator Scalar *() const { return data; }
/// Const accessor for the data
template <typename... Args> MFEM_ATTR_HOST_DEVICE inline
Scalar& operator()(Args... args) const
{
static_assert(sizeof...(args) == Dim, "Wrong number of arguments");
return data[ TensorInd<1, Dim, Args...>::result(sizes, args...) ];
}
MFEM_ATTR_HOST_DEVICE inline Scalar& operator[](int i) const
{
return data[i];
}
};
typedef DeviceTensor<1,int> DeviceArray;
typedef DeviceTensor<1,double> DeviceVector;
typedef DeviceTensor<2,double> DeviceMatrix;
} // mfem namespace
#endif // MFEM_DTENSOR
+2 -3
View File
@@ -78,8 +78,7 @@ void OperatorHandle::MakeSquareBlockDiag(MPI_Comm comm, HYPRE_Int glob_size,
case Operator::PETSC_MATAIJ:
case Operator::PETSC_MATIS:
// Assuming that PetscInt is the same size as HYPRE_Int, checked above.
oper = new PetscParMatrix(comm, glob_size, (PetscInt*)row_starts, diag,
type_id);
oper = new PetscParMatrix(comm, glob_size, row_starts, diag, type_id);
break;
#endif
default: MFEM_ABORT(not_supported_msg << type_id);
@@ -111,7 +110,7 @@ MakeRectangularBlockDiag(MPI_Comm comm, HYPRE_Int glob_num_rows,
case Operator::PETSC_MATIS:
// Assuming that PetscInt is the same size as HYPRE_Int, checked above.
oper = new PetscParMatrix(comm, glob_num_rows, glob_num_cols,
(PetscInt*)row_starts, (PetscInt*)col_starts, diag, type_id);
row_starts, col_starts, diag, type_id);
break;
#endif
default: MFEM_ABORT(not_supported_msg << type_id);
-10
View File
@@ -81,12 +81,6 @@ public:
/// Access the underlying Operator pointer.
Operator *Ptr() const { return oper; }
/// Support the use of -> to call methods of the underlying Operator.
Operator *operator->() const { return oper; }
/// Access the underlying Operator.
Operator &operator*() { return *oper; }
/// Get the currently set operator type id.
Operator::Type Type() const { return type_id; }
@@ -193,10 +187,6 @@ public:
const Vector &X, Vector &B) const;
};
/// Add an alternative name for OperatorHandle -- OperatorPtr.
typedef OperatorHandle OperatorPtr;
} // namespace mfem
#endif
+12 -16
View File
@@ -79,7 +79,7 @@ template<typename TargetT, typename SourceT>
static TargetT *DuplicateAs(const SourceT *array, int size,
bool cplusplus = true)
{
TargetT *target_array = cplusplus ? mfem::New<TargetT>(size)
TargetT *target_array = cplusplus ? new TargetT[size]
/* */ : mfem_hypre_TAlloc(TargetT, size);
for (int i = 0; i < size; i++)
{
@@ -641,13 +641,13 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm, int id, int np,
HYPRE_Int i;
double *a_diag = mfem::New<double>(diag_nnz);
double *a_diag = new double[diag_nnz];
for (i = 0; i < diag_nnz; i++)
{
a_diag[i] = 1.0;
}
double *a_offd = mfem::New<double>(offd_nnz);
double *a_offd = new double[offd_nnz];
for (i = 0; i < offd_nnz; i++)
{
a_offd[i] = 1.0;
@@ -1010,7 +1010,6 @@ HYPRE_Int HypreParMatrix::Mult(HypreParVector &x, HypreParVector &y,
void HypreParMatrix::Mult(double a, const Vector &x, double b, Vector &y) const
{
x.Pull();
MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
<< ", expected size = " << Width());
MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
@@ -1034,13 +1033,11 @@ void HypreParMatrix::Mult(double a, const Vector &x, double b, Vector &y) const
}
hypre_ParCSRMatrixMatvec(a, A, *X, b, *Y);
y.Push();
}
void HypreParMatrix::MultTranspose(double a, const Vector &x,
double b, Vector &y) const
{
x.Pull();
MFEM_ASSERT(x.Size() == Height(), "invalid x.Size() = " << x.Size()
<< ", expected size = " << Height());
MFEM_ASSERT(y.Size() == Width(), "invalid y.Size() = " << y.Size()
@@ -1066,7 +1063,6 @@ void HypreParMatrix::MultTranspose(double a, const Vector &x,
}
hypre_ParCSRMatrixMatvecT(a, A, *Y, b, *X);
y.Push();
}
HYPRE_Int HypreParMatrix::Mult(HYPRE_ParVector x, HYPRE_ParVector y,
@@ -1481,14 +1477,14 @@ void HypreParMatrix::Destroy()
{
if (diagOwner & 1)
{
mfem::Delete(hypre_CSRMatrixI(A->diag));
mfem::Delete(hypre_CSRMatrixJ(A->diag));
delete [] hypre_CSRMatrixI(A->diag);
delete [] hypre_CSRMatrixJ(A->diag);
}
hypre_CSRMatrixI(A->diag) = NULL;
hypre_CSRMatrixJ(A->diag) = NULL;
if (diagOwner & 2)
{
mfem::Delete(hypre_CSRMatrixData(A->diag));
delete [] hypre_CSRMatrixData(A->diag);
}
hypre_CSRMatrixData(A->diag) = NULL;
}
@@ -1496,14 +1492,14 @@ void HypreParMatrix::Destroy()
{
if (offdOwner & 1)
{
mfem::Delete(hypre_CSRMatrixI(A->offd));
mfem::Delete(hypre_CSRMatrixJ(A->offd));
delete [] hypre_CSRMatrixI(A->offd);
delete [] hypre_CSRMatrixJ(A->offd);
}
hypre_CSRMatrixI(A->offd) = NULL;
hypre_CSRMatrixJ(A->offd) = NULL;
if (offdOwner & 2)
{
mfem::Delete(hypre_CSRMatrixData(A->offd));
delete [] hypre_CSRMatrixData(A->offd);
}
hypre_CSRMatrixData(A->offd) = NULL;
}
@@ -1511,7 +1507,7 @@ void HypreParMatrix::Destroy()
{
if (colMapOwner & 1)
{
mfem::Delete(hypre_ParCSRMatrixColMapOffd(A));
delete [] hypre_ParCSRMatrixColMapOffd(A);
}
hypre_ParCSRMatrixColMapOffd(A) = NULL;
}
@@ -3332,7 +3328,7 @@ HypreLOBPCG::SetPreconditioner(Solver & precond)
void
HypreLOBPCG::SetOperator(Operator & A)
{
HYPRE_Int locSize = A.Width();
int locSize = A.Width();
if (HYPRE_AssumedPartitionCheck())
{
@@ -3348,7 +3344,7 @@ HypreLOBPCG::SetOperator(Operator & A)
{
part = new HYPRE_Int[numProcs+1];
MPI_Allgather(&locSize, 1, HYPRE_MPI_INT,
MPI_Allgather(&locSize, 1, MPI_INT,
&part[1], 1, HYPRE_MPI_INT, comm);
part[0] = 0;
+16 -26
View File
@@ -10,9 +10,7 @@
// Software Foundation) version 2.1 dated February 1999.
#include "vector.hpp"
#include "dtensor.hpp"
#include "operator.hpp"
#include "../general/forall.hpp"
#include <iostream>
#include <iomanip>
@@ -165,31 +163,25 @@ ConstrainedOperator::ConstrainedOperator(Operator *A, const Array<int> &list,
void ConstrainedOperator::EliminateRHS(const Vector &x, Vector &b) const
{
w = 0.0;
const int csz = constraint_list.Size();
const DeviceArray idx(constraint_list, csz);
const DeviceVector d_x(x, x.Size());
DeviceVector d_w(w, w.Size());
MFEM_FORALL(i, csz,
for (int i = 0; i < constraint_list.Size(); i++)
{
const int id = idx[i];
d_w[id] = d_x[id];
});
w(constraint_list[i]) = x(constraint_list[i]);
}
A->Mult(w, z);
b -= z;
DeviceVector d_b(b, b.Size());
MFEM_FORALL(i, csz,
for (int i = 0; i < constraint_list.Size(); i++)
{
const int id = idx[i];
d_b[id] = d_x[id];
});
b(constraint_list[i]) = x(constraint_list[i]);
}
}
void ConstrainedOperator::Mult(const Vector &x, Vector &y) const
{
const int csz = constraint_list.Size();
if (csz == 0)
if (constraint_list.Size() == 0)
{
A->Mult(x, y);
return;
@@ -197,19 +189,17 @@ void ConstrainedOperator::Mult(const Vector &x, Vector &y) const
z = x;
const DeviceArray idx(constraint_list, csz);
DeviceVector d_z(z, z.Size());
MFEM_FORALL(i, csz, d_z[idx[i]] = 0.0;);
for (int i = 0; i < constraint_list.Size(); i++)
{
z(constraint_list[i]) = 0.0;
}
A->Mult(z, y);
const DeviceVector d_x(x, x.Size());
DeviceVector d_y(y, y.Size());
MFEM_FORALL(i, csz,
for (int i = 0; i < constraint_list.Size(); i++)
{
const int id = idx[i];
d_y[id] = d_x[id];
});
y(constraint_list[i]) = x(constraint_list[i]);
}
}
}
+2 -26
View File
@@ -124,17 +124,14 @@ public:
enum Type
{
ANY_TYPE, ///< ID for the base class Operator, i.e. any type.
MFEM_SPARSEMAT, ///< ID for class SparseMatrix.
MFEM_SPARSEMAT, ///< ID for class SparseMatrix
Hypre_ParCSR, ///< ID for class HypreParMatrix.
PETSC_MATAIJ, ///< ID for class PetscParMatrix, MATAIJ format.
PETSC_MATIS, ///< ID for class PetscParMatrix, MATIS format.
PETSC_MATSHELL, ///< ID for class PetscParMatrix, MATSHELL format.
PETSC_MATNEST, ///< ID for class PetscParMatrix, MATNEST format.
PETSC_MATHYPRE, ///< ID for class PetscParMatrix, MATHYPRE format.
PETSC_MATGENERIC, ///< ID for class PetscParMatrix, unspecified format.
Complex_Operator, ///< ID for class ComplexOperator.
MFEM_ComplexSparseMat, ///< ID for class ComplexSparseMatrix.
Complex_Hypre_ParCSR ///< ID for class ComplexHypreParMatrix.
PETSC_MATGENERIC ///< ID for class PetscParMatrix, unspecified format.
};
/// Return the type ID of the Operator class.
@@ -299,27 +296,6 @@ public:
/// Operator application
virtual void Mult(const Vector &x, Vector &y) const { y = x; }
/// Application of the transpose
virtual void MultTranspose(const Vector &x, Vector &y) const { y = x; }
};
/// Scaled Operator B: x -> a A(x).
class ScaledOperator : public Operator
{
private:
const Operator &A_;
double a_;
public:
/// Create a scalar product operator related to A.
explicit ScaledOperator(const Operator *A, double a)
: Operator(A->Width(), A->Height()), A_(*A), a_(a) { }
/// Operator application
virtual void Mult(const Vector &x, Vector &y) const
{ A_.Mult(x, y); y *= a_; }
};
+28 -43
View File
@@ -708,21 +708,15 @@ BlockDiagonalConstructor(MPI_Comm comm,
// Copy SparseMatrix into PETSc SeqAIJ format
Mat lA;
ierr = MatISGetLocalMat(A,&lA); PCHKERRQ(A,ierr);
int *II = diag->GetI();
int *JJ = diag->GetJ();
#if defined(PETSC_USE_64BIT_INDICES)
PetscInt *pII,*pJJ;
int m = diag->Height()+1, nnz = II[diag->Height()];
ierr = PetscMalloc2(m,&pII,nnz,&pJJ); PCHKERRQ(lA,ierr);
for (int i = 0; i < m; i++) { pII[i] = II[i]; }
for (int i = 0; i < nnz; i++) { pJJ[i] = JJ[i]; }
ierr = MatSeqAIJSetPreallocationCSR(lA,pII,pJJ,
diag->GetData()); PCHKERRQ(lA,ierr);
ierr = PetscFree2(pII,pJJ); PCHKERRQ(lA,ierr);
#else
ierr = MatSeqAIJSetPreallocationCSR(lA,II,JJ,
diag->GetData()); PCHKERRQ(lA,ierr);
#endif
if (sizeof(PetscInt) == sizeof(int))
{
ierr = MatSeqAIJSetPreallocationCSR(lA,diag->GetI(),diag->GetJ(),
diag->GetData()); PCHKERRQ(lA,ierr);
}
else
{
MFEM_ABORT("64bit indices not yet supported");
}
}
else
{
@@ -742,16 +736,13 @@ BlockDiagonalConstructor(MPI_Comm comm,
CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = PetscMemcpy(djj,diag->GetJ(),nnz*sizeof(PetscInt));
CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = PetscMemcpy(da,diag->GetData(),nnz*sizeof(PetscScalar));
CCHKERRQ(PETSC_COMM_SELF,ierr);
}
else
{
int *iii = diag->GetI();
int *jjj = diag->GetJ();
for (int i = 0; i < m; i++) { dii[i] = iii[i]; }
for (int i = 0; i < nnz; i++) { djj[i] = jjj[i]; }
MFEM_ABORT("64bit indices not yet supported");
}
ierr = PetscMemcpy(da,diag->GetData(),nnz*sizeof(PetscScalar));
CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = PetscCalloc1(m,&oii);
CCHKERRQ(PETSC_COMM_SELF,ierr);
if (commsize > 1)
@@ -1060,7 +1051,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
ierr = MatConvert(*A,MATIS,MAT_INPLACE_MATRIX,A); CCHKERRQ(comm,ierr);
mfem::Array<Mat> *vmatsl2l = new mfem::Array<Mat>(nr);
for (int i=0; i<(int)nr; i++) { (*vmatsl2l)[i] = matsl2l[i]; }
for (PetscInt i=0; i<nr; i++) { (*vmatsl2l)[i] = matsl2l[i]; }
ierr = PetscFree(matsl2l); CCHKERRQ(PETSC_COMM_SELF,ierr);
PetscContainer c;
@@ -2159,7 +2150,7 @@ void PetscBCHandler::ApplyBC(const Vector &x, Vector &y)
y = x;
if (bctype == ZERO)
{
for (int i = 0; i < ess_tdof_list.Size(); ++i)
for (PetscInt i = 0; i < ess_tdof_list.Size(); ++i)
{
y[ess_tdof_list[i]] = 0.0;
}
@@ -2171,7 +2162,7 @@ void PetscBCHandler::ApplyBC(const Vector &x, Vector &y)
Eval(eval_t,eval_g);
eval_t_cached = eval_t;
}
for (int i = 0; i < ess_tdof_list.Size(); ++i)
for (PetscInt i = 0; i < ess_tdof_list.Size(); ++i)
{
y[ess_tdof_list[i]] = eval_g[ess_tdof_list[i]];
}
@@ -2183,7 +2174,7 @@ void PetscBCHandler::ApplyBC(Vector &x)
(*this).SetUp(x.Size());
if (bctype == ZERO)
{
for (int i = 0; i < ess_tdof_list.Size(); ++i)
for (PetscInt i = 0; i < ess_tdof_list.Size(); ++i)
{
x[ess_tdof_list[i]] = 0.0;
}
@@ -2195,7 +2186,7 @@ void PetscBCHandler::ApplyBC(Vector &x)
Eval(eval_t,eval_g);
eval_t_cached = eval_t;
}
for (int i = 0; i < ess_tdof_list.Size(); ++i)
for (PetscInt i = 0; i < ess_tdof_list.Size(); ++i)
{
x[ess_tdof_list[i]] = eval_g[ess_tdof_list[i]];
}
@@ -2207,14 +2198,14 @@ void PetscBCHandler::FixResidualBC(const Vector& x, Vector& y)
(*this).SetUp(x.Size());
if (bctype == ZERO)
{
for (int i = 0; i < ess_tdof_list.Size(); ++i)
for (PetscInt i = 0; i < ess_tdof_list.Size(); ++i)
{
y[ess_tdof_list[i]] = x[ess_tdof_list[i]];
}
}
else
{
for (int i = 0; i < ess_tdof_list.Size(); ++i)
for (PetscInt i = 0; i < ess_tdof_list.Size(); ++i)
{
y[ess_tdof_list[i]] = x[ess_tdof_list[i]] - eval_g[ess_tdof_list[i]];
}
@@ -3884,7 +3875,7 @@ static PetscErrorCode __mfem_snes_postcheck(SNESLineSearch ls,Vec X,Vec Y,Vec W,
PetscBool *cy,PetscBool *cw, void* ctx)
{
__mfem_snes_ctx* snes_ctx = (__mfem_snes_ctx*)ctx;
bool lcy = false,lcw = false;
bool lcy,lcw;
PetscFunctionBeginUser;
mfem::PetscParVector x(X,true);
@@ -4231,21 +4222,7 @@ static PetscErrorCode Convert_Vmarks_IS(MPI_Comm comm,
(const PetscInt**)&jj,&done); CHKERRQ(ierr);
MFEM_VERIFY(done,"Unable to perform MatGetRowIJ on " << i << " l2l matrix");
ierr = MatGetSize(pl2l[i],NULL,&n); CHKERRQ(ierr);
#if defined(PETSC_USE_64BIT_INDICES)
int nnz = (int)ii[m];
int *mii = new int[m+1];
int *mjj = new int[nnz];
for (int j = 0; j < m+1; j++) { mii[j] = (int)ii[j]; }
for (int j = 0; j < nnz; j++) { mjj[j] = (int)jj[j]; }
l2l[i] = new mfem::SparseMatrix(mii,mjj,NULL,m,n,true,true,true);
#else
l2l[i] = new mfem::SparseMatrix(ii,jj,NULL,m,n,false,true,true);
#endif
ierr = MatRestoreRowIJ(pl2l[i],0,PETSC_FALSE,PETSC_FALSE,&m,
(const PetscInt**)&ii,
(const PetscInt**)&jj,&done); CHKERRQ(ierr);
MFEM_VERIFY(done,"Unable to perform MatRestoreRowIJ on "
<< i << " l2l matrix");
}
nl = 0;
for (int i = 0; i < l2l.Size(); i++) { nl += l2l[i]->Width(); }
@@ -4265,6 +4242,14 @@ static PetscErrorCode Convert_Vmarks_IS(MPI_Comm comm,
ierr = Convert_Array_IS(comm,false,&sub_dof_marker,st,is); CCHKERRQ(comm,ierr);
for (int i = 0; i < pl2l.Size(); i++)
{
PetscInt m = l2l[i]->Height();
PetscInt *ii = l2l[i]->GetI(),*jj = l2l[i]->GetJ();
PetscBool done;
ierr = MatRestoreRowIJ(pl2l[i],0,PETSC_FALSE,PETSC_FALSE,&m,
(const PetscInt**)&ii,
(const PetscInt**)&jj,&done); CHKERRQ(ierr);
MFEM_VERIFY(done,"Unable to perform MatRestoreRowIJ on "
<< i << " l2l matrix");
delete l2l[i];
}
PetscFunctionReturn(0);
+57 -78
View File
@@ -12,8 +12,6 @@
// Implementation of sparse matrix
#include "linalg.hpp"
#include "dtensor.hpp"
#include "../general/forall.hpp"
#include "../general/table.hpp"
#include "../general/sort_pairs.hpp"
@@ -90,7 +88,7 @@ SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n,
{
ownData = true;
int nnz = I[height];
A = mfem::New<double>(nnz);
A = new double[ nnz ];
for (int i=0; i<nnz; ++i)
{
A[i] = 0.0;
@@ -110,9 +108,9 @@ SparseMatrix::SparseMatrix(int nrows, int ncols, int rowsize)
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
I = mfem::New<int>(nrows + 1);
J = mfem::New<int>(nrows * rowsize);
A = mfem::New<double>(nrows * rowsize);
I = new int[nrows + 1];
J = new int[nrows * rowsize];
A = new double[nrows * rowsize];
for (int i = 0; i <= nrows; i++)
{
@@ -128,8 +126,8 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
const int nnz = mat.I[height];
if (copy_graph)
{
I = mfem::New<int>(height+1);
J = mfem::New<int>(nnz);
I = new int[height+1];
J = new int[nnz];
memcpy(I, mat.I, sizeof(int)*(height+1));
memcpy(J, mat.J, sizeof(int)*nnz);
ownGraph = true;
@@ -140,7 +138,7 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
J = mat.J;
ownGraph = false;
}
A = mfem::New<double>(nnz);
A = new double[nnz];
memcpy(A, mat.A, sizeof(double)*nnz);
ownData = true;
@@ -198,9 +196,9 @@ SparseMatrix::SparseMatrix(const Vector &v)
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
I = mfem::New<int>(height + 1);
J = mfem::New<int>(height);
A = mfem::New<double>(height);
I = new int[height + 1];
J = new int[height];
A = new double[height];
for (int i = 0; i <= height; i++)
{
@@ -584,22 +582,16 @@ void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
if (a == 1.0)
{
#ifndef MFEM_USE_LEGACY_OPENMP
const DeviceArray d_I(I);
const DeviceArray d_J(J);
const DeviceVector d_A(A);
const DeviceVector d_x(x, x.Size());
DeviceVector d_y(y, y.Size());
MFEM_FORALL(i, height,
#ifndef MFEM_USE_OPENMP
for (i = j = 0; i < height; i++)
{
double d = 0.0;
const int end = d_I[i+1];
for (int j=d_I[i]; j < end; j++)
for (end = Ip[i+1]; j < end; j++)
{
d += d_A[j] * d_x[d_J[j]];
d += Ap[j] * xp[Jp[j]];
}
d_y[i] += d;
});
yp[i] += d;
}
#else
#pragma omp parallel for private(j,end)
for (i = 0; i < height; i++)
@@ -643,11 +635,13 @@ void SparseMatrix::AddMultTranspose(const Vector &x, Vector &y,
"Output vector size (" << y.Size() << ") must match matrix width (" << width
<< ")");
int i, j, end;
double *yp = y.GetData();
if (A == NULL)
{
double *yp = y.GetData();
// The matrix is not finalized, but multiplication is still possible
for (int i = 0; i < height; i++)
for (i = 0; i < height; i++)
{
RowNode *row = Rows[i];
double b = a * x(i);
@@ -658,23 +652,16 @@ void SparseMatrix::AddMultTranspose(const Vector &x, Vector &y,
}
return;
}
// Prepare the lambda capture and get our pointers from the memory manager
const int d_height = height;
const DeviceArray d_I(I);
const DeviceArray d_J(J);
const DeviceVector d_A(A);
const DeviceVector d_x(x, x.Size());
DeviceVector d_y(y, y.Size());
MFEM_FORALL(i, d_height,
for (i = 0; i < height; i++)
{
const double xi = a * d_x[i];
const int end = d_I[i+1];
for (int j = d_I[i]; j < end; j++)
double xi = a * x(i);
end = I[i+1];
for (j = I[i]; j < end; j++)
{
const int Jj = d_J[j];
AtomicAdd(&d_y[Jj], d_A[j] * xi);
yp[J[j]] += A[j]*xi;
}
});
}
}
void SparseMatrix::PartMult(
@@ -722,26 +709,18 @@ void SparseMatrix::BooleanMult(const Array<int> &x, Array<int> &y) const
y.SetSize(Height());
y = 0;
const int height = Height();
const int *d_I = mfem::Ptr(I);
const int *d_J = mfem::Ptr(J);
const DeviceArray d_x(x, x.Size());
DeviceArray d_y(y, y.Size());
MFEM_FORALL(i, height,
for (int i = 0; i < Height(); i++)
{
const int end = d_I[i+1];
for (int j = d_I[i]; j < end; j++)
int end = I[i+1];
for (int j = I[i]; j < end; j++)
{
if (d_x[d_J[j]])
if (x[J[j]])
{
d_y[i] = d_x[d_J[j]];
y[i] = x[J[j]];
break;
}
}
});
// Sync on host for some verifications in
// pfespace GetEssentialTrueDofs and MarkerToList
mfem::Pull(y);
}
}
void SparseMatrix::BooleanMultTranspose(const Array<int> &x,
@@ -845,7 +824,7 @@ void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
delete [] ColPtrNode;
ColPtrNode = NULL;
I = mfem::New<int>(height+1);
I = new int[height+1];
I[0] = 0;
for (i = 1; i <= height; i++)
{
@@ -860,8 +839,8 @@ void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
}
nz = I[height];
J = mfem::New<int>(nz);
A = mfem::New<double>(nz);
J = new int[nz];
A = new double[nz];
// Assume we're sorted until we find out otherwise
isSorted = true;
for (j = i = 0; i < height; i++)
@@ -922,7 +901,7 @@ void SparseMatrix::GetBlocks(Array2D<SparseMatrix *> &blocks) const
{
for (int i = 0; i < br; i++)
{
int *bI = mfem::New<int>(nr + 1);
int *bI = new int[nr + 1];
for (int k = 0; k <= nr; k++)
{
bI[k] = 0;
@@ -966,8 +945,8 @@ void SparseMatrix::GetBlocks(Array2D<SparseMatrix *> &blocks) const
{
rs = b.I[k], b.I[k] = nnz, nnz += rs;
}
b.J = mfem::New<int>(nnz);
b.A = mfem::New<double>(nnz);
b.J = new int[nnz];
b.A = new double[nnz];
}
}
@@ -2729,15 +2708,15 @@ void SparseMatrix::Destroy()
{
if (I != NULL && ownGraph)
{
mfem::Delete(I);
delete [] I;
}
if (J != NULL && ownGraph)
{
mfem::Delete(J);
delete [] J;
}
if (A != NULL && ownData)
{
mfem::Delete(A);
delete [] A;
}
if (Rows != NULL)
@@ -2827,9 +2806,9 @@ SparseMatrix *Transpose (const SparseMatrix &A)
A_j = A.GetJ();
A_data = A.GetData();
At_i = mfem::New<int>(n+1);
At_j = mfem::New<int>(nnz);
At_data = mfem::New<double>(nnz);
At_i = new int[n+1];
At_j = new int[nnz];
At_data = new double[nnz];
for (i = 0; i <= n; i++)
{
@@ -2898,9 +2877,9 @@ SparseMatrix *TransposeAbstractSparseMatrix (const AbstractSparseMatrix &A,
}
nnz = A.NumNonZeroElems();
At_i = mfem::New<int>(n+1);
At_j = mfem::New<int>(nnz);
At_data = mfem::New<double>(nnz);
At_i = new int[n+1];
At_j = new int[nnz];
At_data = new double[nnz];
for (i = 0; i <= n; i++)
{
@@ -2977,7 +2956,7 @@ SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
if (OAB == NULL)
{
C_i = mfem::New<int>(nrowsA+1);
C_i = new int[nrowsA+1];
C_i[0] = num_nonzeros = 0;
for (ic = 0; ic < nrowsA; ic++)
@@ -2998,8 +2977,8 @@ SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
C_i[ic+1] = num_nonzeros;
}
C_j = mfem::New<int>(num_nonzeros);
C_data = mfem::New<double>(num_nonzeros);
C_j = new int[num_nonzeros];
C_data = new double[num_nonzeros];
C = new SparseMatrix (C_i, C_j, C_data, nrowsA, ncolsB);
@@ -3102,7 +3081,7 @@ SparseMatrix *MultAbstractSparseMatrix (const AbstractSparseMatrix &A,
B_marker[ib] = -1;
}
C_i = mfem::New<int>(nrowsA+1);
C_i = new int[nrowsA+1];
C_i[0] = num_nonzeros = 0;
@@ -3128,8 +3107,8 @@ SparseMatrix *MultAbstractSparseMatrix (const AbstractSparseMatrix &A,
C_i[ic+1] = num_nonzeros;
}
C_j = mfem::New<int>(num_nonzeros);
C_data = mfem::New<double>(num_nonzeros);
C_j = new int[num_nonzeros];
C_data = new double[num_nonzeros];
C = new SparseMatrix(C_i, C_j, C_data, nrowsA, ncolsB);
@@ -3255,7 +3234,7 @@ SparseMatrix * Add(double a, const SparseMatrix & A, double b,
int nrows = A.Height();
int ncols = A.Width();
int * C_i =mfem::New<int>(nrows+1);
int * C_i = new int[nrows+1];
int * C_j;
double * C_data;
@@ -3292,8 +3271,8 @@ SparseMatrix * Add(double a, const SparseMatrix & A, double b,
C_i[ic+1] = num_nonzeros;
}
C_j = mfem::New<int>(num_nonzeros);
C_data = mfem::New<double>(num_nonzeros);
C_j = new int[num_nonzeros];
C_data = new double[num_nonzeros];
for (int ia = 0; ia < ncols; ia++)
{
+2 -25
View File
@@ -145,34 +145,11 @@ void STRUMPACKSolver::SetReorderingStrategy( strumpack::ReorderingStrategy
solver_->options().set_reordering_method( method );
}
void STRUMPACKSolver::DisableMatching( )
void STRUMPACKSolver::SetMC64Job( strumpack::MC64Job job )
{
#if STRUMPACK_VERSION_MAJOR >= 3
solver_->options().set_matching( strumpack::MatchingJob::NONE );
#else
solver_->options().set_mc64job( strumpack::MC64Job::NONE );
#endif
solver_->options().set_mc64job( job );
}
void STRUMPACKSolver::EnableMatching( )
{
#if STRUMPACK_VERSION_MAJOR >= 3
solver_->options().set_matching
( strumpack::MatchingJob::MAX_DIAGONAL_PRODUCT_SCALING );
#else
solver_->options().set_mc64job
( strumpack::MC64Job::MAX_DIAGONAL_PRODUCT_SCALING );
#endif
}
#if STRUMPACK_VERSION_MAJOR >= 3
void STRUMPACKSolver::EnableParallelMatching( )
{
solver_->options().set_matching
( strumpack::MatchingJob::COMBBLAS );
}
#endif
void STRUMPACKSolver::SetRelTol( double rtol )
{
solver_->options().set_rel_tol( rtol );
+13 -22
View File
@@ -121,30 +121,21 @@ public:
void SetReorderingStrategy( strumpack::ReorderingStrategy method );
/**
* Disable static pivoting for stability. The static pivoting in strumpack
* permutes the sparse input matrix in order to get large (nonzero) elements
* on the diagonal. If the input matrix is already diagonally dominant, this
* MC64 performs (static) pivoting. Using a matching algorithm, it permutes
* the sparse input matrix in order to get nonzero elements on the
* diagonal. If the input matrix is already diagonally dominant, this
* reordering can be disabled.
* Possible values are:
* NONE: Don't do anything
* MAX_CARDINALITY: Maximum cardinality
* MAX_SMALLEST_DIAGONAL: Maximize smallest diagonal value
* MAX_SMALLEST_DIAGONAL_2: Same as MAX_SMALLEST_DIAGONAL, but
* different algorithm
* MAX_DIAGONAL_SUM: Maximize sum of diagonal values
* MAX_DIAGONAL_PRODUCT_SCALING: Maximize the product of the diagonal
* values and perform row & column scaling
*/
void DisableMatching();
/**
* Enable static pivoting for stability using the MC64 algorithm with
* job=5. Using a matching algorithm, this will permute the sparse input
* matrix in order to get nonzero elements (as large as possible) on the
* diagonal. And will also scale the rows and columns of the matrix.
*/
void EnableMatching();
#if STRUMPACK_VERSION_MAJOR >= 3
/**
* Use the AWPM (approximate weight perfect matching) algorithm from the
* Combinatorial BLAS library for static pivoting, i.e. getting large
* nonzeros on the diagonal. This requires that strumpack was compiled with
* support for Combinatorial BLAS.
*/
void EnableParallelMatching();
#endif
void SetMC64Job( strumpack::MC64Job job );
private:
void Init( int argc, char* argv[] );
+2 -2
View File
@@ -289,7 +289,7 @@ void SuperLUSolver::Init()
}
npcol_ = (int)(numProcs_ / nprow_);
MFEM_ASSERT(nprow_ * npcol_ == numProcs_, "");
assert(nprow_ * npcol_ == numProcs_);
PStatInit(stat); // Initialize the statistics variables.
}
@@ -423,7 +423,7 @@ void SuperLUSolver::SetupGrid()
}
npcol_ = (int)(numProcs_ / nprow_);
MFEM_ASSERT(nprow_ * npcol_ == numProcs_, "");
assert(nprow_ * npcol_ == numProcs_);
}
superlu_gridinit(comm_, nprow_, npcol_, grid);
+175 -324
View File
@@ -12,8 +12,6 @@
// Implementation of data type vector
#include "vector.hpp"
#include "dtensor.hpp"
#include "../general/forall.hpp"
#if defined(MFEM_USE_SUNDIALS) && defined(MFEM_USE_MPI)
#include <nvector/nvector_parallel.h>
@@ -30,16 +28,6 @@
namespace mfem
{
void Vector::Push() const
{
mfem::Push(data, size*sizeof(double));
}
void Vector::Pull() const
{
mfem::Pull(data, size*sizeof(double));
}
Vector::Vector(const Vector &v)
{
int s = v.Size();
@@ -48,8 +36,8 @@ Vector::Vector(const Vector &v)
{
MFEM_ASSERT(v.data, "invalid source vector");
allocsize = size = s;
data = mfem::New<double>(s);
mfem::Memcpy(data, v.data, sizeof(double)*s);
data = new double[s];
std::memcpy(data, v.data, sizeof(double)*s);
}
else
{
@@ -100,7 +88,17 @@ const double &Vector::Elem(int i) const
double Vector::operator*(const double *v) const
{
return Dot(size, data, v);
int s = size;
const double *d = data;
double prod = 0.0;
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for reduction(+:prod)
#endif
for (int i = 0; i < s; i++)
{
prod += d[i] * v[i];
}
return prod;
}
double Vector::operator*(const Vector &v) const
@@ -120,7 +118,7 @@ Vector &Vector::operator=(const double *v)
if (data != v)
{
MFEM_ASSERT(data + size <= v || v + size <= data, "Vectors overlap!");
mfem::Memcpy(data, v, sizeof(double)*size);
std::memcpy(data, v, sizeof(double)*size);
}
return *this;
}
@@ -133,30 +131,40 @@ Vector &Vector::operator=(const Vector &v)
Vector &Vector::operator=(double value)
{
DeviceVector y(data, size);
MFEM_FORALL(i, size, y[i] = value;);
int i, s = size;
double *p = data, v = value;
for (i = 0; i < s; i++)
{
*(p++) = v;
}
return *this;
}
Vector &Vector::operator*=(double c)
{
DeviceVector y(data, size);
MFEM_FORALL(i, size, y[i] *= c;);
for (int i = 0; i < size; i++)
{
data[i] *= c;
}
return *this;
}
Vector &Vector::operator/=(double c)
{
const double m = 1.0/c;
DeviceVector y(data, size);
MFEM_FORALL(i, size, y[i] *= m;);
double m = 1.0/c;
for (int i = 0; i < size; i++)
{
data[i] *= m;
}
return *this;
}
Vector &Vector::operator-=(double c)
{
DeviceVector y(data, size);
MFEM_FORALL(i, size, y[i] -= c;);
for (int i = 0; i < size; i++)
{
data[i] -= c;
}
return *this;
}
@@ -168,10 +176,10 @@ Vector &Vector::operator-=(const Vector &v)
mfem_error("Vector::operator-=(const Vector &)");
}
#endif
const int N = size;
DeviceVector y(data, N);
const DeviceVector x(v, N);
MFEM_FORALL(i, N, y[i] -= x[i];);
for (int i = 0; i < size; i++)
{
data[i] -= v(i);
}
return *this;
}
@@ -183,10 +191,10 @@ Vector &Vector::operator+=(const Vector &v)
mfem_error("Vector::operator+=(const Vector &)");
}
#endif
const int N = size;
DeviceVector y(data, N);
const DeviceVector x(v, N);
MFEM_FORALL(i, N, y[i] += x[i];);
for (int i = 0; i < size; i++)
{
data[i] += v(i);
}
return *this;
}
@@ -200,10 +208,10 @@ Vector &Vector::Add(const double a, const Vector &Va)
#endif
if (a != 0.0)
{
const int N = size;
DeviceVector y(data, N);
const DeviceVector x(Va, N);
MFEM_FORALL(i, N, y[i] += a * x[i];);
for (int i = 0; i < size; i++)
{
data[i] += a * Va(i);
}
}
return *this;
}
@@ -216,10 +224,10 @@ Vector &Vector::Set(const double a, const Vector &Va)
mfem_error("Vector::Set(const double, const Vector &)");
}
#endif
const int N = size;
DeviceVector y(data, N);
const DeviceVector x(Va, N);
MFEM_FORALL(i, N, y[i] = a * x[i];);
for (int i = 0; i < size; i++)
{
data[i] = a * Va(i);
}
return *this;
}
@@ -243,8 +251,10 @@ void Vector::SetVector(const Vector &v, int offset)
void Vector::Neg()
{
DeviceVector y(data, size);
MFEM_FORALL(i, size, y[i] = -y[i];);
for (int i = 0; i < size; i++)
{
data[i] = -data[i];
}
}
void add(const Vector &v1, const Vector &v2, Vector &v)
@@ -256,19 +266,13 @@ void add(const Vector &v1, const Vector &v2, Vector &v)
}
#endif
#if !defined(MFEM_USE_LEGACY_OPENMP)
const int N = v.size;
DeviceVector y(v, N);
const DeviceVector x1(v1, N);
const DeviceVector x2(v2, N);
MFEM_FORALL(i, N, y[i] = x1[i] + x2[i];);
#else
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < v.size; i++)
{
v.data[i] = v1.data[i] + v2.data[i];
}
#endif
}
void add(const Vector &v1, double alpha, const Vector &v2, Vector &v)
@@ -291,21 +295,14 @@ void add(const Vector &v1, double alpha, const Vector &v2, Vector &v)
{
const double *v1p = v1.data, *v2p = v2.data;
double *vp = v.data;
const int s = v.size;
#if !defined(MFEM_USE_LEGACY_OPENMP)
const int N = s;
DeviceVector d_z(vp, N);
const DeviceVector d_x(v1p, N);
const DeviceVector d_y(v2p, N);
MFEM_FORALL(i, N, d_z[i] = d_x[i] + alpha * d_y[i];);
#else
int s = v.size;
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < s; i++)
{
vp[i] = v1p[i] + alpha*v2p[i];
}
#endif
}
}
@@ -329,19 +326,15 @@ void add(const double a, const Vector &x, const Vector &y, Vector &z)
const double *xp = x.data;
const double *yp = y.data;
double *zp = z.data;
const int s = x.size;
#if !defined(MFEM_USE_LEGACY_OPENMP)
DeviceVector z(zp, s);
const DeviceVector x(xp, s);
const DeviceVector y(yp, s);
MFEM_FORALL(i, s, z[i] = a * (x[i] + y[i]););
#else
int s = x.size;
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < s; i++)
{
zp[i] = a * (xp[i] + yp[i]);
}
#endif
}
}
@@ -378,20 +371,15 @@ void add(const double a, const Vector &x,
const double *xp = x.data;
const double *yp = y.data;
double *zp = z.data;
const int s = x.size;
int s = x.size;
#if !defined(MFEM_USE_LEGACY_OPENMP)
DeviceVector z(zp, s);
const DeviceVector x(xp, s);
const DeviceVector y(yp, s);
MFEM_FORALL(i, s, z[i] = a * x[i] + b * y[i];);
#else
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < s; i++)
{
zp[i] = a * xp[i] + b * yp[i];
}
#endif
}
}
@@ -406,20 +394,15 @@ void subtract(const Vector &x, const Vector &y, Vector &z)
const double *xp = x.data;
const double *yp = y.data;
double *zp = z.data;
const int s = x.size;
int s = x.size;
#if !defined(MFEM_USE_LEGACY_OPENMP)
DeviceVector zd(zp, s);
const DeviceVector xd(xp, s);
const DeviceVector yd(yp, s);
MFEM_FORALL(i, s, zd[i] = xd[i] - yd[i];);
#else
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < s; i++)
{
zp[i] = xp[i] - yp[i];
}
#endif
}
void subtract(const double a, const Vector &x, const Vector &y, Vector &z)
@@ -443,136 +426,121 @@ void subtract(const double a, const Vector &x, const Vector &y, Vector &z)
const double *xp = x.data;
const double *yp = y.data;
double *zp = z.data;
const int s = x.size;
int s = x.size;
#if !defined(MFEM_USE_LEGACY_OPENMP)
DeviceVector zd(zp, s);
const DeviceVector xd(xp, s);
const DeviceVector yd(yp, s);
MFEM_FORALL(i, s, zd[i] = a * (xd[i] - yd[i]););
#else
#ifdef MFEM_USE_OPENMP
#pragma omp parallel for
#endif
for (int i = 0; i < s; i++)
{
zp[i] = a * (xp[i] - yp[i]);
}
#endif
}
}
void Vector::median(const Vector &lo, const Vector &hi)
{
const int N = size;
DeviceVector v(data, N);
const DeviceVector l(lo, N);
const DeviceVector h(hi, N);
MFEM_FORALL(i, N,
{
if (v[i] < l[i])
{
v[i] = l[i];
}
else if (v[i] > h[i])
{
v[i] = h[i];
}
});
}
double *v = data;
static void GetSubvector(const int N,
double *y, const double *x, const int* dofs)
{
DeviceVector d_y(y, N);
const DeviceVector d_x(x, N);
const DeviceArray d_dofs(dofs, N);
MFEM_FORALL(i, N,
for (int i = 0; i < size; i++)
{
const int dof_i = d_dofs[i];
d_y[i] = dof_i >= 0 ? d_x[dof_i] : -d_x[-dof_i-1];
});
if (v[i] < lo[i])
{
v[i] = lo[i];
}
else if (v[i] > hi[i])
{
v[i] = hi[i];
}
}
}
void Vector::GetSubVector(const Array<int> &dofs, Vector &elemvect) const
{
const int n = dofs.Size();
elemvect.SetSize(n);
mfem::GetSubvector(n, elemvect, data, dofs);
int i, j, n = dofs.Size();
elemvect.SetSize (n);
for (i = 0; i < n; i++)
{
if ((j=dofs[i]) >= 0)
{
elemvect(i) = data[j];
}
else
{
elemvect(i) = -data[-1-j];
}
}
}
void Vector::GetSubVector(const Array<int> &dofs, double *elem_data) const
{
mfem::GetSubvector(dofs.Size(), elem_data, data,dofs);
}
int i, j, n = dofs.Size();
static void SetSubvector(const int N, double* y, const double d,
const int* dofs)
{
DeviceVector d_y(y,N);
const DeviceArray d_dofs(dofs,N);
MFEM_FORALL(i, N,
for (i = 0; i < n; i++)
{
const int j = d_dofs[i];
if (j >= 0)
if ((j=dofs[i]) >= 0)
{
d_y[j] = d;
elem_data[i] = data[j];
}
else
{
d_y[-1-j] = -d;
elem_data[i] = -data[-1-j];
}
});
}
static void SetSubvector(const int N, double *y, const double *x,
const int* dofs)
{
DeviceVector d_y(y,N);
const DeviceVector d_x(x,N);
const DeviceArray d_dofs(dofs,N);
MFEM_FORALL(i, N,
{
const int dof_i = d_dofs[i];
if (dof_i >= 0)
{
d_y[dof_i] = d_x[i];
}
else
{
d_y[-1-dof_i] = -d_x[i];
}
});
}
}
void Vector::SetSubVector(const Array<int> &dofs, const double value)
{
mfem::SetSubvector(dofs.Size(), data, value, dofs);
const int n = dofs.Size();
for (int i = 0; i < n; i++)
{
const int j = dofs[i];
if (j >= 0)
{
data[j] = value;
}
else
{
data[-1-j] = -value;
}
}
}
void Vector::SetSubVector(const Array<int> &dofs, const Vector &elemvect)
{
mfem::SetSubvector(dofs.Size(), data, elemvect, dofs);
int i, j, n = dofs.Size();
for (i = 0; i < n; i++)
{
if ((j=dofs[i]) >= 0)
{
data[j] = elemvect(i);
}
else
{
data[-1-j] = -elemvect(i);
}
}
}
void Vector::SetSubVector(const Array<int> &dofs, double *elem_data)
{
mfem::SetSubvector(dofs.Size(), data, elem_data, dofs);
}
int i, j, n = dofs.Size();
static void AddElement(const int N, const int *dofs, const double *x, double *y)
{
DeviceVector d_y(y,N);
const DeviceVector d_x(x,N);
const DeviceArray d_dofs(dofs,N);
MFEM_FORALL(i, N,
for (i = 0; i < n; i++)
{
const int j = d_dofs[i];
if (j >= 0)
d_y[j] += d_x[i];
if ((j=dofs[i]) >= 0)
{
data[j] = elem_data[i];
}
else
{
d_y[-1-j] -= d_x[i];
data[-1-j] = -elem_data[i];
}
});
}
}
void Vector::AddElementVector(const Array<int> &dofs, const Vector &elemvect)
@@ -580,32 +548,55 @@ void Vector::AddElementVector(const Array<int> &dofs, const Vector &elemvect)
MFEM_ASSERT(dofs.Size() == elemvect.Size(), "Size mismatch: "
"length of dofs is " << dofs.Size() <<
", length of elemvect is " << elemvect.Size());
mfem::AddElement(dofs.Size(), dofs, elemvect.GetData(), data);
int i, j, n = dofs.Size();
for (i = 0; i < n; i++)
{
if ((j=dofs[i]) >= 0)
{
data[j] += elemvect(i);
}
else
{
data[-1-j] -= elemvect(i);
}
}
}
void Vector::AddElementVector(const Array<int> &dofs, double *elem_data)
{
mfem::AddElement(dofs.Size(), dofs, elem_data, data);
int i, j, n = dofs.Size();
for (i = 0; i < n; i++)
{
if ((j = dofs[i]) >= 0)
{
data[j] += elem_data[i];
}
else
{
data[-1-j] -= elem_data[i];
}
}
}
void Vector::AddElementVector(const Array<int> &dofs, const double a,
const Vector &elemvect)
{
const int N = dofs.Size();
const double alpha = a;
DeviceVector d_y(data, N);
const DeviceVector d_x(elemvect, N);
const DeviceArray d_dofs(dofs, N);
MFEM_FORALL(i, N,
MFEM_ASSERT(dofs.Size() == elemvect.Size(), "");
int i, j, n = dofs.Size();
for (i = 0; i < n; i++)
{
const int j = d_dofs[i];
if (j >= 0)
d_y[j] += alpha * d_x[i];
if ((j=dofs[i]) >= 0)
{
data[j] += a * elemvect(i);
}
else
{
d_y[-1-j] -= alpha * d_x[i];
data[-1-j] -= a * elemvect(i);
}
});
}
}
void Vector::SetSubVectorComplement(const Array<int> &dofs, const double val)
@@ -619,7 +610,7 @@ void Vector::SetSubVectorComplement(const Array<int> &dofs, const double val)
void Vector::Print(std::ostream &out, int width) const
{
if (!size) { return; }
Pull();
for (int i = 0; 1; )
{
out << data[i];
@@ -821,146 +812,6 @@ double Vector::Sum() const
return sum;
}
#ifdef MFEM_USE_CUDA
static __global__ void cuKernelMin(const int N, double *gdsr, const double *x)
{
__shared__ double s_min[MFEM_CUDA_BLOCKS];
const int n = blockDim.x*blockIdx.x + threadIdx.x;
if (n>=N) { return; }
const int bid = blockIdx.x;
const int tid = threadIdx.x;
const int bbd = bid*blockDim.x;
const int rid = bbd+tid;
s_min[tid] = x[n];
for (int workers=blockDim.x>>1; workers>0; workers>>=1)
{
__syncthreads();
if (tid >= workers) { continue; }
if (rid >= N) { continue; }
const int dualTid = tid + workers;
if (dualTid >= N) { continue; }
const int rdd = bbd+dualTid;
if (rdd >= N) { continue; }
if (dualTid >= blockDim.x) { continue; }
s_min[tid] = fmin(s_min[tid], s_min[dualTid]);
}
if (tid==0) { gdsr[bid] = s_min[0]; }
}
static double cuVectorMin(const int N, const double *X)
{
const DeviceVector x(X, N);
const int tpb = MFEM_CUDA_BLOCKS;
const int blockSize = MFEM_CUDA_BLOCKS;
const int gridSize = (N+blockSize-1)/blockSize;
const int min_sz = (N%tpb)==0? (N/tpb) : (1+N/tpb);
const int bytes = min_sz*sizeof(double);
static double *h_min = NULL;
if (!h_min) { h_min = (double*)calloc(min_sz,sizeof(double)); }
static CUdeviceptr gdsr = (CUdeviceptr) NULL;
if (!gdsr) { ::cuMemAlloc(&gdsr,bytes); }
cuKernelMin<<<gridSize,blockSize>>>(N, (double*)gdsr, x);
MFEM_CUDA_CHECK_RT(cudaGetLastError());
::cuMemcpy((CUdeviceptr)h_min,(CUdeviceptr)gdsr,bytes);
double min = std::numeric_limits<double>::infinity();
for (int i = 0; i < min_sz; i++) { min = fmin(min, h_min[i]); }
return min;
}
static __global__ void cuKernelDot(const int N, double *gdsr,
const double *x, const double *y)
{
__shared__ double s_dot[MFEM_CUDA_BLOCKS];
const int n = blockDim.x*blockIdx.x + threadIdx.x;
if (n>=N) { return; }
const int bid = blockIdx.x;
const int tid = threadIdx.x;
const int bbd = bid*blockDim.x;
const int rid = bbd+tid;
s_dot[tid] = x[n] * y[n];
for (int workers=blockDim.x>>1; workers>0; workers>>=1)
{
__syncthreads();
if (tid >= workers) { continue; }
if (rid >= N) { continue; }
const int dualTid = tid + workers;
if (dualTid >= N) { continue; }
const int rdd = bbd+dualTid;
if (rdd >= N) { continue; }
if (dualTid >= blockDim.x) { continue; }
s_dot[tid] += s_dot[dualTid];
}
if (tid==0) { gdsr[bid] = s_dot[0]; }
}
static double cuVectorDot(const int N, const double *X, const double *Y)
{
const DeviceVector x(X, N);
const DeviceVector y(Y, N);
static int dot_block_sz = 0;
const int tpb = MFEM_CUDA_BLOCKS;
const int blockSize = MFEM_CUDA_BLOCKS;
const int gridSize = (N+blockSize-1)/blockSize;
const int dot_sz = (N%tpb)==0? (N/tpb) : (1+N/tpb);
const int bytes = dot_sz*sizeof(double);
static double *h_dot = NULL;
if (!h_dot or dot_block_sz!=dot_sz)
{
if (h_dot) { free(h_dot); }
h_dot = (double*)calloc(dot_sz,sizeof(double));
}
static CUdeviceptr gdsr = (CUdeviceptr) NULL;
if (!gdsr or dot_block_sz!=dot_sz)
{
if (gdsr) { MFEM_CUDA_CHECK_DRV(::cuMemFree(gdsr)); }
MFEM_CUDA_CHECK_DRV(::cuMemAlloc(&gdsr,bytes));
}
if (dot_block_sz!=dot_sz)
{
dot_block_sz = dot_sz;
}
cuKernelDot<<<gridSize,blockSize>>>(N, (double*)gdsr, x, y);
MFEM_CUDA_CHECK_RT(cudaGetLastError());
MFEM_CUDA_CHECK_DRV(::cuMemcpy((CUdeviceptr)h_dot,(CUdeviceptr)gdsr,bytes));
double dot = 0.0;
for (int i = 0; i < dot_sz; i++) { dot += h_dot[i]; }
return dot;
}
#endif // MFEM_USE_CUDA
double Min(const int N, const double *x)
{
if (Device::Allows(Backend::CUDA_MASK))
{
#ifdef MFEM_USE_CUDA
return cuVectorMin(N, x);
#else
mfem_error("Using Min on device w/o support");
#endif // MFEM_USE_CUDA
}
double min = std::numeric_limits<double>::infinity();
for (int i = 0; i < N; i++) { min = fmin(min, x[i]); }
return min;
}
double Dot(const int N, const double *x, const double *y)
{
if (Device::Allows(Backend::CUDA_MASK))
{
#ifdef MFEM_USE_CUDA
return cuVectorDot(N, x, y);
#else
mfem_error("Using Dot on device w/o support");
#endif // MFEM_USE_CUDA
}
double dot = 0.0;
#ifdef MFEM_USE_LEGACY_OPENMP
#pragma omp parallel for reduction(+:dot)
#endif
for (int i = 0; i < N; i++) { dot += x[i] * y[i]; }
return dot;
}
#ifdef MFEM_USE_SUNDIALS
#ifndef SUNTRUE
+8 -35
View File
@@ -12,9 +12,10 @@
#ifndef MFEM_VECTOR
#define MFEM_VECTOR
// Data type vector
#include "../general/array.hpp"
#include "../general/globals.hpp"
#include "../general/mem_manager.hpp"
#ifdef MFEM_USE_SUNDIALS
#include <nvector/nvector_serial.h>
#endif
@@ -69,12 +70,6 @@ public:
Vector (double *_data, int _size)
{ data = _data; size = _size; allocsize = -size; }
/// Copies data from host to device
void Push() const;
/// Copies data from device to host
void Pull() const;
/// Reads a vector from multiple files
void Load (std::istream ** in, int np, int * dim);
@@ -112,7 +107,7 @@ public:
@sa SetDataAndSize(). */
void NewDataAndSize(double *d, int s)
{
if (allocsize > 0) { mfem::Delete(data); }
if (allocsize > 0) { delete [] data; }
SetDataAndSize(d, s);
}
@@ -323,7 +318,7 @@ inline Vector::Vector (int s)
if (s > 0)
{
allocsize = size = s;
data = mfem::New<double>(s);
data = new double[s];
}
else
{
@@ -345,17 +340,17 @@ inline void Vector::SetSize(int s)
}
if (allocsize > 0)
{
mfem::Delete(data);
delete [] data;
}
allocsize = size = s;
data = mfem::New<double>(s);
data = new double[s];
}
inline void Vector::Destroy()
{
if (allocsize > 0)
{
mfem::Delete(data);
delete [] data;
}
allocsize = size = 0;
data = NULL;
@@ -394,7 +389,7 @@ inline Vector::~Vector()
{
if (allocsize > 0)
{
mfem::Delete(data);
delete [] data;
}
}
@@ -448,28 +443,6 @@ inline double InnerProduct(MPI_Comm comm, const Vector &x, const Vector &y)
}
#endif
/// Kernel returning the minimum value in the array x of size N
double Min(const int N, const double *x);
/// Kernel of the inner product of arrays x and y of size N
double Dot(const int N, const double *x, const double *y);
/// Class for a simple Vector of size 3
class Vector3
{
private:
double data[3];
public:
Vector3() {}
Vector3(const double *x) { data[0]=x[0]; data[1]=x[1]; data[2]=x[2]; }
Vector3(const double x0, const double x1 = 0.0, const double x2 = 0.0)
{ data[0]=x0; data[1]=x1; data[2]=x2; }
inline operator double* () { return data; }
inline operator const double* () const { return data; }
inline double& operator()(const int i) { return data[i]; }
inline const double& operator()(const int i) const { return data[i]; }
};
}
#endif
+63 -140
View File
@@ -26,10 +26,6 @@ MFEM makefile targets:
make parallel
make debug
make pdebug
make cuda
make pcuda
make cudebug
make pcudebug
make test/check
make install
make clean
@@ -59,14 +55,6 @@ make debug
A shortcut to configure and build the serial debug version of the library.
make pdebug
A shortcut to configure and build the parallel debug version of the library.
make cuda
A shortcut to configure and build the serial GPU/CUDA optimized version of the library.
make pcuda
A shortcut to configure and build the parallel GPU/CUDA optimized version of the library.
make cudebug
A shortcut to configure and build the serial GPU/CUDA debug version of the library.
make pcudebug
A shortcut to configure and build the parallel GPU/CUDA debug version of the library.
make test
Verify the build by checking the results from running all examples, miniapps,
and tests.
@@ -159,7 +147,7 @@ $(call mfem-info, BLD = $(BLD))
# Include $(CONFIG_MK) unless some of the $(SKIP_INCLUDE_TARGETS) are given
SKIP_INCLUDE_TARGETS = help config clean distclean serial parallel debug pdebug\
cuda pcuda cudebug pcudebug style
style
HAVE_SKIP_INCLUDE_TARGET = $(filter $(SKIP_INCLUDE_TARGETS),$(MAKECMDGOALS))
ifeq (,$(HAVE_SKIP_INCLUDE_TARGET))
$(call mfem-info, Including $(CONFIG_MK))
@@ -194,43 +182,30 @@ CXXFLAGS ?= $(OPTIM_FLAGS)
# MPI configuration
ifneq ($(MFEM_USE_MPI),YES)
CXX_OR_MPICXX = $(CXX)
MFEM_CXX ?= $(CXX)
PKGS_NEED_MPI = SUPERLU STRUMPACK PETSC PUMI
$(foreach mpidep,$(PKGS_NEED_MPI),$(if $(MFEM_USE_$(mpidep):NO=),\
$(warning *** [MPI is OFF] setting MFEM_USE_$(mpidep) = NO)\
$(eval override MFEM_USE_$(mpidep)=NO),))
else
CXX_OR_MPICXX = $(MPICXX)
MFEM_CXX ?= $(MPICXX)
INCFLAGS += $(HYPRE_OPT)
ALL_LIBS += $(HYPRE_LIB)
endif
# CUDA configuration
ifneq ($(MFEM_USE_CUDA),YES)
MFEM_CXX ?= $(CXX_OR_MPICXX)
XCOMPILER = $(CXX_XCOMPILER)
XLINKER = $(CXX_XLINKER)
else
MFEM_CXX ?= $(CUDA_CXX)
CXXFLAGS += $(CUDA_FLAGS) -ccbin $(CXX_OR_MPICXX)
XCOMPILER = $(CUDA_XCOMPILER)
XLINKER = $(CUDA_XLINKER)
# CUDA_OPT and CUDA_LIB are added below
endif
DEP_CXX ?= $(MFEM_CXX)
# Check OpenMP configuration
ifeq ($(MFEM_USE_LEGACY_OPENMP),YES)
ifeq ($(MFEM_USE_OPENMP),YES)
MFEM_THREAD_SAFE ?= YES
ifneq ($(MFEM_THREAD_SAFE),YES)
$(error Incompatible config: MFEM_USE_LEGACY_OPENMP requires MFEM_THREAD_SAFE)
$(error Incompatible config: MFEM_USE_OPENMP requires MFEM_THREAD_SAFE)
endif
endif
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
MFEM_REQ_LIB_DEPS = SUPERLU METIS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
SUITESPARSE STRUMPACK GECKO GNUTLS NETCDF PETSC MPFR PUMI CUDA OCCA RAJA
SUITESPARSE STRUMPACK GECKO GNUTLS NETCDF PETSC MPFR PUMI
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
define mfem_check_dependency
@@ -248,9 +223,6 @@ endif
# List of MFEM dependencies, processed below
MFEM_DEPENDENCIES = $(MFEM_REQ_LIB_DEPS) LIBUNWIND OPENMP
# List of deprecated MFEM dependencies, processed below
MFEM_LEGACY_DEPENDENCIES = OPENMP
# Macro for adding dependencies
define mfem_add_dependency
ifeq ($(MFEM_USE_$(1)),YES)
@@ -259,18 +231,8 @@ ifeq ($(MFEM_USE_$(1)),YES)
endif
endef
# Macro for adding legacy dependencies
define mfem_add_legacy_dependency
ifeq ($(MFEM_USE_LEGACY_$(1)),YES)
INCFLAGS += $($(1)_OPT)
ALL_LIBS += $($(1)_LIB)
endif
endef
# Process dependencies
$(foreach dep,$(MFEM_DEPENDENCIES),$(eval $(call mfem_add_dependency,$(dep))))
$(foreach dep,$(MFEM_LEGACY_DEPENDENCIES),$(eval $(call \
mfem_add_legacy_dependency,$(dep))))
# Timer option
ifeq ($(MFEM_TIMER_TYPE),2)
@@ -287,12 +249,10 @@ endif
MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_METIS MFEM_USE_METIS_5 MFEM_DEBUG MFEM_USE_EXCEPTIONS\
MFEM_USE_GZSTREAM MFEM_USE_LIBUNWIND MFEM_USE_LAPACK MFEM_THREAD_SAFE\
MFEM_USE_OPENMP MFEM_USE_LEGACY_OPENMP MFEM_USE_MEMALLOC MFEM_TIMER_TYPE\
MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE MFEM_USE_GECKO\
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK 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_MM MFEM_USE_RAJA MFEM_SOURCE_DIR\
MFEM_INSTALL_DIR
MFEM_USE_OPENMP MFEM_USE_MEMALLOC MFEM_TIMER_TYPE MFEM_USE_SUNDIALS\
MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE MFEM_USE_GECKO MFEM_USE_SUPERLU\
MFEM_USE_STRUMPACK MFEM_USE_GNUTLS MFEM_USE_NETCDF MFEM_USE_PETSC\
MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_CONDUIT MFEM_USE_PUMI
# List of makefile variables that will be written to config.mk:
MFEM_CONFIG_VARS = MFEM_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS MFEM_INC_DIR\
@@ -319,9 +279,6 @@ MFEM_TEST_MK ?= @MFEM_DIR@/config/test.mk
# Use "\n" (interpreted by sed) to add a newline.
MFEM_CONFIG_EXTRA ?= $(if $(BUILD_DIR_DEF),MFEM_BUILD_DIR ?= @MFEM_DIR@,)
MFEM_SOURCE_DIR := $(MFEM_REAL_DIR)
MFEM_INSTALL_DIR := $(BUILD_REAL_DIR)
# If we have 'config' target, export variables used by config/makefile
ifneq (,$(filter config,$(MAKECMDGOALS)))
export $(MFEM_DEFINES) MFEM_DEFINES $(MFEM_CONFIG_VARS) MFEM_CONFIG_VARS
@@ -360,8 +317,7 @@ RELSRC_FILES = $(patsubst $(SRC)%,%,$(SOURCE_FILES))
OBJECT_FILES = $(patsubst $(SRC)%,$(BLD)%,$(SOURCE_FILES:.cpp=.o))
.PHONY: lib all clean distclean install config status info deps serial parallel\
debug pdebug cuda pcuda cudebug pcudebug style check test unittest\
deprecation-warnings
debug pdebug style check test unittest
.SUFFIXES:
.SUFFIXES: .cpp .o
@@ -398,38 +354,26 @@ doc:
$(BLD)libmfem.a: $(OBJECT_FILES)
$(AR) $(ARFLAGS) $(@) $(OBJECT_FILES)
$(RANLIB) $(@)
@$(MAKE) deprecation-warnings
$(BLD)libmfem.$(SO_EXT): $(BLD)libmfem.$(SO_VER)
cd $(@D) && ln -sf $(<F) $(@F)
@$(MAKE) deprecation-warnings
# If some of the external libraries are build without -fPIC, linking shared MFEM
# library may fail. In such cases, one may set EXT_LIBS on the command line.
EXT_LIBS = $(MFEM_EXT_LIBS)
$(BLD)libmfem.$(SO_VER): $(OBJECT_FILES)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $(BUILD_SOFLAGS) $(OBJECT_FILES) \
$(MFEM_CXX) $(MFEM_BUILD_FLAGS) $(BUILD_SOFLAGS) $(OBJECT_FILES) \
$(EXT_LIBS) -o $(@)
# Shortcut targets options
serial parallel debug pdebug: M_MM=NO
serial debug cuda cudebug: M_MPI=NO
parallel pdebug pcuda pcudebug: M_MPI=YES
serial parallel cuda pcuda: M_DBG=NO
debug pdebug cudebug pcudebug: M_DBG=YES
cuda pcuda cudebug pcudebug: M_CUDA=YES
cuda pcuda cudebug pcudebug: M_MM=YES
serial debug: M_MPI=NO
parallel pdebug: M_MPI=YES
serial parallel: M_DBG=NO
debug pdebug: M_DBG=YES
serial parallel debug pdebug:
$(MAKE) -f $(THIS_MK) config MFEM_USE_MPI=$(M_MPI) MFEM_DEBUG=$(M_DBG) \
$(MAKEOVERRIDES_SAVE)
$(MAKE) $(MAKEOVERRIDES_SAVE)
cuda pcuda cudebug pcudebug:
$(MAKE) -f $(THIS_MK) config MFEM_USE_MPI=$(M_MPI) MFEM_DEBUG=$(M_DBG) \
MFEM_USE_CUDA=$(M_CUDA) MFEM_USE_MM=$(M_MM) $(MAKEOVERRIDES_SAVE)
$(MAKE) $(MAKEOVERRIDES_SAVE)
deps:
rm -f $(BLD)deps.mk
for i in $(RELSRC_FILES:.cpp=); do \
@@ -498,8 +442,7 @@ install: $(if $(static),$(BLD)libmfem.a) $(if $(shared),$(BLD)libmfem.$(SO_EXT))
# install remaining includes in each subdirectory
for dir in $(DIRS); do \
mkdir -p $(PREFIX_INC)/mfem/$$dir && \
$(INSTALL) -m 640 $(SRC)$$dir/*.hpp $(SRC)$$dir/*.okl $(PREFIX_INC)/mfem/$$dir; \
done
$(INSTALL) -m 640 $(SRC)$$dir/*.hpp $(PREFIX_INC)/mfem/$$dir; done
# install config.mk in $(PREFIX_SHARE)
mkdir -p $(PREFIX_SHARE)
$(MAKE) -C $(BLD)config config-mk CONFIG_MK=config-install.mk
@@ -547,58 +490,52 @@ help:
@true
status info:
$(info MFEM_VERSION = $(MFEM_VERSION) [v$(MFEM_VERSION_STRING)])
$(info MFEM_GIT_STRING = $(MFEM_GIT_STRING))
$(info MFEM_USE_MPI = $(MFEM_USE_MPI))
$(info MFEM_USE_METIS = $(MFEM_USE_METIS))
$(info MFEM_USE_METIS_5 = $(MFEM_USE_METIS_5))
$(info MFEM_DEBUG = $(MFEM_DEBUG))
$(info MFEM_USE_EXCEPTIONS = $(MFEM_USE_EXCEPTIONS))
$(info MFEM_USE_GZSTREAM = $(MFEM_USE_GZSTREAM))
$(info MFEM_USE_LIBUNWIND = $(MFEM_USE_LIBUNWIND))
$(info MFEM_USE_LAPACK = $(MFEM_USE_LAPACK))
$(info MFEM_THREAD_SAFE = $(MFEM_THREAD_SAFE))
$(info MFEM_USE_OPENMP = $(MFEM_USE_OPENMP))
$(info MFEM_USE_LEGACY_OPENMP = $(MFEM_USE_LEGACY_OPENMP))
$(info MFEM_USE_MEMALLOC = $(MFEM_USE_MEMALLOC))
$(info MFEM_TIMER_TYPE = $(MFEM_TIMER_TYPE))
$(info MFEM_USE_SUNDIALS = $(MFEM_USE_SUNDIALS))
$(info MFEM_USE_MESQUITE = $(MFEM_USE_MESQUITE))
$(info MFEM_USE_SUITESPARSE = $(MFEM_USE_SUITESPARSE))
$(info MFEM_USE_SUPERLU = $(MFEM_USE_SUPERLU))
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
$(info MFEM_USE_GECKO = $(MFEM_USE_GECKO))
$(info MFEM_USE_GNUTLS = $(MFEM_USE_GNUTLS))
$(info MFEM_USE_NETCDF = $(MFEM_USE_NETCDF))
$(info MFEM_USE_PETSC = $(MFEM_USE_PETSC))
$(info MFEM_USE_MPFR = $(MFEM_USE_MPFR))
$(info MFEM_USE_SIDRE = $(MFEM_USE_SIDRE))
$(info MFEM_USE_CONDUIT = $(MFEM_USE_CONDUIT))
$(info MFEM_USE_PUMI = $(MFEM_USE_PUMI))
$(info MFEM_USE_CUDA = $(MFEM_USE_CUDA))
$(info MFEM_USE_RAJA = $(MFEM_USE_RAJA))
$(info MFEM_USE_OCCA = $(MFEM_USE_OCCA))
$(info MFEM_USE_MM = $(MFEM_USE_MM))
$(info MFEM_CXX = $(value MFEM_CXX))
$(info MFEM_CPPFLAGS = $(value MFEM_CPPFLAGS))
$(info MFEM_CXXFLAGS = $(value MFEM_CXXFLAGS))
$(info MFEM_TPLFLAGS = $(value MFEM_TPLFLAGS))
$(info MFEM_INCFLAGS = $(value MFEM_INCFLAGS))
$(info MFEM_FLAGS = $(value MFEM_FLAGS))
$(info MFEM_LINK_FLAGS = $(value MFEM_LINK_FLAGS))
$(info MFEM_EXT_LIBS = $(value MFEM_EXT_LIBS))
$(info MFEM_LIBS = $(value MFEM_LIBS))
$(info MFEM_LIB_FILE = $(value MFEM_LIB_FILE))
$(info MFEM_BUILD_TAG = $(value MFEM_BUILD_TAG))
$(info MFEM_PREFIX = $(value MFEM_PREFIX))
$(info MFEM_INC_DIR = $(value MFEM_INC_DIR))
$(info MFEM_LIB_DIR = $(value MFEM_LIB_DIR))
$(info MFEM_STATIC = $(MFEM_STATIC))
$(info MFEM_SHARED = $(MFEM_SHARED))
$(info MFEM_BUILD_DIR = $(MFEM_BUILD_DIR))
$(info MFEM_MPIEXEC = $(MFEM_MPIEXEC))
$(info MFEM_MPIEXEC_NP = $(MFEM_MPIEXEC_NP))
$(info MFEM_MPI_NP = $(MFEM_MPI_NP))
$(info MFEM_VERSION = $(MFEM_VERSION) [v$(MFEM_VERSION_STRING)])
$(info MFEM_GIT_STRING = $(MFEM_GIT_STRING))
$(info MFEM_USE_MPI = $(MFEM_USE_MPI))
$(info MFEM_USE_METIS = $(MFEM_USE_METIS))
$(info MFEM_USE_METIS_5 = $(MFEM_USE_METIS_5))
$(info MFEM_DEBUG = $(MFEM_DEBUG))
$(info MFEM_USE_EXCEPTIONS = $(MFEM_USE_EXCEPTIONS))
$(info MFEM_USE_GZSTREAM = $(MFEM_USE_GZSTREAM))
$(info MFEM_USE_LIBUNWIND = $(MFEM_USE_LIBUNWIND))
$(info MFEM_USE_LAPACK = $(MFEM_USE_LAPACK))
$(info MFEM_THREAD_SAFE = $(MFEM_THREAD_SAFE))
$(info MFEM_USE_OPENMP = $(MFEM_USE_OPENMP))
$(info MFEM_USE_MEMALLOC = $(MFEM_USE_MEMALLOC))
$(info MFEM_TIMER_TYPE = $(MFEM_TIMER_TYPE))
$(info MFEM_USE_SUNDIALS = $(MFEM_USE_SUNDIALS))
$(info MFEM_USE_MESQUITE = $(MFEM_USE_MESQUITE))
$(info MFEM_USE_SUITESPARSE = $(MFEM_USE_SUITESPARSE))
$(info MFEM_USE_SUPERLU = $(MFEM_USE_SUPERLU))
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
$(info MFEM_USE_GECKO = $(MFEM_USE_GECKO))
$(info MFEM_USE_GNUTLS = $(MFEM_USE_GNUTLS))
$(info MFEM_USE_NETCDF = $(MFEM_USE_NETCDF))
$(info MFEM_USE_PETSC = $(MFEM_USE_PETSC))
$(info MFEM_USE_MPFR = $(MFEM_USE_MPFR))
$(info MFEM_USE_SIDRE = $(MFEM_USE_SIDRE))
$(info MFEM_USE_CONDUIT = $(MFEM_USE_CONDUIT))
$(info MFEM_USE_PUMI = $(MFEM_USE_PUMI))
$(info MFEM_CXX = $(value MFEM_CXX))
$(info MFEM_CPPFLAGS = $(value MFEM_CPPFLAGS))
$(info MFEM_CXXFLAGS = $(value MFEM_CXXFLAGS))
$(info MFEM_TPLFLAGS = $(value MFEM_TPLFLAGS))
$(info MFEM_INCFLAGS = $(value MFEM_INCFLAGS))
$(info MFEM_FLAGS = $(value MFEM_FLAGS))
$(info MFEM_EXT_LIBS = $(value MFEM_EXT_LIBS))
$(info MFEM_LIBS = $(value MFEM_LIBS))
$(info MFEM_LIB_FILE = $(value MFEM_LIB_FILE))
$(info MFEM_BUILD_TAG = $(value MFEM_BUILD_TAG))
$(info MFEM_PREFIX = $(value MFEM_PREFIX))
$(info MFEM_INC_DIR = $(value MFEM_INC_DIR))
$(info MFEM_LIB_DIR = $(value MFEM_LIB_DIR))
$(info MFEM_STATIC = $(MFEM_STATIC))
$(info MFEM_SHARED = $(MFEM_SHARED))
$(info MFEM_BUILD_DIR = $(MFEM_BUILD_DIR))
$(info MFEM_MPIEXEC = $(MFEM_MPIEXEC))
$(info MFEM_MPIEXEC_NP = $(MFEM_MPIEXEC_NP))
$(info MFEM_MPI_NP = $(MFEM_MPI_NP))
@true
ASTYLE = astyle --options=$(SRC)config/mfem.astylerc
@@ -606,20 +543,6 @@ FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,"$(dir)/*.?pp")
FORMAT_FILES += "tests/unit/*.cpp"
FORMAT_FILES += $(foreach dir,$(DIRS),"tests/unit/$(dir)/*.?pp")
DEPRECATION_WARNING := \
"This feature is planned for removal in the next release."\
"Please open an issue at github.com/mfem/mfem/issues if you depend on it."
deprecation-warnings:
@if [ -t 1 ]; then\
red="\033[0;31m";\
yellow="\033[0;33m";\
end="\033[0m";\
fi;\
if [ $(MFEM_USE_LEGACY_OPENMP) = YES ]; then\
printf $$red"[MFEM_USE_LEGACY_OPENMP]"$$end": "$$yellow"%s"$$end"\n"\
$(DEPRECATION_WARNING);\
fi
style:
@if ! $(ASTYLE) $(FORMAT_FILES) | grep Formatted; then\
echo "No source files were changed.";\
+110 -249
View File
@@ -33,7 +33,6 @@
// METIS 4 prototypes
#if defined(MFEM_USE_METIS) && !defined(MFEM_USE_METIS_5)
typedef int idx_t;
typedef int idxtype;
extern "C" {
void METIS_PartGraphRecursive(int*, idxtype*, idxtype*, idxtype*, idxtype*,
@@ -419,51 +418,27 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
{
ElTr->Attribute = GetBdrAttribute(i);
ElTr->ElementNo = i; // boundary element number
DenseMatrix &pm = ElTr->GetPointMat();
if (Nodes == NULL)
{
GetBdrPointMatrix(i, pm);
ElTr->SetFE(GetTransformationFEforElementType(GetBdrElementType(i)));
GetBdrPointMatrix(i, ElTr->GetPointMat());
ElTr->SetFE(
GetTransformationFEforElementType(GetBdrElementType(i)));
}
else
{
const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
if (bdr_el)
DenseMatrix &pm = ElTr->GetPointMat();
Array<int> vdofs;
Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int k = 0; k < spaceDim; k++)
{
Array<int> vdofs;
Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int k = 0; k < spaceDim; k++)
for (int j = 0; j < n; j++)
{
for (int j = 0; j < n; j++)
{
pm(k,j) = (*Nodes)(vdofs[n*k+j]);
}
pm(k,j) = (*Nodes)(vdofs[n*k+j]);
}
ElTr->SetFE(bdr_el);
}
else // L2 Nodes (e.g., periodic mesh)
{
int elem_id, face_info;
GetBdrElementAdjacentElement(i, elem_id, face_info);
GetLocalFaceTransformation(GetBdrElementType(i),
GetElementType(elem_id),
FaceElemTr.Loc1.Transf, face_info);
// NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
const FiniteElement *face_el =
Nodes->FESpace()->GetTraceElement(elem_id,
GetBdrElementBaseGeometry(i));
IntegrationRule eir(face_el->GetDof());
FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
// 'Transformation' is not used
Nodes->GetVectorValues(Transformation, eir, pm);
ElTr->SetFE(face_el);
}
ElTr->SetFE(Nodes->FESpace()->GetBE(i));
}
ElTr->FinalizeTransformation();
}
@@ -1309,14 +1284,17 @@ void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
#ifdef MFEM_USE_GECKO
void Mesh::GetGeckoElementReordering(Array<int> &ordering,
int iterations, int window,
int period, int seed)
void Mesh::GetGeckoElementReordering(Array<int> &ordering)
{
Gecko::Graph graph;
// We will put some accesors in for these later
Gecko::Functional *functional =
new Gecko::FunctionalGeometric(); // ordering functional
unsigned int iterations = 1; // number of V cycles
unsigned int window = 2; // initial window size
unsigned int period = 1; // iterations between window increment
unsigned int seed = 0; // random number seed
// Run through all the elements and insert the nodes in the graph for them
for (int elemid = 0; elemid < GetNE(); ++elemid)
@@ -2137,7 +2115,7 @@ void Mesh::Finalize(bool refine, bool fix_orientation)
}
void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
double sx, double sy, double sz, bool sfc_ordering)
int generate_edges, double sx, double sy, double sz)
{
int x, y, z;
@@ -2180,58 +2158,31 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
#define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
// Sets elements and the corresponding indices of vertices
if (sfc_ordering && type == Element::HEXAHEDRON)
for (z = 0; z < nz; z++)
{
Array<int> sfc;
NCMesh::GridSfcOrdering3D(nx, ny, nz, sfc);
MFEM_VERIFY(sfc.Size() == 3*nx*ny*nz, "");
for (int k = 0; k < nx*ny*nz; k++)
for (y = 0; y < ny; y++)
{
x = sfc[3*k + 0];
y = sfc[3*k + 1];
z = sfc[3*k + 2];
ind[0] = VTX(x , y , z );
ind[1] = VTX(x+1, y , z );
ind[2] = VTX(x+1, y+1, z );
ind[3] = VTX(x , y+1, z );
ind[4] = VTX(x , y , z+1);
ind[5] = VTX(x+1, y , z+1);
ind[6] = VTX(x+1, y+1, z+1);
ind[7] = VTX(x , y+1, z+1);
AddHex(ind, 1);
}
}
else
{
for (z = 0; z < nz; z++)
{
for (y = 0; y < ny; y++)
for (x = 0; x < nx; x++)
{
for (x = 0; x < nx; x++)
ind[0] = VTX(x , y , z );
ind[1] = VTX(x+1, y , z );
ind[2] = VTX(x+1, y+1, z );
ind[3] = VTX(x , y+1, z );
ind[4] = VTX(x , y , z+1);
ind[5] = VTX(x+1, y , z+1);
ind[6] = VTX(x+1, y+1, z+1);
ind[7] = VTX(x , y+1, z+1);
if (type == Element::TETRAHEDRON)
{
ind[0] = VTX(x , y , z );
ind[1] = VTX(x+1, y , z );
ind[2] = VTX(x+1, y+1, z );
ind[3] = VTX(x , y+1, z );
ind[4] = VTX(x , y , z+1);
ind[5] = VTX(x+1, y , z+1);
ind[6] = VTX(x+1, y+1, z+1);
ind[7] = VTX(x , y+1, z+1);
if (type == Element::TETRAHEDRON)
{
AddHexAsTets(ind, 1);
}
else if (type == Element::WEDGE)
{
AddHexAsWedges(ind, 1);
}
else
{
AddHex(ind, 1);
}
AddHexAsTets(ind, 1);
}
else if (type == Element::WEDGE)
{
AddHexAsWedges(ind, 1);
}
else
{
AddHex(ind, 1);
}
}
}
@@ -2240,7 +2191,6 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
// Sets boundary elements and the corresponding indices of vertices
// bottom, bdr. attribute 1
for (y = 0; y < ny; y++)
{
for (x = 0; x < nx; x++)
{
ind[0] = VTX(x , y , 0);
@@ -2260,10 +2210,8 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
AddBdrQuad(ind, 1);
}
}
}
// top, bdr. attribute 6
for (y = 0; y < ny; y++)
{
for (x = 0; x < nx; x++)
{
ind[0] = VTX(x , y , nz);
@@ -2283,10 +2231,8 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
AddBdrQuad(ind, 6);
}
}
}
// left, bdr. attribute 5
for (z = 0; z < nz; z++)
{
for (y = 0; y < ny; y++)
{
ind[0] = VTX(0 , y , z );
@@ -2302,10 +2248,8 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
AddBdrQuad(ind, 5);
}
}
}
// right, bdr. attribute 3
for (z = 0; z < nz; z++)
{
for (y = 0; y < ny; y++)
{
ind[0] = VTX(nx, y , z );
@@ -2321,10 +2265,8 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
AddBdrQuad(ind, 3);
}
}
}
// front, bdr. attribute 2
for (x = 0; x < nx; x++)
{
for (z = 0; z < nz; z++)
{
ind[0] = VTX(x , 0, z );
@@ -2340,10 +2282,8 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
AddBdrQuad(ind, 2);
}
}
}
// back, bdr. attribute 4
for (x = 0; x < nx; x++)
{
for (z = 0; z < nz; z++)
{
ind[0] = VTX(x , ny, z );
@@ -2359,9 +2299,6 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
AddBdrQuad(ind, 4);
}
}
}
#undef VTX
#if 0
ofstream test_stream("debug.mesh");
@@ -2374,9 +2311,8 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
// Finalize(...) can be called after this method, if needed
}
void Mesh::Make2D(int nx, int ny, Element::Type type,
double sx, double sy,
bool generate_edges, bool sfc_ordering)
void Mesh::Make2D(int nx, int ny, Element::Type type, int generate_edges,
double sx, double sy)
{
int i, j, k;
@@ -2413,37 +2349,17 @@ void Mesh::Make2D(int nx, int ny, Element::Type type,
}
// Sets elements and the corresponding indices of vertices
if (sfc_ordering)
k = 0;
for (j = 0; j < ny; j++)
{
Array<int> sfc;
NCMesh::GridSfcOrdering2D(nx, ny, sfc);
MFEM_VERIFY(sfc.Size() == 2*nx*ny, "");
for (k = 0; k < nx*ny; k++)
for (i = 0; i < nx; i++)
{
i = sfc[2*k + 0];
j = sfc[2*k + 1];
ind[0] = i + j*(nx+1);
ind[1] = i + 1 +j*(nx+1);
ind[2] = i + 1 + (j+1)*(nx+1);
ind[3] = i + (j+1)*(nx+1);
elements[k] = new Quadrilateral(ind);
}
}
else
{
k = 0;
for (j = 0; j < ny; j++)
{
for (i = 0; i < nx; i++)
{
ind[0] = i + j*(nx+1);
ind[1] = i + 1 +j*(nx+1);
ind[2] = i + 1 + (j+1)*(nx+1);
ind[3] = i + (j+1)*(nx+1);
elements[k] = new Quadrilateral(ind);
k++;
}
k++;
}
}
@@ -3235,12 +3151,12 @@ Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
{
Dim = orig_mesh->Dimension();
MFEM_VERIFY(ref_factor >= 1, "the refinement factor must be >= 1");
MFEM_VERIFY(ref_factor > 1, "the refinement factor must be > 1");
MFEM_VERIFY(ref_type == BasisType::ClosedUniform ||
ref_type == BasisType::GaussLobatto, "invalid refinement type");
MFEM_VERIFY(Dim == 1 || Dim == 2 || Dim == 3,
"only implemented for Segment, Quadrilateral and Hexahedron "
"elements in 1D/2D/3D");
MFEM_VERIFY(Dim == 2 || Dim == 3,
"only implemented for Hexahedron and Quadrilateral elements in "
"2D/3D");
MFEM_VERIFY(orig_mesh->GetNumGeometries(Dim) <= 1,
"meshes with mixed elements are not supported");
@@ -3249,7 +3165,7 @@ Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
H1_FECollection rfec(ref_factor, Dim, ref_type);
FiniteElementSpace rfes(orig_mesh, &rfec);
int r_bndr_factor = pow(ref_factor, Dim - 1);
int r_bndr_factor = ref_factor * (Dim == 2 ? 1 : ref_factor);
int r_elem_factor = ref_factor * r_bndr_factor;
int r_num_vert = rfes.GetNDofs();
@@ -3306,34 +3222,18 @@ Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
rfes.GetBdrElementDofs(el, rdofs);
MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
if (Dim == 1)
const int *c2h_map = rfec.GetDofMap(geom);
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
{
// Dim == 1 is a special case because the boundary elements are
// zero-dimensional points, and therefore don't have a DofMap
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
Element *elem = NewElement(geom);
elem->SetAttribute(attrib);
int *v = elem->GetVertices();
for (int k = 0; k < nvert; k++)
{
Element *elem = NewElement(geom);
elem->SetAttribute(attrib);
int *v = elem->GetVertices();
v[0] = rdofs[RG.RefGeoms[nvert*j]];
AddBdrElement(elem);
}
}
else
{
const int *c2h_map = rfec.GetDofMap(geom);
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
{
Element *elem = NewElement(geom);
elem->SetAttribute(attrib);
int *v = elem->GetVertices();
for (int k = 0; k < nvert; k++)
{
int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
v[k] = rdofs[c2h_map[cid]];
}
AddBdrElement(elem);
int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
v[k] = rdofs[c2h_map[cid]];
}
AddBdrElement(elem);
}
}
@@ -3591,12 +3491,6 @@ void Mesh::SetNodalFESpace(FiniteElementSpace *nfes)
SetNodalGridFunction(nodes, true);
}
void Mesh::EnsureNodes()
{
if (Nodes) { return; }
SetCurvature(1, false, -1, Ordering::byVDIM);
}
void Mesh::SetNodalGridFunction(GridFunction *nodes, bool make_owner)
{
GetNodes(*nodes);
@@ -5005,42 +4899,21 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
}
else
{
idx_t *I, *J, n;
int *I, *J, n;
#ifndef MFEM_USE_METIS_5
idx_t wgtflag = 0;
idx_t numflag = 0;
idx_t options[5];
int wgtflag = 0;
int numflag = 0;
int options[5];
#else
idx_t ncon = 1;
idx_t err;
idx_t options[40];
int ncon = 1;
int err;
int options[40];
#endif
idx_t edgecut;
// In case METIS have been compiled with 64bit indices
bool freedata = false;
idx_t mparts = (idx_t) nparts;
idx_t *mpartitioning;
int edgecut;
n = NumOfElements;
if (sizeof(idx_t) == sizeof(int))
{
I = (idx_t*) el_to_el->GetI();
J = (idx_t*) el_to_el->GetJ();
mpartitioning = (idx_t*) partitioning;
}
else
{
int *iI = el_to_el->GetI();
int *iJ = el_to_el->GetJ();
int m = iI[n];
I = new idx_t[n+1];
J = new idx_t[m];
for (int k = 0; k < n+1; k++) { I[k] = iI[k]; }
for (int k = 0; k < m; k++) { J[k] = iJ[k]; }
mpartitioning = new idx_t[n];
freedata = true;
}
I = el_to_el->GetI();
J = el_to_el->GetJ();
#ifndef MFEM_USE_METIS_5
options[0] = 0;
#else
@@ -5057,7 +4930,7 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
// std::sort(J+I[i], J+I[i+1]);
// Sort in decreasing order, as in previous versions of MFEM.
std::sort(J+I[i], J+I[i+1], std::greater<idx_t>());
std::sort(J+I[i], J+I[i+1], std::greater<int>());
}
}
@@ -5067,30 +4940,30 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
{
#ifndef MFEM_USE_METIS_5
METIS_PartGraphRecursive(&n,
I,
J,
NULL,
NULL,
(idxtype *) I,
(idxtype *) J,
(idxtype *) NULL,
(idxtype *) NULL,
&wgtflag,
&numflag,
&mparts,
&nparts,
options,
&edgecut,
mpartitioning);
(idxtype *) partitioning);
#else
err = METIS_PartGraphRecursive(&n,
&ncon,
I,
J,
NULL,
NULL,
NULL,
&mparts,
NULL,
NULL,
(idx_t *) NULL,
(idx_t *) NULL,
(idx_t *) NULL,
&nparts,
(real_t *) NULL,
(real_t *) NULL,
options,
&edgecut,
mpartitioning);
partitioning);
if (err != 1)
mfem_error("Mesh::GeneratePartitioning: "
" error in METIS_PartGraphRecursive!");
@@ -5103,30 +4976,30 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
{
#ifndef MFEM_USE_METIS_5
METIS_PartGraphKway(&n,
I,
J,
NULL,
NULL,
(idxtype *) I,
(idxtype *) J,
(idxtype *) NULL,
(idxtype *) NULL,
&wgtflag,
&numflag,
&mparts,
&nparts,
options,
&edgecut,
mpartitioning);
(idxtype *) partitioning);
#else
err = METIS_PartGraphKway(&n,
&ncon,
I,
J,
NULL,
NULL,
NULL,
&mparts,
NULL,
NULL,
(idx_t *) NULL,
(idx_t *) NULL,
(idx_t *) NULL,
&nparts,
(real_t *) NULL,
(real_t *) NULL,
options,
&edgecut,
mpartitioning);
partitioning);
if (err != 1)
mfem_error("Mesh::GeneratePartitioning: "
" error in METIS_PartGraphKway!");
@@ -5139,31 +5012,31 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
{
#ifndef MFEM_USE_METIS_5
METIS_PartGraphVKway(&n,
I,
J,
NULL,
NULL,
(idxtype *) I,
(idxtype *) J,
(idxtype *) NULL,
(idxtype *) NULL,
&wgtflag,
&numflag,
&mparts,
&nparts,
options,
&edgecut,
mpartitioning);
(idxtype *) partitioning);
#else
options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_VOL;
err = METIS_PartGraphKway(&n,
&ncon,
I,
J,
NULL,
NULL,
NULL,
&mparts,
NULL,
NULL,
(idx_t *) NULL,
(idx_t *) NULL,
(idx_t *) NULL,
&nparts,
(real_t *) NULL,
(real_t *) NULL,
options,
&edgecut,
mpartitioning);
partitioning);
if (err != 1)
mfem_error("Mesh::GeneratePartitioning: "
" error in METIS_PartGraphKway!");
@@ -5174,17 +5047,6 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
mfem::out << "Mesh::GeneratePartitioning(...): edgecut = "
<< edgecut << endl;
#endif
nparts = (int) mparts;
if (mpartitioning != (idx_t*)partitioning)
{
for (int k = 0; k<NumOfElements; k++) { partitioning[k] = mpartitioning[k]; }
}
if (freedata)
{
delete[] I;
delete[] J;
delete[] mpartitioning;
}
}
if (el_to_el)
@@ -5828,7 +5690,6 @@ void Mesh::SetNodes(const Vector &node_coord)
void Mesh::NewNodes(GridFunction &nodes, bool make_owner)
{
if (own_nodes) { delete Nodes; }
nodes.Pull();
Nodes = &nodes;
spaceDim = Nodes->FESpace()->GetVDim();
own_nodes = (int)make_owner;
+20 -39
View File
@@ -210,7 +210,7 @@ protected:
void ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
bool &finalize_topo);
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf);
void ReadInlineMesh(std::istream &input, bool generate_edges = false);
void ReadInlineMesh(std::istream &input, int generate_edges = 0);
void ReadGmshMesh(std::istream &input);
/* Note NetCDF (optional library) is used for reading cubit files */
#ifdef MFEM_USE_NETCDF
@@ -408,20 +408,17 @@ protected:
/** Creates mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into
nx*ny*nz hexahedra if type=HEXAHEDRON or into 6*nx*ny*nz tetrahedrons if
type=TETRAHEDRON. The parameter @a sfc_ordering controls how the elements
(when type=HEXAHEDRON) are ordered: true - use space-filling curve
ordering, or false - use lexicographic ordering. */
void Make3D(int nx, int ny, int nz, Element::Type type,
double sx, double sy, double sz, bool sfc_ordering);
type=TETRAHEDRON. If generate_edges = 0 (default) edges are not
generated, if 1 edges are generated. */
void Make3D(int nx, int ny, int nz, Element::Type type, int generate_edges,
double sx, double sy, double sz);
/** Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny
quadrilaterals if type = QUADRILATERAL or into 2*nx*ny triangles if
type = TRIANGLE. If generate_edges = 0 (default) edges are not generated,
if 1 edges are generated. The parameter @a sfc_ordering controls how the
elements (when type=QUADRILATERAL) are ordered: true - use space-filling
curve ordering, or false - use lexicographic ordering. */
void Make2D(int nx, int ny, Element::Type type, double sx, double sy,
bool generate_edges, bool sfc_ordering);
if 1 edges are generated. */
void Make2D(int nx, int ny, Element::Type type, int generate_edges,
double sx, double sy);
/// Creates a 1D mesh for the interval [0,sx] divided into n equal intervals.
void Make1D(int n, double sx = 1.0);
@@ -558,46 +555,34 @@ public:
/** This is our integration with the Gecko library. This will call the
Gecko library to find an element ordering that will increase memory
coherency by putting elements that are in physical proximity closer in
memory. It can also be used to get a space-filling curve ordering for
ParNCMesh partitioning.
@param[out] ordering Output element ordering.
@param[in] iterations Number of V cycles (default 1).
@param[in] window Initial window size (default 2).
@param[in] period Iterations between window increment (default 1).
@param[in] seed Random number seed (default 0). */
void GetGeckoElementReordering(Array<int> &ordering,
int iterations = 1, int window = 2,
int period = 1, int seed = 0);
memory. */
void GetGeckoElementReordering(Array<int> &ordering);
#endif
/** Rebuilds the mesh with a different order of elements. The ordering
vector maps the old element number to the new element number. This also
reorders the vertices and nodes edges and faces along with the elements. */
reorders the vertices and nodes edges and faces along with the elements. */
void ReorderElements(const Array<int> &ordering, bool reorder_vertices = true);
/** Creates mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into
nx*ny*nz hexahedra if type=HEXAHEDRON or into 6*nx*ny*nz tetrahedrons if
type=TETRAHEDRON. If sfc_ordering = true (default), elements are ordered
along a space-filling curve, instead of row by row and layer by layer.
The parameter @a generate_edges is ignored (for now, it is kept for
backward compatibility). */
Mesh(int nx, int ny, int nz, Element::Type type, bool generate_edges = false,
double sx = 1.0, double sy = 1.0, double sz = 1.0,
bool sfc_ordering = true)
type=TETRAHEDRON. If generate_edges = 0 (default) edges are not
generated, if 1 edges are generated. */
Mesh(int nx, int ny, int nz, Element::Type type, int generate_edges = 0,
double sx = 1.0, double sy = 1.0, double sz = 1.0)
{
Make3D(nx, ny, nz, type, sx, sy, sz, sfc_ordering);
Make3D(nx, ny, nz, type, generate_edges, sx, sy, sz);
Finalize(true); // refine = true
}
/** Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny
quadrilaterals if type = QUADRILATERAL or into 2*nx*ny triangles if
type = TRIANGLE. If generate_edges = 0 (default) edges are not generated,
if 1 edges are generated. If scf_ordering = true (default), elements are
ordered along a space-filling curve, instead of row by row. */
Mesh(int nx, int ny, Element::Type type, bool generate_edges = false,
double sx = 1.0, double sy = 1.0, bool sfc_ordering = true)
if 1 edges are generated. */
Mesh(int nx, int ny, Element::Type type, int generate_edges = 0,
double sx = 1.0, double sy = 1.0)
{
Make2D(nx, ny, type, sx, sy, generate_edges, sfc_ordering);
Make2D(nx, ny, type, generate_edges, sx, sy);
Finalize(true); // refine = true
}
@@ -1035,10 +1020,6 @@ public:
/** Return the FiniteElementSpace on which the current mesh nodes are
defined or NULL if the mesh does not have nodes. */
const FiniteElementSpace *GetNodalFESpace() const;
/** Make sure that the mesh has valid nodes, i.e. its geometry is described
by a vector finite element grid function (even if it is a low-order mesh
with straight edges). */
void EnsureNodes();
/** Set the curvature of the mesh nodes using the given polynomial degree,
'order', and optionally: discontinuous or continuous FE space, 'discont',
+3 -4
View File
@@ -731,7 +731,7 @@ void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf)
}
}
void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
void Mesh::ReadInlineMesh(std::istream &input, int generate_edges)
{
// Initialize to negative numbers so that we know if they've been set. We're
// using Element::POINT as our flag, since we're not going to make a 0D mesh,
@@ -862,7 +862,7 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
<< " ny = " << ny << "\n"
<< " sx = " << sx << "\n"
<< " sy = " << sy << "\n");
Make2D(nx, ny, type, sx, sy, generate_edges, true);
Make2D(nx, ny, type, generate_edges, sx, sy);
}
else if (type == Element::TETRAHEDRON || type == Element::WEDGE ||
type == Element::HEXAHEDRON)
@@ -877,8 +877,7 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
<< " sx = " << sx << "\n"
<< " sy = " << sy << "\n"
<< " sz = " << sz << "\n");
Make3D(nx, ny, nz, type, sx, sy, sz, true);
// TODO: maybe have an option in the file to control ordering?
Make3D(nx, ny, nz, type, generate_edges, sx, sy, sz);
}
else
{
+36 -557
View File
@@ -11,12 +11,10 @@
#include "mesh_headers.hpp"
#include "../fem/fem.hpp"
#include "../general/sort_pairs.hpp"
#include <string>
#include <cmath>
#include <climits> // INT_MAX
#include <map>
namespace mfem
{
@@ -33,118 +31,6 @@ const DenseTensor &CoarseFineTransformations::GetPointMatrices(
return pm_it->second;
}
namespace internal
{
// Used in CoarseFineTransformations::GetCoarseToFineMap() below.
struct RefType
{
Geometry::Type geom;
int num_children;
const Pair<int,int> *children;
RefType(Geometry::Type g, int n, const Pair<int,int> *c)
: geom(g), num_children(n), children(c) { }
bool operator<(const RefType &other) const
{
if (geom < other.geom) { return true; }
if (geom > other.geom) { return false; }
if (num_children < other.num_children) { return true; }
if (num_children > other.num_children) { return false; }
for (int i = 0; i < num_children; i++)
{
if (children[i].one < other.children[i].one) { return true; }
if (children[i].one > other.children[i].one) { return false; }
}
return false; // everything is equal
}
};
}
void CoarseFineTransformations::GetCoarseToFineMap(
const mfem::Mesh &fine_mesh, Table &coarse_to_fine,
Array<int> &coarse_to_ref_type, Table &ref_type_to_matrix,
Array<mfem::Geometry::Type> &ref_type_to_geom) const
{
const int fine_ne = embeddings.Size();
int coarse_ne = -1;
for (int i = 0; i < fine_ne; i++)
{
coarse_ne = std::max(coarse_ne, embeddings[i].parent);
}
coarse_ne++;
coarse_to_ref_type.SetSize(coarse_ne);
coarse_to_fine.SetDims(coarse_ne, fine_ne);
Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
Array<Pair<int,int> > cf_j(fine_ne);
cf_i = 0;
for (int i = 0; i < fine_ne; i++)
{
cf_i[embeddings[i].parent+1]++;
}
cf_i.PartialSum();
MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
for (int i = 0; i < fine_ne; i++)
{
const Embedding &e = embeddings[i];
cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
cf_j[cf_i[e.parent]].two = i;
cf_i[e.parent]++;
}
std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
cf_i[0] = 0;
for (int i = 0; i < coarse_ne; i++)
{
std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
}
for (int i = 0; i < fine_ne; i++)
{
coarse_to_fine.GetJ()[i] = cf_j[i].two;
}
using internal::RefType;
using std::map;
using std::pair;
map<RefType,int> ref_type_map;
for (int i = 0; i < coarse_ne; i++)
{
const int num_children = cf_i[i+1]-cf_i[i];
MFEM_ASSERT(num_children > 0, "");
const int fine_el = cf_j[cf_i[i]].two;
// Assuming the coarse and the fine elements have the same geometry:
const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
pair<map<RefType,int>::iterator,bool> res =
ref_type_map.insert(
pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
coarse_to_ref_type[i] = res.first->second;
}
ref_type_to_matrix.MakeI((int)ref_type_map.size());
ref_type_to_geom.SetSize((int)ref_type_map.size());
for (map<RefType,int>::iterator it = ref_type_map.begin();
it != ref_type_map.end(); ++it)
{
ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
ref_type_to_geom[it->second] = it->first.geom;
}
ref_type_to_matrix.MakeJ();
for (map<RefType,int>::iterator it = ref_type_map.begin();
it != ref_type_map.end(); ++it)
{
const RefType &rt = it->first;
for (int j = 0; j < rt.num_children; j++)
{
ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
}
}
ref_type_to_matrix.ShiftUpI();
}
NCMesh::GeomInfo NCMesh::GI[Geometry::NumGeom];
NCMesh::GeomInfo& NCMesh::gi_hex = NCMesh::GI[Geometry::CUBE];
@@ -234,7 +120,8 @@ NCMesh::NCMesh(const Mesh *mesh, std::istream *vertex_parents)
}
// create the NCMesh::Element struct for each Mesh element
for (int i = 0; i < mesh->GetNE(); i++)
root_count = mesh->GetNE();
for (int i = 0; i < root_count; i++)
{
const mfem::Element *elem = mesh->GetElement(i);
@@ -293,11 +180,6 @@ NCMesh::NCMesh(const Mesh *mesh, std::istream *vertex_parents)
}
}
if (!vertex_parents) // not loading mesh
{
InitRootState(mesh->GetNE());
}
Update();
}
@@ -308,9 +190,9 @@ NCMesh::NCMesh(const NCMesh &other)
, nodes(other.nodes)
, faces(other.faces)
, elements(other.elements)
, root_count(other.root_count)
{
other.free_element_ids.Copy(free_element_ids);
other.root_state.Copy(root_state);
other.top_vertex_pos.Copy(top_vertex_pos);
Update();
}
@@ -1246,53 +1128,6 @@ void NCMesh::Refine(const Array<Refinement>& refinements)
//// Derefinement //////////////////////////////////////////////////////////////
static int quad_deref_table[3][4 + 4] =
{
{ 0, 1, 1, 0, /**/ 1, 1, 0, 0 }, // 1 - X
{ 0, 0, 1, 1, /**/ 0, 0, 1, 1 }, // 2 - Y
{ 0, 1, 2, 3, /**/ 1, 1, 3, 3 } // 3 - iso
};
static int hex_deref_table[7][8 + 6] =
{
{ 0, 1, 1, 0, 0, 1, 1, 0, /**/ 1, 1, 1, 0, 0, 0 }, // 1 - X
{ 0, 0, 1, 1, 0, 0, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 2 - Y
{ 0, 1, 2, 3, 0, 1, 2, 3, /**/ 1, 1, 1, 3, 3, 3 }, // 3 - XY
{ 0, 0, 0, 0, 1, 1, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 4 - Z
{ 0, 1, 1, 0, 3, 2, 2, 3, /**/ 1, 1, 1, 3, 3, 3 }, // 5 - XZ
{ 0, 0, 1, 1, 2, 2, 3, 3, /**/ 0, 0, 0, 3, 3, 3 }, // 6 - YZ
{ 0, 1, 2, 3, 4, 5, 6, 7, /**/ 1, 1, 1, 7, 7, 7 } // 7 - iso
};
int NCMesh::RetrieveNode(const Element &el, int index)
{
if (!el.ref_type) { return el.node[index]; }
// need to retrieve node from a child element (there is always a child
// that inherited the parent's corner under the same index)
int ch;
switch (el.geom)
{
case Geometry::CUBE:
ch = el.child[hex_deref_table[el.ref_type - 1][index]];
break;
case Geometry::SQUARE:
ch = el.child[quad_deref_table[el.ref_type - 1][index]];
break;
case Geometry::TRIANGLE:
ch = el.child[index];
break;
default:
ch = 0; // suppress compiler warning
MFEM_ABORT("Unsupported element geometry.");
}
return RetrieveNode(elements[ch], index);
}
void NCMesh::DerefineElement(int elem)
{
Element &el = elements[elem];
@@ -1314,14 +1149,23 @@ void NCMesh::DerefineElement(int elem)
int fa[6];
if (el.geom == Geometry::CUBE)
{
const int table[7][8 + 6] =
{
{ 0, 1, 1, 0, 0, 1, 1, 0, /**/ 1, 1, 1, 0, 0, 0 }, // 1 - X
{ 0, 0, 1, 1, 0, 0, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 2 - Y
{ 0, 1, 2, 3, 0, 1, 2, 3, /**/ 1, 1, 1, 3, 3, 3 }, // 3 - XY
{ 0, 0, 0, 0, 1, 1, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 4 - Z
{ 0, 1, 1, 0, 3, 2, 2, 3, /**/ 1, 1, 1, 3, 3, 3 }, // 5 - XZ
{ 0, 0, 1, 1, 2, 2, 3, 3, /**/ 0, 0, 0, 3, 3, 3 }, // 6 - YZ
{ 0, 1, 2, 3, 4, 5, 6, 7, /**/ 1, 1, 1, 7, 7, 7 } // 7 - iso
};
for (int i = 0; i < 8; i++)
{
Element &ch = elements[child[hex_deref_table[el.ref_type - 1][i]]];
el.node[i] = ch.node[i];
el.node[i] = elements[child[table[el.ref_type - 1][i]]].node[i];
}
for (int i = 0; i < 6; i++)
{
Element &ch = elements[child[hex_deref_table[el.ref_type - 1][i + 8]]];
Element &ch = elements[child[table[el.ref_type - 1][i + 8]]];
const int* fv = gi_hex.faces[i];
fa[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
ch.node[fv[2]], ch.node[fv[3]])->attribute;
@@ -1329,14 +1173,19 @@ void NCMesh::DerefineElement(int elem)
}
else if (el.geom == Geometry::SQUARE)
{
const int table[3][4 + 4] =
{
{ 0, 1, 1, 0, /**/ 1, 1, 0, 0 }, // 1 - X
{ 0, 0, 1, 1, /**/ 0, 0, 1, 1 }, // 2 - Y
{ 0, 1, 2, 3, /**/ 1, 1, 3, 3 } // 3 - iso
};
for (int i = 0; i < 4; i++)
{
Element &ch = elements[child[quad_deref_table[el.ref_type - 1][i]]];
el.node[i] = ch.node[i];
el.node[i] = elements[child[table[el.ref_type - 1][i]]].node[i];
}
for (int i = 0; i < 4; i++)
{
Element &ch = elements[child[quad_deref_table[el.ref_type - 1][i + 4]]];
Element &ch = elements[child[table[el.ref_type - 1][i + 4]]];
const int* fv = gi_quad.faces[i];
fa[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
ch.node[fv[2]], ch.node[fv[3]])->attribute;
@@ -1423,7 +1272,7 @@ const Table& NCMesh::GetDerefinementTable()
Array<Connection> list;
list.Reserve(leaf_elements.Size());
for (int i = 0; i < root_state.Size(); i++)
for (int i = 0; i < root_count; i++)
{
CollectDerefinements(i, list);
}
@@ -1633,9 +1482,9 @@ void NCMesh::UpdateLeafElements()
{
// collect leaf elements from all roots
leaf_elements.SetSize(0);
for (int i = 0; i < root_state.Size(); i++)
for (int i = 0; i < root_count; i++)
{
CollectLeafElements(i, root_state[i]);
CollectLeafElements(i, 0);
// TODO: root state should not always be 0, we need a precomputed array
// with root element states to ensure continuity where possible, also
// optimized ordering of the root elements themselves (Gecko?)
@@ -1652,69 +1501,6 @@ void NCMesh::AssignLeafIndices()
}
}
void NCMesh::InitRootState(int root_count)
{
root_state.SetSize(root_count);
root_state = 0;
char* node_order;
int nch;
switch (GetElementGeometry())
{
case Geometry::SQUARE:
nch = 4;
node_order = (char*) quad_hilbert_child_order;
break;
case Geometry::CUBE:
nch = 8;
node_order = (char*) hex_hilbert_child_order;
break;
default:
return; // do nothing, all states stay zero
}
int entry_node = -2;
// process the root element sequence
for (int i = 0; i < root_count; i++)
{
Element &el = elements[i];
int v_in = FindNodeExt(el, entry_node, false);
if (v_in < 0) { v_in = 0; }
// determine which nodes are shared with the next element
bool shared[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
if (i+1 < root_count)
{
Element &next = elements[i+1];
for (int j = 0; j < nch; j++)
{
int node = FindNodeExt(el, RetrieveNode(next, j), false);
if (node >= 0) { shared[node] = true; }
}
}
// select orientation that starts in v_in and exits in shared node
int state = Dim*v_in;
for (int j = 0; j < Dim; j++)
{
if (shared[(int) node_order[nch*(state + j) + nch-1]])
{
state += j;
break;
}
}
root_state[i] = state;
entry_node = RetrieveNode(el, node_order[nch*state + nch-1]);
}
}
mfem::Element* NCMesh::NewMeshElement(int geom) const
{
switch (geom)
@@ -1917,16 +1703,6 @@ int NCMesh::find_node(const Element &el, int node)
return -1;
}
int NCMesh::FindNodeExt(const Element &el, int node, bool abort)
{
for (int i = 0; i < GI[(int) el.geom].nv; i++)
{
if (RetrieveNode(el, i) == node) { return i; }
}
if (abort) { MFEM_ABORT("Node not found."); }
return -1;
}
int NCMesh::find_element_edge(const Element &el, int vn0, int vn1)
{
MFEM_ASSERT(!el.ref_type, "");
@@ -2163,7 +1939,7 @@ void NCMesh::BuildEdgeList()
processed_edges = 0;
Array<int> edge_element(nodes.NumIds());
Array<signed char> edge_local(nodes.NumIds());
Array<char> edge_local(nodes.NumIds());
edge_local = -1;
// visit edges of leaf elements
@@ -2422,7 +2198,7 @@ void NCMesh::CollectFaceVertices(int v0, int v1, int v2, int v3,
void NCMesh::BuildElementToVertexTable()
{
int nrows = leaf_elements.Size();
int* I = mfem::New<int>(nrows + 1);
int* I = new int[nrows + 1];
int** JJ = new int*[nrows];
Array<int> indices;
@@ -2460,8 +2236,8 @@ void NCMesh::BuildElementToVertexTable()
indices.Unique();
int size = indices.Size();
I[i] = size;
JJ[i] = mfem::New<int>(size);
std::memcpy(JJ[i], indices.GetData(), size * sizeof(int));
JJ[i] = new int[size];
memcpy(JJ[i], indices.GetData(), size * sizeof(int));
}
// finalize the I array of the table
@@ -2475,13 +2251,13 @@ void NCMesh::BuildElementToVertexTable()
I[nrows] = nnz;
// copy the temporarily stored rows into one J array
int *J = mfem::New<int>(nnz);
int *J = new int[nnz];
nnz = 0;
for (int i = 0; i < nrows; i++)
{
int cnt = I[i+1] - I[i];
std::memcpy(J+nnz, JJ[i], cnt * sizeof(int));
mfem::Delete(JJ[i]);
memcpy(J+nnz, JJ[i], cnt * sizeof(int));
delete [] JJ[i];
nnz += cnt;
}
@@ -3211,273 +2987,6 @@ void NCMesh::ClearTransforms()
}
//// SFC Ordering //////////////////////////////////////////////////////////////
static int sgn(int x)
{
return (x < 0) ? -1 : (x > 0) ? 1 : 0;
}
static void HilbertSfc2D(int x, int y, int ax, int ay, int bx, int by,
Array<int> &coords)
{
int w = std::abs(ax + ay);
int h = std::abs(bx + by);
int dax = sgn(ax), day = sgn(ay); // unit major direction ("right")
int dbx = sgn(bx), dby = sgn(by); // unit orthogonal direction ("up")
if (h == 1) // trivial row fill
{
for (int i = 0; i < w; i++, x += dax, y += day)
{
coords.Append(x);
coords.Append(y);
}
return;
}
if (w == 1) // trivial column fill
{
for (int i = 0; i < h; i++, x += dbx, y += dby)
{
coords.Append(x);
coords.Append(y);
}
return;
}
int ax2 = ax/2, ay2 = ay/2;
int bx2 = bx/2, by2 = by/2;
int w2 = std::abs(ax2 + ay2);
int h2 = std::abs(bx2 + by2);
if (2*w > 3*h) // long case: split in two parts only
{
if ((w2 & 0x1) && (w > 2))
{
ax2 += dax, ay2 += day; // prefer even steps
}
HilbertSfc2D(x, y, ax2, ay2, bx, by, coords);
HilbertSfc2D(x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by, coords);
}
else // standard case: one step up, one long horizontal step, one step down
{
if ((h2 & 0x1) && (h > 2))
{
bx2 += dbx, by2 += dby; // prefer even steps
}
HilbertSfc2D(x, y, bx2, by2, ax2, ay2, coords);
HilbertSfc2D(x+bx2, y+by2, ax, ay, bx-bx2, by-by2, coords);
HilbertSfc2D(x+(ax-dax)+(bx2-dbx), y+(ay-day)+(by2-dby),
-bx2, -by2, -(ax-ax2), -(ay-ay2), coords);
}
}
static void HilbertSfc3D(int x, int y, int z,
int ax, int ay, int az,
int bx, int by, int bz,
int cx, int cy, int cz,
Array<int> &coords)
{
int w = std::abs(ax + ay + az);
int h = std::abs(bx + by + bz);
int d = std::abs(cx + cy + cz);
int dax = sgn(ax), day = sgn(ay), daz = sgn(az); // unit major dir ("right")
int dbx = sgn(bx), dby = sgn(by), dbz = sgn(bz); // unit ortho dir ("forward")
int dcx = sgn(cx), dcy = sgn(cy), dcz = sgn(cz); // unit ortho dir ("up")
// trivial row/column fills
if (h == 1 && d == 1)
{
for (int i = 0; i < w; i++, x += dax, y += day, z += daz)
{
coords.Append(x);
coords.Append(y);
coords.Append(z);
}
return;
}
if (w == 1 && d == 1)
{
for (int i = 0; i < h; i++, x += dbx, y += dby, z += dbz)
{
coords.Append(x);
coords.Append(y);
coords.Append(z);
}
return;
}
if (w == 1 && h == 1)
{
for (int i = 0; i < d; i++, x += dcx, y += dcy, z += dcz)
{
coords.Append(x);
coords.Append(y);
coords.Append(z);
}
return;
}
int ax2 = ax/2, ay2 = ay/2, az2 = az/2;
int bx2 = bx/2, by2 = by/2, bz2 = bz/2;
int cx2 = cx/2, cy2 = cy/2, cz2 = cz/2;
int w2 = std::abs(ax2 + ay2 + az2);
int h2 = std::abs(bx2 + by2 + bz2);
int d2 = std::abs(cx2 + cy2 + cz2);
// prefer even steps
if ((w2 & 0x1) && (w > 2))
{
ax2 += dax, ay2 += day, az2 += daz;
}
if ((h2 & 0x1) && (h > 2))
{
bx2 += dbx, by2 += dby, bz2 += dbz;
}
if ((d2 & 0x1) && (d > 2))
{
cx2 += dcx, cy2 += dcy, cz2 += dcz;
}
// wide case, split in w only
if ((2*w > 3*h) && (2*w > 3*d))
{
HilbertSfc3D(x, y, z,
ax2, ay2, az2,
bx, by, bz,
cx, cy, cz, coords);
HilbertSfc3D(x+ax2, y+ay2, z+az2,
ax-ax2, ay-ay2, az-az2,
bx, by, bz,
cx, cy, cz, coords);
}
// do not split in d
else if (3*h > 4*d)
{
HilbertSfc3D(x, y, z,
bx2, by2, bz2,
cx, cy, cz,
ax2, ay2, az2, coords);
HilbertSfc3D(x+bx2, y+by2, z+bz2,
ax, ay, az,
bx-bx2, by-by2, bz-bz2,
cx, cy, cz, coords);
HilbertSfc3D(x+(ax-dax)+(bx2-dbx),
y+(ay-day)+(by2-dby),
z+(az-daz)+(bz2-dbz),
-bx2, -by2, -bz2,
cx, cy, cz,
-(ax-ax2), -(ay-ay2), -(az-az2), coords);
}
// do not split in h
else if (3*d > 4*h)
{
HilbertSfc3D(x, y, z,
cx2, cy2, cz2,
ax2, ay2, az2,
bx, by, bz, coords);
HilbertSfc3D(x+cx2, y+cy2, z+cz2,
ax, ay, az,
bx, by, bz,
cx-cx2, cy-cy2, cz-cz2, coords);
HilbertSfc3D(x+(ax-dax)+(cx2-dcx),
y+(ay-day)+(cy2-dcy),
z+(az-daz)+(cz2-dcz),
-cx2, -cy2, -cz2,
-(ax-ax2), -(ay-ay2), -(az-az2),
bx, by, bz, coords);
}
// regular case, split in all w/h/d
else
{
HilbertSfc3D(x, y, z,
bx2, by2, bz2,
cx2, cy2, cz2,
ax2, ay2, az2, coords);
HilbertSfc3D(x+bx2, y+by2, z+bz2,
cx, cy, cz,
ax2, ay2, az2,
bx-bx2, by-by2, bz-bz2, coords);
HilbertSfc3D(x+(bx2-dbx)+(cx-dcx),
y+(by2-dby)+(cy-dcy),
z+(bz2-dbz)+(cz-dcz),
ax, ay, az,
-bx2, -by2, -bz2,
-(cx-cx2), -(cy-cy2), -(cz-cz2), coords);
HilbertSfc3D(x+(ax-dax)+bx2+(cx-dcx),
y+(ay-day)+by2+(cy-dcy),
z+(az-daz)+bz2+(cz-dcz),
-cx, -cy, -cz,
-(ax-ax2), -(ay-ay2), -(az-az2),
bx-bx2, by-by2, bz-bz2, coords);
HilbertSfc3D(x+(ax-dax)+(bx2-dbx),
y+(ay-day)+(by2-dby),
z+(az-daz)+(bz2-dbz),
-bx2, -by2, -bz2,
cx2, cy2, cz2,
-(ax-ax2), -(ay-ay2), -(az-az2), coords);
}
}
void NCMesh::GridSfcOrdering2D(int width, int height, Array<int> &coords)
{
coords.SetSize(0);
coords.Reserve(2*width*height);
if (width >= height)
{
HilbertSfc2D(0, 0, width, 0, 0, height, coords);
}
else
{
HilbertSfc2D(0, 0, 0, height, width, 0, coords);
}
}
void NCMesh::GridSfcOrdering3D(int width, int height, int depth,
Array<int> &coords)
{
coords.SetSize(0);
coords.Reserve(3*width*height*depth);
if (width >= height && width >= depth)
{
HilbertSfc3D(0, 0, 0,
width, 0, 0,
0, height, 0,
0, 0, depth, coords);
}
else if (height >= width && height >= depth)
{
HilbertSfc3D(0, 0, 0,
0, height, 0,
width, 0, 0,
0, 0, depth, coords);
}
else // depth >= width && depth >= height
{
HilbertSfc3D(0, 0, 0,
0, 0, depth,
width, 0, 0,
0, height, 0, coords);
}
}
//// Utility ///////////////////////////////////////////////////////////////////
void NCMesh::GetEdgeVertices(const MeshId &edge_id, int vert_index[2],
@@ -3911,7 +3420,7 @@ void NCMesh::PrintCoarseElements(std::ostream &out) const
// print the hierarchy recursively
int coarse_id = leaf_elements.Size();
for (int i = 0; i < root_state.Size(); i++)
for (int i = 0; i < root_count; i++)
{
PrintElements(out, i, coarse_id);
}
@@ -3991,7 +3500,7 @@ void NCMesh::LoadCoarseElements(std::istream &input)
index_map = -1;
// copy roots, they need to be at the beginning of 'elements'
int root_count = 0;
root_count = 0;
for (elem_iterator el = tmp_elements.begin(); el != tmp_elements.end(); ++el)
{
if (el->parent == -1)
@@ -4024,8 +3533,6 @@ void NCMesh::LoadCoarseElements(std::istream &input)
// set the Iso flag (must be false if there are 3D aniso refinements)
Iso = iso;
InitRootState(root_count);
Update();
}
@@ -4075,7 +3582,6 @@ long NCMesh::MemoryUsage() const
faces.MemoryUsage() +
elements.MemoryUsage() +
free_element_ids.MemoryUsage() +
root_state.MemoryUsage() +
top_vertex_pos.MemoryUsage() +
leaf_elements.MemoryUsage() +
vertex_nodeId.MemoryUsage() +
@@ -4098,7 +3604,6 @@ int NCMesh::PrintMemoryDetail() const
mfem::out << elements.MemoryUsage() << " elements\n"
<< free_element_ids.MemoryUsage() << " free_element_ids\n"
<< root_state.MemoryUsage() << " root_state\n"
<< top_vertex_pos.MemoryUsage() << " top_vertex_pos\n"
<< leaf_elements.MemoryUsage() << " leaf_elements\n"
<< vertex_nodeId.MemoryUsage() << " vertex_nodeId\n"
@@ -4141,8 +3646,7 @@ void NCMesh::PrintStats(std::ostream &out) const
free_element_ids.MemoryUsage())/MiB << " MiB ]\n"
" free " << std::setw(9)
<< free_element_ids.Size() << "\n"
" number of root elements : " << std::setw(9)
<< root_state.Size() << "\n"
" number of root elements : " << std::setw(9) << root_count << "\n"
" number of leaf elements : " << std::setw(9)
<< leaf_elements.Size() << "\n"
" number of vertices : " << std::setw(9)
@@ -4171,31 +3675,6 @@ void NCMesh::PrintStats(std::ostream &out) const
}
#ifdef MFEM_DEBUG
void NCMesh::DebugLeafOrder(std::ostream &out) const
{
tmp_vertex = new TmpVertex[nodes.NumIds()];
for (int i = 0; i < leaf_elements.Size(); i++)
{
const Element* elem = &elements[leaf_elements[i]];
for (int j = 0; j < Dim; j++)
{
double sum = 0.0;
int count = 0;
for (int k = 0; k < 8; k++)
{
if (elem->node[k] >= 0)
{
sum += CalcVertexPos(elem->node[k])[j];
count++;
}
}
out << sum / count << " ";
}
out << "\n";
}
delete [] tmp_vertex;
}
void NCMesh::DebugDump(std::ostream &out) const
{
// dump nodes
+4 -44
View File
@@ -60,13 +60,6 @@ struct CoarseFineTransformations
Array<Embedding> embeddings;
const DenseTensor &GetPointMatrices(Geometry::Type geom) const;
void GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine,
Array<int> &coarse_to_ref_type,
Table &ref_type_to_matrix,
Array<Geometry::Type> &ref_type_to_geom) const;
void Clear() { point_matrices.clear(); embeddings.DeleteAll(); }
long MemoryUsage() const;
};
@@ -252,24 +245,6 @@ public:
void ClearTransforms();
// grid ordering
/** Return a space filling curve for a rectangular grid of elements.
Implemented is a generalized Hilbert curve for arbitrary grid dimensions.
If the width is odd, height should be odd too, otherwise one diagonal
(vertex-neighbor) step cannot be avoided in the curve. Even dimensions
are recommended. */
static void GridSfcOrdering2D(int width, int height,
Array<int> &coords);
/** Return a space filling curve for a 3D rectangular grid of elements.
The Hilbert-curve-like algorithm works well for even dimensions. For odd
width/height/depth it tends to produce some diagonal (edge-neighbor)
steps. Even dimensions are recommended. */
static void GridSfcOrdering3D(int width, int height, int depth,
Array<int> &coords);
// utility
/// Return Mesh vertex indices of an edge identified by 'edge_id'.
@@ -392,7 +367,7 @@ protected: // implementation
Face() : attribute(-1), index(-1) { elem[0] = elem[1] = -1; }
bool Boundary() const { return attribute >= 0; }
bool Unused() const { return elem[0] < 0 && elem[1] < 0; }
bool Unused() const { return elem[0] < 0 && elem[1] < 0;}
// add or remove an element from the 'elem[2]' array
void RegisterElement(int e);
@@ -431,12 +406,10 @@ protected: // implementation
BlockArray<Element> elements; // storage for all Elements
Array<int> free_element_ids; // unused element ids - indices into 'elements'
/** Initial traversal state (~ element orientation) for each root element
NOTE: M = root_state.Size() is the number of root elements.
NOTE: the first M items of 'elements' is the coarse mesh. */
Array<int> root_state;
// the first 'root_count' entries of 'elements' is the coarse mesh
int root_count;
/// coordinates of top-level vertices (organized as triples)
// coordinates of top-level vertices (organized as triples)
Array<double> top_vertex_pos;
typedef HashTable<Node>::iterator node_iterator;
@@ -477,12 +450,6 @@ protected: // implementation
virtual void AssignLeafIndices();
/** Try to find a space-filling curve friendly orientation of the root
elements: set 'root_state' based on the ordering of coarse elements.
Note that the coarse mesh itself must be ordered as an SFC by e.g.
Mesh::GetGeckoElementReordering. */
void InitRootState(int root_count);
virtual bool IsGhost(const Element &el) const { return false; }
virtual int GetNumGhostElements() const { return 0; }
virtual int GetNumGhostVertices() const { return 0; }
@@ -561,12 +528,6 @@ protected: // implementation
void CollectDerefinements(int elem, Array<Connection> &list);
/// Return el.node[index] correctly, even if the element is refined.
int RetrieveNode(const Element &el, int index);
/// Extended version of find_node: works if 'el' is refined; optional abort.
int FindNodeExt(const Element &el, int node, bool abort = false);
// face/edge lists
@@ -776,7 +737,6 @@ protected: // implementation
#ifdef MFEM_DEBUG
public:
void DebugLeafOrder(std::ostream &out) const;
void DebugDump(std::ostream &out) const;
#endif
+2 -6
View File
@@ -1668,14 +1668,12 @@ void NURBSExtension::ConnectBoundaries()
void NURBSExtension::ConnectBoundaries2D(int bnd0, int bnd1)
{
int idx0 = -1, idx1 = -1;
int idx0, idx1;
for (int b = 0; b < GetNBP(); b++)
{
if (bnd0 == patchTopo->GetBdrAttribute(b)) { idx0 = b; }
if (bnd1 == patchTopo->GetBdrAttribute(b)) { idx1 = b; }
}
MFEM_VERIFY(idx0 != -1,"Bdr 0 not found");
MFEM_VERIFY(idx1 != -1,"Bdr 1 not found");
NURBSPatchMap p2g0(this);
NURBSPatchMap p2g1(this);
@@ -1744,14 +1742,12 @@ void NURBSExtension::ConnectBoundaries2D(int bnd0, int bnd1)
void NURBSExtension::ConnectBoundaries3D(int bnd0, int bnd1)
{
int idx0 = -1, idx1 = -1;
int idx0, idx1;
for (int b = 0; b < GetNBP(); b++)
{
if (bnd0 == patchTopo->GetBdrAttribute(b)) { idx0 = b; }
if (bnd1 == patchTopo->GetBdrAttribute(b)) { idx1 = b; }
}
MFEM_VERIFY(idx0 != -1,"Bdr 0 not found");
MFEM_VERIFY(idx1 != -1,"Bdr 1 not found");
NURBSPatchMap p2g0(this);
NURBSPatchMap p2g1(this);
+22 -41
View File
@@ -2988,8 +2988,6 @@ void ParMesh::NonconformingRefinement(const Array<Refinement> &refinements,
"serial Mesh)");
}
DeleteFaceNbrData();
// NOTE: no check of !refinements.Size(), in parallel we would have to reduce
// do the refinements
@@ -3110,22 +3108,6 @@ void ParMesh::Rebalance()
last_operation = Mesh::REBALANCE;
sequence++;
// Make sure the Nodes use a ParFiniteElementSpace
if (Nodes && dynamic_cast<ParFiniteElementSpace*>(Nodes->FESpace()) == NULL)
{
ParFiniteElementSpace *pfes =
new ParFiniteElementSpace(*Nodes->FESpace(), *this);
ParGridFunction *new_nodes = new ParGridFunction(pfes);
*new_nodes = *Nodes;
if (Nodes->OwnFEC())
{
new_nodes->MakeOwner(Nodes->OwnFEC());
Nodes->MakeOwner(NULL); // takes away ownership of 'fec' and 'fes'
delete Nodes->FESpace();
}
delete Nodes;
Nodes = new_nodes;
}
UpdateNodes();
}
@@ -3146,16 +3128,16 @@ void ParMesh::RefineGroups(const DSTable &v_to_v, int *middle)
int *I_group_svert, *J_group_svert;
int *I_group_sedge, *J_group_sedge;
I_group_svert = mfem::New<int>(GetNGroups()+1);
I_group_sedge = mfem::New<int>(GetNGroups()+1);
I_group_svert = new int[GetNGroups()+1];
I_group_sedge = new int[GetNGroups()+1];
I_group_svert[0] = I_group_svert[1] = 0;
I_group_sedge[0] = I_group_sedge[1] = 0;
// overestimate the size of the J arrays
J_group_svert = mfem::New<int>(group_svert.Size_of_connections()
+ group_sedge.Size_of_connections());
J_group_sedge = mfem::New<int>(2*group_sedge.Size_of_connections());
J_group_svert = new int[group_svert.Size_of_connections()
+ group_sedge.Size_of_connections()];
J_group_sedge = new int[2*group_sedge.Size_of_connections()];
for (int group = 0; group < GetNGroups()-1; group++)
{
@@ -3393,16 +3375,16 @@ void ParMesh::UniformRefineGroups2D(int old_nv)
int *I_group_svert, *J_group_svert;
int *I_group_sedge, *J_group_sedge;
I_group_svert = mfem::New<int>(GetNGroups());
I_group_sedge = mfem::New<int>(GetNGroups());
I_group_svert = new int[GetNGroups()];
I_group_sedge = new int[GetNGroups()];
I_group_svert[0] = 0;
I_group_sedge[0] = 0;
// compute the size of the J arrays
J_group_svert = mfem::New<int>(group_svert.Size_of_connections()
+ group_sedge.Size_of_connections());
J_group_sedge = mfem::New<int>(2*group_sedge.Size_of_connections());
J_group_svert = new int[group_svert.Size_of_connections()
+ group_sedge.Size_of_connections()];
J_group_sedge = new int[2*group_sedge.Size_of_connections()];
for (int group = 0; group < GetNGroups()-1; group++)
{
@@ -3451,10 +3433,10 @@ void ParMesh::UniformRefineGroups3D(int old_nv, int old_nedges,
int *I_group_stria, *J_group_stria;
int *I_group_squad, *J_group_squad;
I_group_svert = mfem::New<int>(GetNGroups());
I_group_sedge = mfem::New<int>(GetNGroups());
I_group_stria = mfem::New<int>(GetNGroups());
I_group_squad = mfem::New<int>(GetNGroups());
I_group_svert = new int[GetNGroups()];
I_group_sedge = new int[GetNGroups()];
I_group_stria = new int[GetNGroups()];
I_group_squad = new int[GetNGroups()];
I_group_svert[0] = 0;
I_group_sedge[0] = 0;
@@ -3462,14 +3444,14 @@ void ParMesh::UniformRefineGroups3D(int old_nv, int old_nedges,
I_group_squad[0] = 0;
// compute the size of the J arrays
J_group_svert = mfem::New<int>(group_svert.Size_of_connections()
+ group_sedge.Size_of_connections()
+ group_squad.Size_of_connections());
J_group_sedge = mfem::New<int>(2*group_sedge.Size_of_connections()
+ 3*group_stria.Size_of_connections()
+ 4*group_squad.Size_of_connections());
J_group_stria = mfem::New<int>(4*group_stria.Size_of_connections());
J_group_squad = mfem::New<int>(4*group_squad.Size_of_connections());
J_group_svert = new int[group_svert.Size_of_connections()
+ group_sedge.Size_of_connections()
+ group_squad.Size_of_connections()];
J_group_sedge = new int[2*group_sedge.Size_of_connections()
+ 3*group_stria.Size_of_connections()
+ 4*group_squad.Size_of_connections()];
J_group_stria = new int[4*group_stria.Size_of_connections()];
J_group_squad = new int[4*group_squad.Size_of_connections()];
const int oface = old_nv + old_nedges;
@@ -3632,7 +3614,6 @@ void ParMesh::UniformRefinement3D()
// update the groups
UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
f2qf.Size() ? &f2qf : NULL);
delete faces_tbl;
UpdateNodes();
}
+2 -6
View File
@@ -921,10 +921,6 @@ void ParNCMesh::GetConformingSharedStructures(ParMesh &pmesh)
pmesh.group_stria.ShiftUpI();
// create shared_edges
for (int i = 0; i < pmesh.shared_edges.Size(); i++)
{
delete pmesh.shared_edges[i];
}
pmesh.shared_edges.SetSize(pmesh.sedge_ledge.Size());
for (int i = 0; i < pmesh.shared_edges.Size(); i++)
{
@@ -1296,7 +1292,7 @@ void ParNCMesh::Prune()
}
// derefine subtrees whose leaves are all unneeded
for (int i = 0; i < root_state.Size(); i++)
for (int i = 0; i < root_count; i++)
{
if (PruneTree(i)) { DerefineElement(i); }
}
@@ -2106,7 +2102,7 @@ void ParNCMesh::ElementSet::Encode(const Array<int> &elements)
// Each refinement tree that contains at least one element from the set
// is encoded as HEADER + TREE, where HEADER is the root element number and
// TREE is the output of EncodeTree().
for (int i = 0; i < ncmesh->root_state.Size(); i++)
for (int i = 0; i < ncmesh->root_count; i++)
{
if (ncmesh->elements[i].flag)
{
-1
View File
@@ -15,7 +15,6 @@
#include "config/config.hpp"
#include "general/error.hpp"
#include "general/device.hpp"
#include "general/array.hpp"
#include "general/sets.hpp"
#include "general/hash.hpp"
+3 -39
View File
@@ -56,44 +56,9 @@ RT_FESpace::~RT_FESpace()
delete FEC_;
}
void VisualizeMesh(socketstream &sock, const char *vishost, int visport,
Mesh &mesh, const char *title,
int x, int y, int w, int h, const char * keys, bool vec)
{
bool newly_opened = false;
int connection_failed;
do
{
if (!sock.is_open() || !sock)
{
sock.open(vishost, visport);
sock.precision(8);
newly_opened = true;
}
sock << "solution\n";
mesh.Print(sock);
if (newly_opened)
{
sock << "window_title '" << title << "'\n"
<< "window_geometry "
<< x << " " << y << " " << w << " " << h << "\n";
if ( keys ) { sock << "keys " << keys << "\n"; }
else { sock << "keys maaAc\n"; }
if ( vec ) { sock << "vvv"; }
sock << endl;
}
connection_failed = !sock && !newly_opened;
}
while (connection_failed);
}
void VisualizeField(socketstream &sock, const char *vishost, int visport,
GridFunction &gf, const char *title,
int x, int y, int w, int h, const char * keys, bool vec)
int x, int y, int w, int h, bool vec)
{
Mesh &mesh = *gf.FESpace()->GetMesh();
@@ -117,9 +82,8 @@ void VisualizeField(socketstream &sock, const char *vishost, int visport,
{
sock << "window_title '" << title << "'\n"
<< "window_geometry "
<< x << " " << y << " " << w << " " << h << "\n";
if ( keys ) { sock << "keys " << keys << "\n"; }
else { sock << "keys maaAc\n"; }
<< x << " " << y << " " << w << " " << h << "\n"
<< "keys maaAc";
if ( vec ) { sock << "vvv"; }
sock << endl;
}
+1 -9
View File
@@ -66,21 +66,13 @@ private:
};
/// Visualize the given mesh object, using a GLVis server on the
/// specified host and port. Set the visualization window title, and optionally,
/// its geometry.
void VisualizeMesh(socketstream &sock, const char *vishost, int visport,
Mesh &mesh, const char *title,
int x = 0, int y = 0, int w = 400, int h = 400,
const char *keys = NULL);
/// Visualize the given grid function, using a GLVis server on the
/// specified host and port. Set the visualization window title, and optionally,
/// its geometry.
void VisualizeField(socketstream &sock, const char *vishost, int visport,
GridFunction &gf, const char *title,
int x = 0, int y = 0, int w = 400, int h = 400,
const char *keys = NULL, bool vec = false);
bool vec = false);
} // namespace miniapps
+3 -52
View File
@@ -261,57 +261,9 @@ DivergenceFreeProjector::Update()
this->IrrotationalProjector::Update();
}
void VisualizeMesh(socketstream &sock, const char *vishost, int visport,
ParMesh &pmesh, const char *title,
int x, int y, int w, int h, const char *keys, bool vec)
{
MPI_Comm comm = pmesh.GetComm();
int num_procs, myid;
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
bool newly_opened = false;
int connection_failed;
do
{
if (myid == 0)
{
if (!sock.is_open() || !sock)
{
sock.open(vishost, visport);
sock.precision(8);
newly_opened = true;
}
sock << "solution\n";
}
pmesh.PrintAsOne(sock);
if (myid == 0 && newly_opened)
{
sock << "window_title '" << title << "'\n"
<< "window_geometry "
<< x << " " << y << " " << w << " " << h << "\n";
if ( keys ) { sock << "keys " << keys << "\n"; }
else { sock << "keys maaAc"; }
if ( vec ) { sock << "vvv"; }
sock << endl;
}
if (myid == 0)
{
connection_failed = !sock && !newly_opened;
}
MPI_Bcast(&connection_failed, 1, MPI_INT, 0, comm);
}
while (connection_failed);
}
void VisualizeField(socketstream &sock, const char *vishost, int visport,
ParGridFunction &gf, const char *title,
int x, int y, int w, int h, const char *keys, bool vec)
int x, int y, int w, int h, bool vec)
{
ParMesh &pmesh = *gf.ParFESpace()->GetParMesh();
MPI_Comm comm = pmesh.GetComm();
@@ -343,9 +295,8 @@ void VisualizeField(socketstream &sock, const char *vishost, int visport,
{
sock << "window_title '" << title << "'\n"
<< "window_geometry "
<< x << " " << y << " " << w << " " << h << "\n";
if ( keys ) { sock << "keys " << keys << "\n"; }
else { sock << "keys maaAc"; }
<< x << " " << y << " " << w << " " << h << "\n"
<< "keys maaAc";
if ( vec ) { sock << "vvv"; }
sock << endl;
}
+1 -9
View File
@@ -185,21 +185,13 @@ public:
};
/// Visualize the given parallel mesh object, using a GLVis server on the
/// specified host and port. Set the visualization window title, and optionally,
/// its geometry.
void VisualizeMesh(socketstream &sock, const char *vishost, int visport,
ParMesh &pmesh, const char *title,
int x = 0, int y = 0, int w = 400, int h = 400,
const char *keys = NULL);
/// Visualize the given parallel grid function, using a GLVis server on the
/// specified host and port. Set the visualization window title, and optionally,
/// its geometry.
void VisualizeField(socketstream &sock, const char *vishost, int visport,
ParGridFunction &gf, const char *title,
int x = 0, int y = 0, int w = 400, int h = 400,
const char *keys = NULL, bool vec = false);
bool vec = false);
} // namespace miniapps
+4
View File
@@ -0,0 +1,4 @@
build
lib
*.mesh
*.gf
+35
View File
@@ -0,0 +1,35 @@
# Discontinuous Galerkin MFEM mini-app using partial assembly
This mini-app demonstrates using partial-assembly to solve hyperbolic
conservation laws using discontinuous Galerkin methods and explicit
time integration.
The main object is a `PartialAssembly` object, which provides:
- local interpolation and differentiation operators (including at faces)
- face access to metric terms at quadrature data
On top of this object, there are several operators that are provided
(but more are possible). All of these operators allow for a "coefficient"
to be evaluated at quadrature points, which is referred to as D. These
operators are:
- `BtDB`, which represents mass or source terms with coefficient `D`
- `GtDB`, which represents dot product with the gradient of test functions
- `BtDB_face`, which represents integrating against test functions on faces
Using any of these operators simply requires templating on a class `D` which
provides an operator to evaluate the coefficient at a quadrature point.
There is a `ConservationLaw` object which is built on these three operators.
Given a flux function and numerical flux function, it will assemble the
corresponding DG residual.
Examples are provided in the `apps` directory for solving the scalar advection
equation, Burgers' equation and the Euler equations of gas dynamics.
This mini-app is still incomplete. Improvements are needed for:
[ ] Handing of mixed meshes
[ ] AMR and non-conforming meshes
[ ] Second-order operators and viscous terms

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