Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c50ef26b1b | ||
|
|
189b609c62 | ||
|
|
0799a89ede | ||
|
|
d1914c96b6 | ||
|
|
f8d2377984 | ||
|
|
fdee495d9f | ||
|
|
fddad9c348 | ||
|
|
36b6e73789 | ||
|
|
936c69b951 | ||
|
|
fd109248a2 | ||
|
|
e0918b2c71 | ||
|
|
1881dd8885 | ||
|
|
f11adaef3e | ||
|
|
e64afc006a | ||
|
|
f123c55d1f | ||
|
|
875cf632f7 | ||
|
|
4822ca6a91 | ||
|
|
e4fc982466 | ||
|
|
270df97348 | ||
|
|
ab6b27db4a | ||
|
|
97621f6a19 | ||
|
|
e561888af1 | ||
|
|
d1bee894ce | ||
|
|
522f60d587 | ||
|
|
654e4ef540 | ||
|
|
c2c4d0d4c0 |
+3
-6
@@ -82,9 +82,9 @@ examples/ex20.dat
|
||||
examples/ex20p_?????.dat
|
||||
examples/gnuplot_ex20.inp
|
||||
examples/gnuplot_ex20p.inp
|
||||
examples/ex21*.mesh
|
||||
examples/ex21*.sol
|
||||
examples/ex21p_*.*
|
||||
examples/ex22*.mesh
|
||||
examples/ex22*.sol
|
||||
examples/ex22p_*.*
|
||||
|
||||
examples/sundials/ex9
|
||||
examples/sundials/ex1[06]
|
||||
@@ -183,6 +183,3 @@ miniapps/nurbs/Example1*
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
|
||||
# VPATH builds
|
||||
build-*/*
|
||||
|
||||
@@ -8,14 +8,22 @@
|
||||
http://mfem.org
|
||||
|
||||
|
||||
Version 4.0.1 (development)
|
||||
===========================
|
||||
Version 4.0-RC2, Apr 24, 2019
|
||||
=============================
|
||||
|
||||
Requirements and Limitations
|
||||
----------------------------
|
||||
- This is a release candidate for mfem-4.0.
|
||||
- Use at your own risk -- not everything will work and the API may change.
|
||||
- We are looking for feedback from friendly users.
|
||||
- Unlike previous MFEM releases, this version requires a C++11 compiler.
|
||||
|
||||
Version 4.0, released on May 24, 2019
|
||||
=====================================
|
||||
|
||||
Unlike previous MFEM releases, this version requires a C++11 compiler.
|
||||
- GPU-related limitations:
|
||||
* Hypre preconditioners are not yet available in GPU mode.
|
||||
* Only constant coefficients are currently supported on GPUs.
|
||||
* Full-assembly (on device), element assembly, and matrix-free bilinear forms
|
||||
are not supported yet. Element batching is currently ignored.
|
||||
* Partial assembly kernels are not implemented yet for simplices.
|
||||
|
||||
GPU support
|
||||
-----------
|
||||
@@ -26,7 +34,7 @@ GPU support
|
||||
seamlessly with a new lightweight device/host memory manager. The kernels can
|
||||
be implemented either in OCCA, or as a simple wrapper around for-loops, which
|
||||
can then be dispatched to RAJA and native backends. See the files forall.hpp
|
||||
and mem_manager.hpp in the general/ directory for more details.
|
||||
and mem_manager.hpp in the general/ directory.
|
||||
|
||||
- Several of the MFEM example codes (ex1, ex1p, ex6, and ex6p) can now take
|
||||
advantage of GPU acceleration with the backend selectable at runtime. Many of
|
||||
@@ -34,44 +42,26 @@ GPU support
|
||||
bilinear forms) have been extended to take advantage of kernel acceleration by
|
||||
simply replacing loops with the MFEM_FORALL() macro.
|
||||
|
||||
- In addition to native CUDA kernels, the library currently supports OCCA, RAJA
|
||||
and OpenMP kernels, which could be mixed and matched in different parts of the
|
||||
same application. We plan on adding support for more programming models and
|
||||
devices in the future, without the need for significant modifications in user
|
||||
code. The list of current backends is: "occa-cuda", "raja-cuda", "cuda",
|
||||
"occa-omp", "raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
|
||||
|
||||
- GPU-related limitations:
|
||||
* Hypre preconditioners are not yet available in GPU mode, and in particular
|
||||
hypre must be built in CPU mode.
|
||||
* Only constant coefficients are currently supported on GPUs.
|
||||
* Optimized element assembly, and matrix-free bilinear forms are not
|
||||
implemented yet. Element batching is currently ignored.
|
||||
* In device mode, full assembly is performed on the host (but the matvec
|
||||
action is performed on the device).
|
||||
* Partial assembly kernels are not implemented yet for simplices.
|
||||
- In addition to pure CUDA, the library currently supports OCCA, RAJA and OpenMP
|
||||
kernels, which could be mixed and matched in different parts of the same
|
||||
application. We plan on adding support for more programming models and devices
|
||||
in the future, without the need for significant modifications in user code.
|
||||
The list of current backends is: "occa-cuda", "raja-cuda", "cuda", "occa-omp",
|
||||
"raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
|
||||
- Partial assembled finite element operators are now available in the core
|
||||
library, based on the new classes PABilinearFormExtension, ElementRestriction,
|
||||
DofToQuad and GeometricFactors (associated with the classes BilinearForm,
|
||||
FiniteElementSpace, FiniteElement and Mesh, respectively). The kernels for
|
||||
partial assembled Setup/Assembly and Action/Mult are implemented in the
|
||||
BilinearFormIntegrator methods AssemblePA and AddMultPA.
|
||||
|
||||
- Added support for a general "low-order refined"-to-"high-order" transfer of
|
||||
GridFunction data from a "low-order refined" (LOR) space defined on a refined
|
||||
mesh to a "high-order" (HO) finite element space defined on a coarse mesh. See
|
||||
the new classes InterpolationGridTransfer and L2ProjectionGridTransfer and the
|
||||
new LOR Transfer miniapp: miniapps/tools/lor-transfer.cpp.
|
||||
|
||||
- Added support for derefinement of vector (RT + ND) spaces.
|
||||
|
||||
- Added element flux, and flux energy computation in class ElasticityIntegrator,
|
||||
allowing for the use of Zienkiewicz-Zhu type error estimators with the
|
||||
integrator. For an illustration of this addition, see the new Example 21.
|
||||
|
||||
- Added support for derefinement of vector (RT + ND) spaces.
|
||||
integrator. For an illustration of this addition, see the new Example 22.
|
||||
|
||||
- Added a variety of coefficients which are sums or products of existing
|
||||
coefficients as well as grid function coefficients which return the
|
||||
@@ -83,13 +73,13 @@ Support for wedge elements and meshes with mixed element types
|
||||
type PRISM) which have two triangular faces and three quadrilateral faces.
|
||||
Several examples of such meshes can be found in the data/ directory.
|
||||
|
||||
- Added H1 and L2 finite elements of arbitrary order for Wedge elements.
|
||||
|
||||
- Added support for mixed meshes containing triangles and quadrilaterals in 2D
|
||||
or tetrahedra, wedges, and hexahedra in 3D. This includes support for uniform
|
||||
refinement of such meshes. Several examples of such meshes can be found in the
|
||||
data/ directory.
|
||||
|
||||
- Added H1 and L2 finite elements of arbitrary order for Wedge elements.
|
||||
|
||||
- Added support for reading and writing linear and quadratic meshes containing
|
||||
wedge elements in VTK mesh format. Several examples of such meshes can be
|
||||
found in the data/ directory.
|
||||
@@ -110,10 +100,6 @@ Other meshing improvements
|
||||
This guarantees that the shape regularity of the elements will be preserved
|
||||
under refinement.
|
||||
|
||||
- The TMOP mesh optimization algorithms were extended to support user-defined
|
||||
space-dependent limiting terms. Improved the TMOP objective functions by more
|
||||
accurate normalization of the different terms.
|
||||
|
||||
- Added support for parallel communication groups on non-conforming meshes.
|
||||
|
||||
- Improved parallel partitioning of non-conforming meshes. If the coarse mesh
|
||||
@@ -127,6 +113,10 @@ Other meshing improvements
|
||||
- Added support for reading linear and quadratic 2D quadrilateral and triangular
|
||||
Cubit meshes.
|
||||
|
||||
- The TMOP mesh optimization algorithms were extended to support user-defined
|
||||
space-dependent limiting terms. Improved the TMOP objective functions by more
|
||||
accurate normalization of the different terms.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new meshing miniapp, Toroid, which can produce a variety of torus
|
||||
@@ -142,7 +132,7 @@ New and updated examples and miniapps
|
||||
from a Hamiltonian. The example demonstrates the use of the variable order,
|
||||
symplectic integration algorithm implemented in class SIAVSolver.
|
||||
|
||||
- Added a new example, Example 21/21p, that illustrates the use of AMR to solve
|
||||
- Added a new example, Example 22/22p, that illustrates the use of AMR to solve
|
||||
a linear elasticity problem. This is an extension of Example 2/2p.
|
||||
|
||||
New and improved solvers and preconditioners
|
||||
@@ -154,24 +144,21 @@ New and improved solvers and preconditioners
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added unit tests based on the Catch++ library in the test/ directory.
|
||||
- In SparseMatrix added the option to perform MultTranspose() by matvec with
|
||||
computed and stored transpose matrix. This is required for deterministic
|
||||
results when using devices such as CUDA and OpenMP.
|
||||
|
||||
- Added unit tests based on the Catch++ library.
|
||||
|
||||
- Renamed the option MFEM_USE_OPENMP to MFEM_USE_LEGACY_OPENMP. This legacy
|
||||
option is deprecated and planned for removal in a future release. The original
|
||||
option name, MFEM_USE_OPENMP, is now used to enable the new OpenMP backends in
|
||||
the new kernels.
|
||||
|
||||
- In SparseMatrix added the option to perform MultTranspose() by matvec with
|
||||
computed and stored transpose matrix. This is required for deterministic
|
||||
results when using devices such as CUDA and OpenMP.
|
||||
|
||||
- Altered the way FGMRES counts its iterations so that it matches GMRES.
|
||||
|
||||
- Various other simplifications, extensions, and bugfixes in the code.
|
||||
|
||||
- Construct abstract parallel rectangular truedof-to-truedof operators via
|
||||
Operator::FormDiscreteOperator().
|
||||
|
||||
API changes
|
||||
-----------
|
||||
- In multiple places, use Geometry::Type instead of int, where appropriate.
|
||||
|
||||
+6
-5
@@ -50,7 +50,7 @@ project(mfem NONE)
|
||||
# Current version of MFEM, see also `makefile`.
|
||||
# mfem_VERSION = (string)
|
||||
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
|
||||
set(${PROJECT_NAME}_VERSION 4.0.1)
|
||||
set(${PROJECT_NAME}_VERSION 3.4.1)
|
||||
|
||||
# Prohibit in-source build
|
||||
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
|
||||
@@ -286,6 +286,7 @@ if (MFEM_USE_CUDA)
|
||||
set(CUDA_CCBIN_COMPILER ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
string(APPEND CMAKE_CUDA_FLAGS " -ccbin ${CUDA_CCBIN_COMPILER}")
|
||||
set(MFEM_USE_MM YES CACHE BOOL "Enable MFEM's memory manager" FORCE)
|
||||
endif()
|
||||
|
||||
# OCCA
|
||||
@@ -395,11 +396,11 @@ endif()
|
||||
set_target_properties(mfem PROPERTIES VERSION "${mfem_VERSION}")
|
||||
set_target_properties(mfem PROPERTIES SOVERSION "${mfem_VERSION}")
|
||||
|
||||
# If building out-of-source, define MFEM_CONFIG_FILE to point to the config file
|
||||
# inside the build directory.
|
||||
# If building out-of-source, define MFEM_BUILD_DIR to point to the build
|
||||
# directory.
|
||||
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
target_compile_definitions(mfem PRIVATE
|
||||
"MFEM_CONFIG_FILE=\"${PROJECT_BINARY_DIR}/config/_config.hpp\"")
|
||||
"MFEM_BUILD_DIR=${PROJECT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
# Generate configuration file in the build directory: config/_config.hpp.
|
||||
@@ -415,7 +416,7 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
"Writing substitute header --> \"${Header}\"")
|
||||
file(WRITE "${PROJECT_BINARY_DIR}/${Header}"
|
||||
"// Auto-generated file.
|
||||
#define MFEM_CONFIG_FILE \"${PROJECT_BINARY_DIR}/config/_config.hpp\"
|
||||
#define MFEM_BUILD_DIR ${PROJECT_BINARY_DIR}
|
||||
#include \"${PROJECT_SOURCE_DIR}/${Header}\"
|
||||
")
|
||||
# This version will be installed in the top include directory:
|
||||
|
||||
@@ -404,12 +404,18 @@ MFEM_USE_PUMI = YES/NO
|
||||
models and effectively supports automated adaptive analysis. PUMI enables
|
||||
support for parallel unstructured mesh modifications in MFEM.
|
||||
|
||||
MFEM_USE_MM = YES/NO
|
||||
Enables support for the MFEM's memory manager (MM), which is required to
|
||||
support devices with different memory spaces. This option is required when
|
||||
CUDA support is enabled, i.e. when MFEM_USE_CUDA=YES.
|
||||
|
||||
MFEM_USE_CUDA = YES/NO
|
||||
Enables support for CUDA devices in MFEM. CUDA is a parallel computing
|
||||
platform and programming model for general computing on graphical processing
|
||||
units (GPUs). The variable CUDA_ARCH is used to specify the CUDA compute
|
||||
capability used during compilation (by default, CUDA_ARCH=sm_60). When
|
||||
enabled, this option uses the CUDA_* build options, see below.
|
||||
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
|
||||
@@ -690,6 +696,7 @@ MFEM_USE_PUMI
|
||||
MFEM_USE_CUDA
|
||||
MFEM_USE_OCCA
|
||||
MFEM_USE_RAJA
|
||||
MFEM_USE_MM
|
||||
|
||||
The following options are CMake specific:
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ set(MFEM_USE_MPFR @MFEM_USE_MPFR@)
|
||||
set(MFEM_USE_SIDRE @MFEM_USE_SIDRE@)
|
||||
set(MFEM_USE_CONDUIT @MFEM_USE_CONDUIT@)
|
||||
set(MFEM_USE_PUMI @MFEM_USE_PUMI@)
|
||||
set(MFEM_USE_MM @MFEM_USE_MM@)
|
||||
set(MFEM_USE_CUDA @MFEM_USE_CUDA@)
|
||||
set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
|
||||
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
|
||||
|
||||
@@ -120,6 +120,9 @@
|
||||
// Enable MFEM functionality based on the OCCA library
|
||||
#cmakedefine MFEM_USE_OCCA
|
||||
|
||||
// Enable MFEM's internal Memory Manager (needed e.g. for MFEM_USE_CUDA)
|
||||
#cmakedefine MFEM_USE_MM
|
||||
|
||||
// Which library functions to use in class StopWatch for measuring time.
|
||||
// For a list of the available options, see INSTALL.
|
||||
// If not defined, an option is selected automatically.
|
||||
|
||||
@@ -720,7 +720,8 @@ function(mfem_export_mk_files)
|
||||
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE
|
||||
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GECKO MFEM_USE_GNUTLS
|
||||
MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_MPFR MFEM_USE_SIDRE
|
||||
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA)
|
||||
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_MM MFEM_USE_CUDA MFEM_USE_OCCA
|
||||
MFEM_USE_RAJA)
|
||||
foreach(var ${CONFIG_MK_BOOL_VARS})
|
||||
if (${var})
|
||||
set(${var} YES)
|
||||
|
||||
+11
-3
@@ -10,15 +10,18 @@
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
|
||||
// Support out-of-source builds: if MFEM_CONFIG_FILE is defined, include it.
|
||||
// Support out-of-source builds: if MFEM_BUILD_DIR is defined, load the config
|
||||
// file MFEM_BUILD_DIR/config/_config.hpp.
|
||||
//
|
||||
// Otherwise, use the local file: _config.hpp.
|
||||
|
||||
#ifndef MFEM_CONFIG_HPP
|
||||
#define MFEM_CONFIG_HPP
|
||||
|
||||
#ifdef MFEM_CONFIG_FILE
|
||||
#include MFEM_CONFIG_FILE
|
||||
#ifdef MFEM_BUILD_DIR
|
||||
#define MFEM_QUOTE(a) #a
|
||||
#define MFEM_MAKE_PATH(x,y) MFEM_QUOTE(x/y)
|
||||
#include MFEM_MAKE_PATH(MFEM_BUILD_DIR,config/_config.hpp)
|
||||
#else
|
||||
#include "_config.hpp"
|
||||
#endif
|
||||
@@ -53,4 +56,9 @@
|
||||
#endif
|
||||
#endif // MFEM_USE_MPI not defined
|
||||
|
||||
// CUDA requires the memory manager
|
||||
#if defined(MFEM_USE_CUDA) && !defined(MFEM_USE_MM)
|
||||
#error Building with CUDA (MFEM_USE_CUDA=YES) requires MFEM_USE_MM=YES
|
||||
#endif
|
||||
|
||||
#endif // MFEM_CONFIG_HPP
|
||||
|
||||
@@ -131,6 +131,9 @@
|
||||
// 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@
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ 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@
|
||||
|
||||
# Compiler, compile options, and link options
|
||||
MFEM_CXX = @MFEM_CXX@
|
||||
|
||||
@@ -42,6 +42,7 @@ option(MFEM_USE_MPFR "Enable MPFR usage." OFF)
|
||||
option(MFEM_USE_SIDRE "Enable Axom/Sidre usage" OFF)
|
||||
option(MFEM_USE_CONDUIT "Enable Conduit usage" OFF)
|
||||
option(MFEM_USE_PUMI "Enable PUMI" OFF)
|
||||
option(MFEM_USE_MM "Enable MFEM's memory manager" OFF)
|
||||
option(MFEM_USE_CUDA "Enable CUDA" OFF)
|
||||
option(MFEM_USE_OCCA "Enable OCCA" OFF)
|
||||
option(MFEM_USE_RAJA "Enable RAJA" OFF)
|
||||
|
||||
@@ -124,6 +124,7 @@ MFEM_USE_PUMI = NO
|
||||
MFEM_USE_CUDA = NO
|
||||
MFEM_USE_RAJA = NO
|
||||
MFEM_USE_OCCA = NO
|
||||
MFEM_USE_MM = NO
|
||||
|
||||
# Compile and link options for zlib.
|
||||
ZLIB_DIR =
|
||||
|
||||
+1
-2
@@ -36,7 +36,6 @@ CONFIG_MK = config.mk
|
||||
all: header config-mk
|
||||
|
||||
MPI = $(MFEM_USE_MPI:NO=)
|
||||
GHV_CXX ?= $(MFEM_CXX)
|
||||
GHV = get_hypre_version
|
||||
GHV_FLAGS = $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..),$(HYPRE_OPT))
|
||||
SMX = $(if $(MFEM_USE_PUMI:NO=),MFEM_USE_SIMMETRIX)
|
||||
@@ -45,7 +44,7 @@ SMX_FILE = $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..),$(SMX_PATH))
|
||||
|
||||
$(GHV): $(SRC)$(GHV).cpp
|
||||
$(call mfem-info, Determining HYPRE version ...)
|
||||
$(GHV_CXX) ${GHV_FLAGS} $(SRC)$(GHV).cpp -o $(GHV)
|
||||
$(MFEM_CXX) ${GHV_FLAGS} $(SRC)$(GHV).cpp -o $(GHV)
|
||||
$(GHV).out: $(GHV)
|
||||
./$(GHV) > $(GHV).out
|
||||
.INTERMEDIATE: $(GHV) $(GHV).out
|
||||
|
||||
@@ -276,7 +276,8 @@ case "$1" in
|
||||
;;
|
||||
-dev)
|
||||
device_runs="yes"
|
||||
mfem_config+=" MFEM_USE_CUDA=YES MFEM_USE_OCCA=YES MFEM_USE_RAJA=YES MFEM_USE_OPENMP=YES"
|
||||
mfem_config+=" MFEM_USE_CUDA=YES MFEM_USE_MM=YES \
|
||||
MFEM_USE_OCCA=YES MFEM_USE_RAJA=YES MFEM_USE_OPENMP=YES"
|
||||
;;
|
||||
-v)
|
||||
valgrind="yes"
|
||||
|
||||
+3
-2
@@ -43,14 +43,15 @@
|
||||
#define MFEM_ALIGN_SIZE(size,type) \
|
||||
MFEM_ROUNDUP(size,(MFEM_SIMD_SIZE)/sizeof(type))
|
||||
|
||||
#ifdef MFEM_COUNT_FLOPS
|
||||
namespace mfem
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
extern long long flop_count;
|
||||
long long flop_count;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_COUNT_FLOPS
|
||||
#define MFEM_FLOPS_RESET() (mfem::internal::flop_count = 0)
|
||||
#define MFEM_FLOPS_ADD(cnt) (mfem::internal::flop_count += (cnt))
|
||||
#define MFEM_FLOPS_GET() (mfem::internal::flop_count)
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = "MFEM"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v4.0.1
|
||||
PROJECT_NUMBER = v3.4.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -37,9 +37,7 @@ namespace mfem {
|
||||
*
|
||||
* <H3>Main GPU classes</H3>
|
||||
* - Device
|
||||
* - Memory
|
||||
* - MemoryManager
|
||||
* - MFEM_FORALL macro in forall.hpp
|
||||
*
|
||||
* <H3>Example codes</H3>
|
||||
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Laplace problem
|
||||
@@ -79,8 +77,8 @@ namespace mfem {
|
||||
* - <a class="el" href="ex19p_8cpp_source.html">Example 19p</a>: parallel incompressible nonlinear elasticity
|
||||
* - <a class="el" href="ex20_8cpp_source.html">Example 20</a>: symplectic ODE integration
|
||||
* - <a class="el" href="ex20p_8cpp_source.html">Example 20p</a>: parallel symplectic ODE integration
|
||||
* - <a class="el" href="ex21_8cpp_source.html">Example 21</a>: adaptive mesh refinement for linear elasticity
|
||||
* - <a class="el" href="ex21p_8cpp_source.html">Example 21p</a>: parallel adaptive mesh refinement for linear elasticity
|
||||
* - <a class="el" href="ex22_8cpp_source.html">Example 22</a>: adaptive mesh refinement for linear elasticity
|
||||
* - <a class="el" href="ex22p_8cpp_source.html">Example 22p</a>: parallel adaptive mesh refinement for linear elasticity
|
||||
*
|
||||
* <H4>SUNDIALS Examples</H4>
|
||||
* - Variants of Examples
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 134 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 128 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
@@ -27,7 +27,8 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex18.cpp
|
||||
ex19.cpp
|
||||
ex20.cpp
|
||||
ex21.cpp
|
||||
ex22.cpp
|
||||
ex23.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -52,7 +53,8 @@ if (MFEM_USE_MPI)
|
||||
ex18p.cpp
|
||||
ex19p.cpp
|
||||
ex20p.cpp
|
||||
ex21p.cpp
|
||||
ex22p.cpp
|
||||
ex23p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
+164
-245
File diff suppressed because one or more lines are too long
+19
-15
@@ -62,7 +62,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
const char *device = "cpu";
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -75,7 +75,7 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
args.AddOption(&device, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -88,18 +88,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 3. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
// largest number that gives a final mesh with no more than 50,000
|
||||
// elements.
|
||||
@@ -112,7 +107,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use continuous
|
||||
// 4. Define a finite element space on the mesh. Here we use continuous
|
||||
// Lagrange finite elements of the specified order. If order < 1, we
|
||||
// instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
@@ -133,7 +128,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of finite element unknowns: "
|
||||
<< fespace->GetTrueVSize() << endl;
|
||||
|
||||
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// 5. Determine the list of true (i.e. conforming) essential boundary dofs.
|
||||
// In this example, the boundary conditions are defined by marking all
|
||||
// the boundary attributes from the mesh as essential (Dirichlet) and
|
||||
// converting them to a list of true dofs.
|
||||
@@ -145,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
|
||||
// the basis functions in the finite element fespace.
|
||||
LinearForm *b = new LinearForm(fespace);
|
||||
@@ -153,6 +148,12 @@ int main(int argc, char *argv[])
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 7. Set device config parameters from the command line options and switch
|
||||
// to working on the device.
|
||||
Device::Configure(device);
|
||||
Device::Print();
|
||||
Device::Enable();
|
||||
|
||||
// 8. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
@@ -202,7 +203,10 @@ int main(int argc, char *argv[])
|
||||
// 12. Recover the solution as a finite element grid function.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 13. Save the refined mesh and the solution. This output can be viewed later
|
||||
// 13. Switch back to the host.
|
||||
Device::Disable();
|
||||
|
||||
// 14. Save the refined mesh and the solution. This output can be viewed later
|
||||
// using GLVis: "glvis -m refined.mesh -g sol.gf".
|
||||
ofstream mesh_ofs("refined.mesh");
|
||||
mesh_ofs.precision(8);
|
||||
@@ -211,7 +215,7 @@ int main(int argc, char *argv[])
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
|
||||
// 14. Send the solution by socket to a GLVis server.
|
||||
// 15. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -221,7 +225,7 @@ int main(int argc, char *argv[])
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
}
|
||||
|
||||
// 15. Free the used memory.
|
||||
// 16. Free the used memory.
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ void InitialDeformation(const Vector &x, Vector &y);
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
const char *mesh_file = "../data/beam-hex.mesh";
|
||||
int ref_levels = 0;
|
||||
int order = 2;
|
||||
bool visualization = true;
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ int main(int argc, char *argv[])
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
const char *mesh_file = "../data/beam-hex.mesh";
|
||||
int ser_ref_levels = 0;
|
||||
int par_ref_levels = 0;
|
||||
int order = 2;
|
||||
|
||||
+20
-16
@@ -65,7 +65,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
const char *device = "cpu";
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -78,7 +78,7 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
args.AddOption(&device, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -98,18 +98,13 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements.
|
||||
@@ -122,7 +117,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
@@ -135,7 +130,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
@@ -162,7 +157,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// 7. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
@@ -174,7 +169,7 @@ int main(int argc, char *argv[])
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// 8. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
@@ -182,6 +177,12 @@ int main(int argc, char *argv[])
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b->Assemble();
|
||||
|
||||
// 9. Set device config parameters from the command line options and switch
|
||||
// to working on the device.
|
||||
Device::Configure(device);
|
||||
if (myid == 0) { Device::Print(); }
|
||||
Device::Enable();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
@@ -224,7 +225,10 @@ int main(int argc, char *argv[])
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// 15. Switch back to the host.
|
||||
Device::Disable();
|
||||
|
||||
// 16. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
@@ -240,7 +244,7 @@ int main(int argc, char *argv[])
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// 17. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
@@ -251,7 +255,7 @@ int main(int argc, char *argv[])
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
// 18. Free the used memory.
|
||||
delete a;
|
||||
delete b;
|
||||
delete fespace;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
// MFEM Example 21
|
||||
// MFEM Example 22
|
||||
//
|
||||
// Compile with: make ex21
|
||||
// Compile with: make ex22
|
||||
//
|
||||
// Sample runs: ex21
|
||||
// ex21 -o 3
|
||||
// ex21 -m ../data/beam-quad.mesh
|
||||
// ex21 -m ../data/beam-quad.mesh -o 3
|
||||
// ex21 -m ../data/beam-quad.mesh -o 3 -f 1
|
||||
// ex21 -m ../data/beam-tet.mesh
|
||||
// ex21 -m ../data/beam-tet.mesh -o 2
|
||||
// ex21 -m ../data/beam-hex.mesh
|
||||
// ex21 -m ../data/beam-hex.mesh -o 2
|
||||
// Sample runs: ex22
|
||||
// ex22 -o 3
|
||||
// ex22 -m ../data/beam-quad.mesh
|
||||
// ex22 -m ../data/beam-quad.mesh -o 3
|
||||
// ex22 -m ../data/beam-quad.mesh -o 3 -f 1
|
||||
// ex22 -m ../data/beam-tet.mesh
|
||||
// ex22 -m ../data/beam-tet.mesh -o 2
|
||||
// ex22 -m ../data/beam-hex.mesh
|
||||
// ex22 -m ../data/beam-hex.mesh -o 2
|
||||
//
|
||||
// Description: This is a version of Example 2 with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the linear
|
||||
@@ -287,11 +287,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
{
|
||||
ofstream mesh_ref_out("ex21_reference.mesh");
|
||||
ofstream mesh_ref_out("ex22_reference.mesh");
|
||||
mesh_ref_out.precision(16);
|
||||
mesh.Print(mesh_ref_out);
|
||||
|
||||
ofstream mesh_out("ex21_deformed.mesh");
|
||||
ofstream mesh_out("ex22_deformed.mesh");
|
||||
mesh_out.precision(16);
|
||||
GridFunction nodes(&fespace), *nodes_p = &nodes;
|
||||
mesh.GetNodes(nodes);
|
||||
@@ -301,7 +301,7 @@ int main(int argc, char *argv[])
|
||||
mesh.Print(mesh_out);
|
||||
mesh.SwapNodes(nodes_p, own_nodes);
|
||||
|
||||
ofstream x_out("ex21_displacement.sol");
|
||||
ofstream x_out("ex22_displacement.sol");
|
||||
x_out.precision(16);
|
||||
x.Save(x_out);
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
// MFEM Example 21
|
||||
// MFEM Example 22
|
||||
//
|
||||
// Compile with: make ex21p
|
||||
// Compile with: make ex22p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex21p
|
||||
// mpirun -np 4 ex21p -o 3
|
||||
// mpirun -np 4 ex21p -m ../data/beam-quad.mesh
|
||||
// mpirun -np 4 ex21p -m ../data/beam-quad.mesh -o 3
|
||||
// mpirun -np 4 ex21p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex21p -m ../data/beam-tet.mesh -o 2
|
||||
// mpirun -np 4 ex21p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex21p -m ../data/beam-hex.mesh -o 2
|
||||
// Sample runs: mpirun -np 4 ex22p
|
||||
// mpirun -np 4 ex22p -o 3
|
||||
// mpirun -np 4 ex22p -m ../data/beam-quad.mesh
|
||||
// mpirun -np 4 ex22p -m ../data/beam-quad.mesh -o 3
|
||||
// mpirun -np 4 ex22p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex22p -m ../data/beam-tet.mesh -o 2
|
||||
// mpirun -np 4 ex22p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex22p -m ../data/beam-hex.mesh -o 2
|
||||
//
|
||||
// Description: This is a version of Example 2p with a simple adaptive mesh
|
||||
// refinement loop. The problem being solved is again the linear
|
||||
@@ -330,7 +330,7 @@ int main(int argc, char *argv[])
|
||||
x.Update();
|
||||
}
|
||||
|
||||
// 21. Inform also the bilinear and linear forms that the space has
|
||||
// 22. Inform also the bilinear and linear forms that the space has
|
||||
// changed.
|
||||
a.Update();
|
||||
b.Update();
|
||||
@@ -338,9 +338,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
{
|
||||
ostringstream mref_name, mesh_name, sol_name;
|
||||
mref_name << "ex21p_reference_mesh." << setfill('0') << setw(6) << myid;
|
||||
mesh_name << "ex21p_deformed_mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "ex21p_displacement." << setfill('0') << setw(6) << myid;
|
||||
mref_name << "ex22p_reference_mesh." << setfill('0') << setw(6) << myid;
|
||||
mesh_name << "ex22p_deformed_mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "ex22p_displacement." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ref_out(mref_name.str().c_str());
|
||||
mesh_ref_out.precision(16);
|
||||
@@ -0,0 +1,734 @@
|
||||
// MFEM Example 23
|
||||
//
|
||||
// Compile with: make ex23
|
||||
//
|
||||
// Sample runs:
|
||||
// ex23 -m ../data/periodic-segment.mesh -p 0 -s 2 -dt 0.001 -vs 50
|
||||
// ex23 -m ../data/periodic-segment.mesh -p 0 -s 12 -dt 0.01
|
||||
// ex23 -m ../data/periodic-segment.mesh -p 0 -s 22 -dt 0.01
|
||||
// ex23 -m ../data/periodic-segment.mesh -p 0 -s 32 -dt 0.005 -vs 10
|
||||
// ex23 -m ../data/periodic-square.mesh -p 0 -dt 0.01
|
||||
// ex23 -m ../data/periodic-square.mesh -p 0 -s 32 -dt 0.01
|
||||
// ex23 -m ../data/periodic-hexagon.mesh -p 0 -d 0.001 -s 12 -dt 0.02
|
||||
// ex23 -m ../data/periodic-hexagon.mesh -p 0 -d 0.001 -s 32 -dt 0.009 -vs 10
|
||||
// ex23 -m ../data/periodic-square.mesh -p 1 -dt 0.01 -tf 9
|
||||
// ex23 -m ../data/periodic-hexagon.mesh -p 1 -dt 0.01 -tf 9
|
||||
// ex23 -m ../data/amr-quad.mesh -p 1 -dt 0.01 -tf 9 -vs 2
|
||||
// ex23 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.01 -tf 9
|
||||
// ex23 -m ../data/disc-nurbs.mesh -p 2 -r 3 -dt 0.01 -tf 9
|
||||
// ex23 -m ../data/disc-nurbs.mesh -p 3 -r 3 -dt 0.01 -tf 9 -d 0.02
|
||||
// ex23 -m ../data/periodic-square.mesh -p 3 -r 3 -dt 0.025 -tf 9
|
||||
// ex23 -m ../data/periodic-cube.mesh -p 0 -o 2 -dt 0.025 -tf 8
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection-diffusion
|
||||
// equation
|
||||
// du/dt - div(D grad(u)) + v.grad(u) = 0, where
|
||||
// D is a diffusion coefficient,
|
||||
// v is a given fluid velocity, and
|
||||
// u0(x)=u(0,x) is a given initial condition.
|
||||
//
|
||||
// The example demonstrates the use of Discontinuous Galerkin (DG)
|
||||
// bilinear forms in MFEM (face integrators), the use of explicit,
|
||||
// implicit, and implicit-explicit ODE time integrators, the
|
||||
// definition of periodic boundary conditions through periodic
|
||||
// meshes, as well as the use of GLVis for persistent
|
||||
// visualization of a time-evolving solution. The saving of
|
||||
// time-dependent data files for external visualization with
|
||||
// VisIt (visit.llnl.gov) is also illustrated.
|
||||
//
|
||||
// This example is a merger of examples 9 and 14.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Choice for the problem setup. The fluid velocity, initial condition and
|
||||
// boundary condition are chosen based on this parameter.
|
||||
int problem;
|
||||
|
||||
// Velocity coefficient
|
||||
void velocity_function(const Vector &x, Vector &v);
|
||||
|
||||
// Initial condition
|
||||
double u0_function(const Vector &x);
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE for use with
|
||||
explicit ODE solvers. The DG weak form of du/dt = div(D grad(u))-v.grad(u) is
|
||||
M du/dt = - S u + K u + b, where M, S, and K are the mass,
|
||||
stiffness, and advection matrices, and b describes sources and the flow on
|
||||
the boundary.
|
||||
This can be written as a general ODE,
|
||||
du/dt = M^{-1} (-S u + K u + b), and this class is used to compute the RHS
|
||||
and perform the solve for du/dt. */
|
||||
class EX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
SparseMatrix &M, &S, &K;
|
||||
const Vector &b;
|
||||
|
||||
DSmoother M_prec;
|
||||
CGSolver M_solver;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
void initA(double dt);
|
||||
|
||||
public:
|
||||
EX_Evolution(SparseMatrix &_M, SparseMatrix &_S, SparseMatrix &_K,
|
||||
const Vector &_b);
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual ~EX_Evolution() {}
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE for use with
|
||||
implicit ODE solvers. The DG weak form of du/dt = div(D grad(u))-v.grad(u) is
|
||||
[M + dt (S - K)] du/dt = - S u + K u + b, where M, S, and K are the mass,
|
||||
stiffness, and advection matrices, and b describes sources and the flow on
|
||||
the boundary.
|
||||
This can be written as a general ODE,
|
||||
du/dt = A^{-1} (-S u + K u + b) with A = [M + dt (S - K)], and this class is
|
||||
used to perform the fully implicit solve for du/dt. */
|
||||
class IM_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
SparseMatrix &M, &S, &K;
|
||||
SparseMatrix *A;
|
||||
const Vector &b;
|
||||
|
||||
DSmoother M_prec;
|
||||
CGSolver M_solver;
|
||||
|
||||
DSmoother *A_prec;
|
||||
GMRESSolver *A_solver;
|
||||
double dt;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
void initA(double dt);
|
||||
|
||||
public:
|
||||
IM_Evolution(SparseMatrix &_M, SparseMatrix &_S, SparseMatrix &_K,
|
||||
const Vector &_b);
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &y);
|
||||
|
||||
virtual ~IM_Evolution() { delete A_solver; delete A_prec; delete A; }
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE for use with
|
||||
IMEX (Implicit-Explicit) ODE solvers. The DG weak form of
|
||||
du/dt = div(D grad(u))-v.grad(u) is
|
||||
[M + dt S] du/dt = - S u + K u + b, where M, S, and K are the mass,
|
||||
stiffness, and advection matrices, and b describes sources and the flow on
|
||||
the boundary.
|
||||
This can be written as a general ODE,
|
||||
du/dt = A^{-1} (-S u + K u + b) with A = [M + dt (S - K)], and this class is
|
||||
used to perform the implicit or explicit solve for du/dt. */
|
||||
class IMEX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
SparseMatrix &M, &S, &K;
|
||||
SparseMatrix *A;
|
||||
const Vector &b;
|
||||
|
||||
DSmoother M_prec;
|
||||
CGSolver M_solver;
|
||||
|
||||
DSmoother *A_prec;
|
||||
CGSolver *A_solver;
|
||||
double dt;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
void initA(double dt);
|
||||
|
||||
public:
|
||||
IMEX_Evolution(SparseMatrix &_M, SparseMatrix &_S, SparseMatrix &_K,
|
||||
const Vector &_b);
|
||||
|
||||
virtual void ExplicitMult(const Vector &x, Vector &y) const;
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &y);
|
||||
|
||||
virtual ~IMEX_Evolution() { delete A_solver; delete A_prec; delete A; }
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
problem = 0;
|
||||
const char *mesh_file = "../data/periodic-hexagon.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 3;
|
||||
int ode_solver_type = 12;
|
||||
double t_final = 10.0;
|
||||
double d_coef = 0.01;
|
||||
double dt = 0.01;
|
||||
double sigma = -1.0;
|
||||
double kappa = -1.0;
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler, 2 - RK2, 3 - RK3 SSP,"
|
||||
" 4 - RK4, 5 - Generalized Alpha,\n\t"
|
||||
"11 - Backward Euler, 12 - SDIRK2, 13 - SDIRK3,\n\t"
|
||||
"22 - Implicit Midpoint, 23 SDIRK23, 24 - SDIRK34,\n\t"
|
||||
"31 - IMEX BE/FE, 32 - IMEX RK2.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&d_coef, "-d", "--diff-coef",
|
||||
"Diffusion coefficient.");
|
||||
args.AddOption(&sigma, "-s", "--sigma",
|
||||
"One of the two DG penalty parameters, typically +1/-1."
|
||||
" See the documentation of class DGDiffusionIntegrator.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"One of the two DG penalty parameters, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
|
||||
"--no-visit-datafiles",
|
||||
"Save data files for VisIt (visit.llnl.gov) visualization.");
|
||||
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
|
||||
"--ascii-datafiles",
|
||||
"Use binary (Sidre) or ascii format for VisIt data files.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit methods
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 5: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit L-stable methods
|
||||
case 11: ode_solver = new BackwardEulerSolver; break;
|
||||
case 12: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 13: ode_solver = new SDIRK33Solver; break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
// Implicit-Explicit methods
|
||||
case 31: ode_solver = new IMEX_BE_FE; break;
|
||||
case 32: ode_solver = new IMEXRK2; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 3. Read the serial mesh from the given mesh file on all processors. We can
|
||||
// handle geometrically periodic meshes in this code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 4. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter. If the mesh is of NURBS type, we convert it
|
||||
// to a (piecewise-polynomial) high-order mesh.
|
||||
for (int lev = 0; lev < ref_levels; lev++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
mesh.SetCurvature(max(order, 1));
|
||||
}
|
||||
mesh.GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
// 5. Define the parallel discontinuous DG finite element space on the
|
||||
// parallel refined mesh of the given polynomial order.
|
||||
DG_FECollection fec(order, dim);
|
||||
FiniteElementSpace fes(&mesh, &fec);
|
||||
|
||||
cout << "Number of unknowns: " << fes.GetVSize() << endl;
|
||||
|
||||
// 6. Set up and assemble the parallel bilinear and linear forms (and the
|
||||
// parallel hypre matrices) corresponding to the DG discretization. The
|
||||
// DGTraceIntegrator involves integrals over mesh interior faces.
|
||||
ConstantCoefficient diff_coef(d_coef);
|
||||
VectorFunctionCoefficient velocity(dim, velocity_function);
|
||||
FunctionCoefficient u0(u0_function);
|
||||
|
||||
BilinearForm m(&fes);
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
|
||||
BilinearForm s(&fes);
|
||||
s.AddDomainIntegrator(new DiffusionIntegrator(diff_coef));
|
||||
s.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coef, sigma,
|
||||
kappa));
|
||||
s.AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coef, sigma, kappa));
|
||||
|
||||
BilinearForm k(&fes);
|
||||
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
k.AddInteriorFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
k.AddBdrFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
|
||||
LinearForm b(&fes);
|
||||
b.AddBdrFaceIntegrator(
|
||||
new DGDirichletLFIntegrator(u0, diff_coef, sigma, kappa));
|
||||
|
||||
int skip_zeros = 0;
|
||||
m.Assemble(skip_zeros);
|
||||
m.Finalize(skip_zeros);
|
||||
s.Assemble(skip_zeros);
|
||||
s.Finalize(skip_zeros);
|
||||
k.Assemble(skip_zeros);
|
||||
k.Finalize(skip_zeros);
|
||||
b.Assemble();
|
||||
|
||||
// 7. Define the initial conditions, save the corresponding grid function to
|
||||
// a file and (optionally) save data in the VisIt format and initialize
|
||||
// GLVis visualization.
|
||||
GridFunction u(&fes);
|
||||
u.ProjectCoefficient(u0);
|
||||
|
||||
{
|
||||
ofstream omesh("ex23.mesh");
|
||||
omesh.precision(precision);
|
||||
mesh.Print(omesh);
|
||||
ofstream osol("ex23-init.gf");
|
||||
osol.precision(precision);
|
||||
u.Save(osol);
|
||||
}
|
||||
|
||||
// Create data collection for solution output: either VisItDataCollection for
|
||||
// ascii data files, or SidreDataCollection for binary data files.
|
||||
DataCollection *dc = NULL;
|
||||
if (visit)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
dc = new SidreDataCollection("Example23", &mesh);
|
||||
#else
|
||||
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = new VisItDataCollection("Example23", &mesh);
|
||||
dc->SetPrecision(precision);
|
||||
}
|
||||
dc->RegisterField("solution", &u);
|
||||
dc->SetCycle(0);
|
||||
dc->SetTime(0.0);
|
||||
dc->Save();
|
||||
}
|
||||
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
visualization = false;
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sout.precision(precision);
|
||||
sout << "solution\n" << mesh << u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Define the time-dependent evolution operator describing the ODE
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
|
||||
TimeDependentOperator *adv = NULL;
|
||||
if (ode_solver_type < 10)
|
||||
{
|
||||
adv = new EX_Evolution(m.SpMat(), s.SpMat(), k.SpMat(), b);
|
||||
}
|
||||
else if (ode_solver_type < 30)
|
||||
{
|
||||
adv = new IM_Evolution(m.SpMat(), s.SpMat(), k.SpMat(), b);
|
||||
}
|
||||
else
|
||||
{
|
||||
adv = new IMEX_Evolution(m.SpMat(), s.SpMat(), k.SpMat(), b);
|
||||
}
|
||||
|
||||
double t = 0.0;
|
||||
adv->SetTime(t);
|
||||
ode_solver->Init(*adv);
|
||||
|
||||
int n_steps = (int)ceil(t_final / dt);
|
||||
double dt_real = t_final / n_steps;
|
||||
|
||||
for (int ti = 0; ti < n_steps; )
|
||||
{
|
||||
ode_solver->Step(u, t, dt_real);
|
||||
ti++;
|
||||
|
||||
if (ti % vis_steps == 0 || ti == n_steps)
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sout << "solution\n" << mesh << u << flush;
|
||||
}
|
||||
|
||||
if (visit)
|
||||
{
|
||||
dc->SetCycle(ti);
|
||||
dc->SetTime(t);
|
||||
dc->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Save the final solution in parallel. This output can be viewed later
|
||||
// using GLVis: "glvis -np <np> -m ex23-mesh -g ex23-final".
|
||||
{
|
||||
ofstream osol("ex23-final.gf");
|
||||
osol.precision(precision);
|
||||
u.Save(osol);
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete adv;
|
||||
delete dc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Implementation of class EX_Evolution
|
||||
EX_Evolution::EX_Evolution(SparseMatrix &_M, SparseMatrix &_S,
|
||||
SparseMatrix &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
M(_M), S(_S), K(_K), b(_b), z(_M.Height())
|
||||
{
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void EX_Evolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (-S x + K x + b)
|
||||
K.Mult(x, z);
|
||||
S.AddMult(x, z, -1.0);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
// Implementation of class IM_Evolution
|
||||
IM_Evolution::IM_Evolution(SparseMatrix &_M, SparseMatrix &_S,
|
||||
SparseMatrix &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
M(_M), S(_S), K(_K), A(NULL), b(_b),
|
||||
A_prec(NULL), A_solver(NULL), dt(-1.0), z(_M.Height())
|
||||
{
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IM_Evolution::initA(double _dt)
|
||||
{
|
||||
if (fabs(dt - _dt) > 1e-4 * _dt)
|
||||
{
|
||||
delete A_solver;
|
||||
delete A_prec;
|
||||
delete A;
|
||||
|
||||
SparseMatrix * SK = Add(1.0, S, -1.0, K);
|
||||
A = Add(1.0, M, _dt, *SK);
|
||||
delete SK;
|
||||
dt = _dt;
|
||||
|
||||
A_prec = new DSmoother(*A);
|
||||
A_solver = new GMRESSolver;
|
||||
A_solver->SetOperator(*A);
|
||||
A_solver->SetPreconditioner(*A_prec);
|
||||
|
||||
A_solver->iterative_mode = false;
|
||||
A_solver->SetRelTol(1e-9);
|
||||
A_solver->SetAbsTol(0.0);
|
||||
A_solver->SetMaxIter(100);
|
||||
A_solver->SetPrintLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
void IM_Evolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (-S x + K x + b)
|
||||
K.Mult(x, z);
|
||||
S.AddMult(x, z, -1.0);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IM_Evolution::ImplicitSolve(const double _dt, const Vector &x, Vector &y)
|
||||
{
|
||||
this->initA(_dt);
|
||||
|
||||
// y = (M + dt S - dt K)^{-1} (-S x + K x + b)
|
||||
K.Mult(x, z);
|
||||
S.AddMult(x, z, -1.0);
|
||||
z += b;
|
||||
A_solver->Mult(z, y);
|
||||
}
|
||||
|
||||
// Implementation of class IMEX_Evolution
|
||||
IMEX_Evolution::IMEX_Evolution(SparseMatrix &_M, SparseMatrix &_S,
|
||||
SparseMatrix &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
M(_M), S(_S), K(_K), A(NULL), b(_b),
|
||||
A_prec(NULL), A_solver(NULL), dt(-1.0), z(_M.Height())
|
||||
{
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::initA(double _dt)
|
||||
{
|
||||
if (fabs(dt - _dt) > 1e-4 * _dt)
|
||||
{
|
||||
delete A_solver;
|
||||
delete A_prec;
|
||||
delete A;
|
||||
|
||||
A = Add(_dt, S, 1.0, M); // A = M + dt * S
|
||||
dt = _dt;
|
||||
|
||||
A_prec = new DSmoother(*A);
|
||||
A_solver = new CGSolver;
|
||||
A_solver->SetOperator(*A);
|
||||
A_solver->SetPreconditioner(*A_prec);
|
||||
|
||||
A_solver->iterative_mode = false;
|
||||
A_solver->SetRelTol(1e-9);
|
||||
A_solver->SetAbsTol(0.0);
|
||||
A_solver->SetMaxIter(100);
|
||||
A_solver->SetPrintLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
void IMEX_Evolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (-S x + K x + b)
|
||||
K.Mult(x, z);
|
||||
S.AddMult(x, z, -1.0);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ExplicitMult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (K x + b)
|
||||
K.Mult(x, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ImplicitSolve(const double _dt, const Vector &x, Vector &y)
|
||||
{
|
||||
this->initA(_dt);
|
||||
// y = (M + dt S)^{-1} (-S x + b)
|
||||
S.Mult(x, z);
|
||||
z *= -1.0;
|
||||
z += b;
|
||||
A_solver->Mult(z, y);
|
||||
}
|
||||
|
||||
// Velocity coefficient
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
double center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const double w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const double w = M_PI/2;
|
||||
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial condition
|
||||
double u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
double center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
|
||||
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
double x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const double f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Inflow boundary condition (zero for the problems considered in this example)
|
||||
double inflow_function(const Vector &x)
|
||||
{
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3: return 0.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
@@ -0,0 +1,797 @@
|
||||
// MFEM Example 23 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex23p
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-segment.mesh -p 0 -s 2 -dt 0.001 -vs 50
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-segment.mesh -p 0 -s 12 -dt 0.01
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-segment.mesh -p 0 -s 22 -dt 0.01
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-segment.mesh -p 0 -s 32 -dt 0.005 -vs 10
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-square.mesh -p 0 -dt 0.01
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-square.mesh -p 0 -s 32 -dt 0.01
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-hexagon.mesh -p 0 -d 0.001 -s 12 -dt 0.02
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-hexagon.mesh -p 0 -d 0.001 -s 32 -dt 0.009 -vs 10
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-square.mesh -p 1 -dt 0.01 -tf 9
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.01 -tf 9
|
||||
// mpirun -np 4 ex23p -m ../data/amr-quad.mesh -p 1 -dt 0.01 -tf 9 -vs 2
|
||||
// mpirun -np 4 ex23p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.01 -tf 9
|
||||
// mpirun -np 4 ex23p -m ../data/disc-nurbs.mesh -p 2 -rp 1 -dt 0.01 -tf 9
|
||||
// mpirun -np 4 ex23p -m ../data/disc-nurbs.mesh -p 3 -rp 1 -dt 0.01 -tf 9 -d 0.02
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-square.mesh -p 3 -rp 1 -dt 0.025 -tf 9
|
||||
// mpirun -np 4 ex23p -m ../data/periodic-cube.mesh -p 0 -o 2 -dt 0.025 -tf 8
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection-diffusion
|
||||
// equation
|
||||
// du/dt - div(D grad(u)) + v.grad(u) = 0, where
|
||||
// D is a diffusion coefficient,
|
||||
// v is a given fluid velocity, and
|
||||
// u0(x)=u(0,x) is a given initial condition.
|
||||
//
|
||||
// The example demonstrates the use of Discontinuous Galerkin (DG)
|
||||
// bilinear forms in MFEM (face integrators), the use of explicit,
|
||||
// implicit, and implicit-explicit ODE time integrators, the
|
||||
// definition of periodic boundary conditions through periodic
|
||||
// meshes, as well as the use of GLVis for persistent
|
||||
// visualization of a time-evolving solution. The saving of
|
||||
// time-dependent data files for external visualization with
|
||||
// VisIt (visit.llnl.gov) is also illustrated.
|
||||
//
|
||||
// This example is a merger of examples 9 and 14.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Choice for the problem setup. The fluid velocity, initial condition and
|
||||
// boundary condition are chosen based on this parameter.
|
||||
int problem;
|
||||
|
||||
// Velocity coefficient
|
||||
void velocity_function(const Vector &x, Vector &v);
|
||||
|
||||
// Initial condition
|
||||
double u0_function(const Vector &x);
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE for use with
|
||||
explicit ODE solvers. The DG weak form of du/dt = div(D grad(u))-v.grad(u) is
|
||||
M du/dt = - S u + K u + b, where M, S, and K are the mass,
|
||||
stiffness, and advection matrices, and b describes sources and the flow on
|
||||
the boundary.
|
||||
This can be written as a general ODE,
|
||||
du/dt = M^{-1} (-S u + K u + b), and this class is used to compute the RHS
|
||||
and perform the solve for du/dt. */
|
||||
class EX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
HypreParMatrix &M, &S, &K;
|
||||
const Vector &b;
|
||||
|
||||
HypreSmoother M_prec;
|
||||
CGSolver M_solver;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
void initA(double dt);
|
||||
|
||||
public:
|
||||
EX_Evolution(HypreParMatrix &_M, HypreParMatrix &_S, HypreParMatrix &_K,
|
||||
const Vector &_b);
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual ~EX_Evolution() {}
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE for use with
|
||||
implicit ODE solvers. The DG weak form of du/dt = div(D grad(u))-v.grad(u) is
|
||||
[M + dt (S - K)] du/dt = - S u + K u + b, where M, S, and K are the mass,
|
||||
stiffness, and advection matrices, and b describes sources and the flow on
|
||||
the boundary.
|
||||
This can be written as a general ODE,
|
||||
du/dt = A^{-1} (-S u + K u + b) with A = [M + dt (S - K)], and this class is
|
||||
used to perform the fully implicit solve for du/dt. */
|
||||
class IM_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
HypreParMatrix &M, &S, &K;
|
||||
HypreParMatrix *A;
|
||||
const Vector &b;
|
||||
|
||||
HypreSmoother M_prec;
|
||||
CGSolver M_solver;
|
||||
|
||||
HypreBoomerAMG *A_prec;
|
||||
GMRESSolver *A_solver;
|
||||
double dt;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
void initA(double dt);
|
||||
|
||||
public:
|
||||
IM_Evolution(HypreParMatrix &_M, HypreParMatrix &_S, HypreParMatrix &_K,
|
||||
const Vector &_b);
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &y);
|
||||
|
||||
virtual ~IM_Evolution() { delete A_solver; delete A_prec; delete A; }
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE for use with
|
||||
IMEX (Implicit-Explicit) ODE solvers. The DG weak form of
|
||||
du/dt = div(D grad(u))-v.grad(u) is
|
||||
[M + dt S] du/dt = - S u + K u + b, where M, S, and K are the mass,
|
||||
stiffness, and advection matrices, and b describes sources and the flow on
|
||||
the boundary.
|
||||
This can be written as a general ODE,
|
||||
du/dt = A^{-1} (-S u + K u + b) with A = [M + dt (S - K)], and this class is
|
||||
used to perform the implicit or explicit solve for du/dt. */
|
||||
class IMEX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
HypreParMatrix &M, &S, &K;
|
||||
HypreParMatrix *A;
|
||||
const Vector &b;
|
||||
|
||||
HypreSmoother M_prec;
|
||||
CGSolver M_solver;
|
||||
|
||||
HypreBoomerAMG *A_prec;
|
||||
CGSolver *A_solver;
|
||||
double dt;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
void initA(double dt);
|
||||
|
||||
public:
|
||||
IMEX_Evolution(HypreParMatrix &_M, HypreParMatrix &_S, HypreParMatrix &_K,
|
||||
const Vector &_b);
|
||||
|
||||
virtual void ExplicitMult(const Vector &x, Vector &y) const;
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &y);
|
||||
|
||||
virtual ~IMEX_Evolution() { delete A_solver; delete A_prec; delete A; }
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
problem = 0;
|
||||
const char *mesh_file = "../data/periodic-hexagon.mesh";
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 0;
|
||||
int order = 3;
|
||||
int ode_solver_type = 12;
|
||||
double t_final = 10.0;
|
||||
double d_coef = 0.01;
|
||||
double dt = 0.01;
|
||||
double sigma = -1.0;
|
||||
double kappa = -1.0;
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler, 2 - RK2, 3 - RK3 SSP,"
|
||||
" 4 - RK4, 5 - Generalized Alpha,\n\t"
|
||||
"11 - Backward Euler, 12 - SDIRK2, 13 - SDIRK3,\n\t"
|
||||
"22 - Implicit Midpoint, 23 SDIRK23, 24 - SDIRK34,\n\t"
|
||||
"31 - IMEX BE/FE, 32 - IMEX RK2.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&d_coef, "-d", "--diff-coef",
|
||||
"Diffusion coefficient.");
|
||||
args.AddOption(&sigma, "-s", "--sigma",
|
||||
"One of the two DG penalty parameters, typically +1/-1."
|
||||
" See the documentation of class DGDiffusionIntegrator.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"One of the two DG penalty parameters, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
|
||||
"--no-visit-datafiles",
|
||||
"Save data files for VisIt (visit.llnl.gov) visualization.");
|
||||
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
|
||||
"--ascii-datafiles",
|
||||
"Use binary (Sidre) or ascii format for VisIt data files.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit,
|
||||
// implicitit, and implicit-explicit Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver = NULL;
|
||||
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit methods
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 5: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit L-stable methods
|
||||
case 11: ode_solver = new BackwardEulerSolver; break;
|
||||
case 12: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 13: ode_solver = new SDIRK33Solver; break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
// Implicit-Explicit methods
|
||||
case 31: ode_solver = new IMEX_BE_FE; break;
|
||||
case 32: ode_solver = new IMEXRK2; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 4. Read the serial mesh from the given mesh file on all processors. We can
|
||||
// handle geometrically periodic meshes in this code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter. If the mesh is of NURBS type, we convert it
|
||||
// to a (piecewise-polynomial) high-order mesh.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
}
|
||||
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 7. Define the parallel discontinuous DG finite element space on the
|
||||
// parallel refined mesh of the given polynomial order.
|
||||
DG_FECollection fec(order, dim);
|
||||
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
|
||||
|
||||
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of unknowns: " << global_vSize << endl;
|
||||
}
|
||||
|
||||
// 8. Set up and assemble the parallel bilinear and linear forms (and the
|
||||
// parallel hypre matrices) corresponding to the DG discretization. The
|
||||
// DGTraceIntegrator involves integrals over mesh interior faces.
|
||||
ConstantCoefficient diff_coef(d_coef);
|
||||
VectorFunctionCoefficient velocity(dim, velocity_function);
|
||||
FunctionCoefficient u0(u0_function);
|
||||
|
||||
ParBilinearForm *m = new ParBilinearForm(fes);
|
||||
m->AddDomainIntegrator(new MassIntegrator);
|
||||
|
||||
ParBilinearForm *s = new ParBilinearForm(fes);
|
||||
s->AddDomainIntegrator(new DiffusionIntegrator(diff_coef));
|
||||
s->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coef, sigma,
|
||||
kappa));
|
||||
s->AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coef, sigma, kappa));
|
||||
|
||||
ParBilinearForm *k = new ParBilinearForm(fes);
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
k->AddInteriorFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
k->AddBdrFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
|
||||
ParLinearForm *b = new ParLinearForm(fes);
|
||||
b->AddBdrFaceIntegrator(
|
||||
new DGDirichletLFIntegrator(u0, diff_coef, sigma, kappa));
|
||||
|
||||
int skip_zeros = 0;
|
||||
m->Assemble(skip_zeros);
|
||||
m->Finalize(skip_zeros);
|
||||
s->Assemble(skip_zeros);
|
||||
s->Finalize(skip_zeros);
|
||||
k->Assemble(skip_zeros);
|
||||
k->Finalize(skip_zeros);
|
||||
b->Assemble();
|
||||
|
||||
HypreParMatrix *M = m->ParallelAssemble();
|
||||
HypreParMatrix *S = s->ParallelAssemble();
|
||||
HypreParMatrix *K = k->ParallelAssemble();
|
||||
HypreParVector *B = b->ParallelAssemble();
|
||||
|
||||
// 9. Define the initial conditions, save the corresponding grid function to
|
||||
// a file and (optionally) save data in the VisIt format and initialize
|
||||
// GLVis visualization.
|
||||
ParGridFunction *u = new ParGridFunction(fes);
|
||||
u->ProjectCoefficient(u0);
|
||||
HypreParVector *U = u->GetTrueDofs();
|
||||
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "ex23-mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "ex23-init." << setfill('0') << setw(6) << myid;
|
||||
ofstream omesh(mesh_name.str().c_str());
|
||||
omesh.precision(precision);
|
||||
pmesh->Print(omesh);
|
||||
ofstream osol(sol_name.str().c_str());
|
||||
osol.precision(precision);
|
||||
u->Save(osol);
|
||||
}
|
||||
|
||||
// Create data collection for solution output: either VisItDataCollection for
|
||||
// ascii data files, or SidreDataCollection for binary data files.
|
||||
DataCollection *dc = NULL;
|
||||
if (visit)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
dc = new SidreDataCollection("Example23-Parallel", pmesh);
|
||||
#else
|
||||
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = new VisItDataCollection("Example23-Parallel", pmesh);
|
||||
dc->SetPrecision(precision);
|
||||
// To save the mesh using MFEM's parallel mesh format:
|
||||
// dc->SetFormat(DataCollection::PARALLEL_FORMAT);
|
||||
}
|
||||
dc->RegisterField("solution", u);
|
||||
dc->SetCycle(0);
|
||||
dc->SetTime(0.0);
|
||||
dc->Save();
|
||||
}
|
||||
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
visualization = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout.precision(precision);
|
||||
sout << "solution\n" << *pmesh << *u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
if (myid == 0)
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Define the time-dependent evolution operator describing the ODE
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
|
||||
TimeDependentOperator *adv = NULL;
|
||||
if (ode_solver_type < 10)
|
||||
{
|
||||
adv = new EX_Evolution(*M, *S, *K, *B);
|
||||
}
|
||||
else if (ode_solver_type < 30)
|
||||
{
|
||||
adv = new IM_Evolution(*M, *S, *K, *B);
|
||||
}
|
||||
else
|
||||
{
|
||||
adv = new IMEX_Evolution(*M, *S, *K, *B);
|
||||
}
|
||||
|
||||
double t = 0.0;
|
||||
adv->SetTime(t);
|
||||
ode_solver->Init(*adv);
|
||||
|
||||
int n_steps = (int)ceil(t_final / dt);
|
||||
double dt_real = t_final / n_steps;
|
||||
|
||||
for (int ti = 0; ti < n_steps; )
|
||||
{
|
||||
ode_solver->Step(*U, t, dt_real);
|
||||
ti++;
|
||||
|
||||
if (ti % vis_steps == 0 || ti == n_steps)
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
}
|
||||
|
||||
// 11. Extract the parallel grid function corresponding to the finite
|
||||
// element approximation U (the local solution on each processor).
|
||||
*u = *U;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << *pmesh << *u << flush;
|
||||
}
|
||||
|
||||
if (visit)
|
||||
{
|
||||
dc->SetCycle(ti);
|
||||
dc->SetTime(t);
|
||||
dc->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 12. Save the final solution in parallel. This output can be viewed later
|
||||
// using GLVis: "glvis -np <np> -m ex23-mesh -g ex23-final".
|
||||
{
|
||||
*u = *U;
|
||||
ostringstream sol_name;
|
||||
sol_name << "ex23-final." << setfill('0') << setw(6) << myid;
|
||||
ofstream osol(sol_name.str().c_str());
|
||||
osol.precision(precision);
|
||||
u->Save(osol);
|
||||
}
|
||||
|
||||
// 13. Free the used memory.
|
||||
delete U;
|
||||
delete u;
|
||||
delete B;
|
||||
delete b;
|
||||
delete K;
|
||||
delete k;
|
||||
delete S;
|
||||
delete s;
|
||||
delete M;
|
||||
delete m;
|
||||
delete fes;
|
||||
delete pmesh;
|
||||
delete ode_solver;
|
||||
delete adv;
|
||||
delete dc;
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Implementation of class EX_Evolution
|
||||
EX_Evolution::EX_Evolution(HypreParMatrix &_M, HypreParMatrix &_S,
|
||||
HypreParMatrix &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
M(_M), S(_S), K(_K), b(_b),
|
||||
M_prec(M), M_solver(M.GetComm()), z(M.Height())
|
||||
{
|
||||
M_prec.SetType(HypreSmoother::Jacobi);
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void EX_Evolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (-S x + K x + b)
|
||||
S.Mult(-1.0, x, 0.0, z);
|
||||
K.Mult(1.0, x, 1.0, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
// Implementation of class IM_Evolution
|
||||
IM_Evolution::IM_Evolution(HypreParMatrix &_M, HypreParMatrix &_S,
|
||||
HypreParMatrix &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
M(_M), S(_S), K(_K), A(NULL), b(_b),
|
||||
M_prec(M), M_solver(M.GetComm()),
|
||||
A_prec(NULL), A_solver(NULL), dt(-1.0), z(M.Height())
|
||||
{
|
||||
M_prec.SetType(HypreSmoother::Jacobi);
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IM_Evolution::initA(double _dt)
|
||||
{
|
||||
if (fabs(dt - _dt) > 1e-4 * _dt)
|
||||
{
|
||||
delete A_solver;
|
||||
delete A_prec;
|
||||
delete A;
|
||||
|
||||
HypreParMatrix * SK = Add(1.0, S, -1.0, K); // SK = S - K
|
||||
A = Add(_dt, *SK, 1.0, M); // A = M + dt * (S - K)
|
||||
delete SK;
|
||||
dt = _dt;
|
||||
|
||||
A_prec = new HypreBoomerAMG(*A);
|
||||
A_solver = new GMRESSolver(A->GetComm());
|
||||
A_solver->SetOperator(*A);
|
||||
A_solver->SetPreconditioner(*A_prec);
|
||||
|
||||
A_solver->iterative_mode = false;
|
||||
A_solver->SetRelTol(1e-9);
|
||||
A_solver->SetAbsTol(0.0);
|
||||
A_solver->SetMaxIter(100);
|
||||
A_solver->SetPrintLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
void IM_Evolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (-S x + K x + b)
|
||||
S.Mult(-1.0, x, 0.0, z);
|
||||
K.Mult(1.0, x, 1.0, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IM_Evolution::ImplicitSolve(const double _dt, const Vector &x, Vector &y)
|
||||
{
|
||||
this->initA(_dt);
|
||||
|
||||
// y = (M + dt S - dt K)^{-1} (-S x + K x + b)
|
||||
S.Mult(-1.0, x, 0.0, z);
|
||||
K.Mult(1.0, x, 1.0, z);
|
||||
z += b;
|
||||
A_solver->Mult(z, y);
|
||||
}
|
||||
|
||||
// Implementation of class IMEX_Evolution
|
||||
IMEX_Evolution::IMEX_Evolution(HypreParMatrix &_M, HypreParMatrix &_S,
|
||||
HypreParMatrix &_K, const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
M(_M), S(_S), K(_K), A(NULL), b(_b),
|
||||
M_prec(M), M_solver(M.GetComm()),
|
||||
A_prec(NULL), A_solver(NULL), dt(-1.0), z(M.Height())
|
||||
{
|
||||
M_prec.SetType(HypreSmoother::Jacobi);
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::initA(double _dt)
|
||||
{
|
||||
if (fabs(dt - _dt) > 1e-4 * _dt)
|
||||
{
|
||||
delete A_solver;
|
||||
delete A_prec;
|
||||
delete A;
|
||||
|
||||
A = Add(_dt, S, 1.0, M); // A = M + dt * S
|
||||
dt = _dt;
|
||||
|
||||
A_prec = new HypreBoomerAMG(*A);
|
||||
A_solver = new CGSolver(A->GetComm());
|
||||
A_solver->SetOperator(*A);
|
||||
A_solver->SetPreconditioner(*A_prec);
|
||||
|
||||
A_solver->iterative_mode = false;
|
||||
A_solver->SetRelTol(1e-9);
|
||||
A_solver->SetAbsTol(0.0);
|
||||
A_solver->SetMaxIter(100);
|
||||
A_solver->SetPrintLevel(0);
|
||||
}
|
||||
}
|
||||
|
||||
void IMEX_Evolution::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (-S x + K x + b)
|
||||
S.Mult(-1.0, x, 0.0, z);
|
||||
K.Mult(1.0, x, 1.0, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ExplicitMult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// y = M^{-1} (K x + b)
|
||||
K.Mult(1.0, x, 0.0, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ImplicitSolve(const double _dt, const Vector &x, Vector &y)
|
||||
{
|
||||
this->initA(_dt);
|
||||
// y = (M + dt S)^{-1} (-S x + b)
|
||||
S.Mult(-1.0, x, 0.0, z);
|
||||
z += b;
|
||||
A_solver->Mult(z, y);
|
||||
}
|
||||
|
||||
// Velocity coefficient
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
double center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const double w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const double w = M_PI/2;
|
||||
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial condition
|
||||
double u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
double center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
|
||||
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
double x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const double f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
+12
-11
@@ -49,7 +49,7 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
const char *device = "cpu";
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -59,7 +59,7 @@ int main(int argc, char *argv[])
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
args.AddOption(&device, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -72,19 +72,14 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.Print();
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
|
||||
// the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
int sdim = mesh.SpaceDimension();
|
||||
|
||||
// 4. Since a NURBS mesh can currently only be refined uniformly, we need to
|
||||
// 3. Since a NURBS mesh can currently only be refined uniformly, we need to
|
||||
// convert it to a piecewise-polynomial curved mesh. First we refine the
|
||||
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
|
||||
if (mesh.NURBSext)
|
||||
@@ -96,11 +91,15 @@ int main(int argc, char *argv[])
|
||||
mesh.SetCurvature(2);
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. The polynomial order is
|
||||
// 4. Define a finite element space on the mesh. The polynomial order is
|
||||
// one (linear) by default, but this can be changed on the command line.
|
||||
H1_FECollection fec(order, dim);
|
||||
FiniteElementSpace fespace(&mesh, &fec);
|
||||
|
||||
// 5. Set device config parameters from the command line options.
|
||||
Device::Configure(device);
|
||||
Device::Print();
|
||||
|
||||
// 6. As in Example 1, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
@@ -169,7 +168,8 @@ int main(int argc, char *argv[])
|
||||
x.ProjectBdrCoefficient(zero, ess_bdr);
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
// 15. Assemble the stiffness matrix.
|
||||
// 15. Switch to the device and assemble the stiffness matrix.
|
||||
Device::Enable();
|
||||
a.Assemble();
|
||||
|
||||
// 16. Create the linear system: eliminate boundary conditions, constrain
|
||||
@@ -204,6 +204,7 @@ int main(int argc, char *argv[])
|
||||
// 18. After solving the linear system, reconstruct the solution as a
|
||||
// finite element GridFunction. Constrained nodes are interpolated
|
||||
// from true DOFs (it may therefore happen that x.Size() >= X.Size()).
|
||||
Device::Disable();
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 19. Send solution by socket to the GLVis server.
|
||||
|
||||
+16
-15
@@ -55,7 +55,7 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int order = 1;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
const char *device = "cpu";
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -65,7 +65,7 @@ int main(int argc, char *argv[])
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
args.AddOption(&device, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -85,19 +85,14 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution.
|
||||
// 4. Refine the serial mesh on all processors to increase the resolution.
|
||||
// Also project a NURBS mesh to a piecewise-quadratic curved mesh. Make
|
||||
// sure that the mesh is non-conforming.
|
||||
if (mesh->NURBSext)
|
||||
@@ -107,7 +102,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
mesh->EnsureNCMesh();
|
||||
|
||||
// 6. Define a parallel mesh by partitioning the serial mesh.
|
||||
// 5. Define a parallel mesh by partitioning the serial mesh.
|
||||
// Once the parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
@@ -117,11 +112,15 @@ int main(int argc, char *argv[])
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
|
||||
// 7. Define a finite element space on the mesh. The polynomial order is
|
||||
// 6. Define a finite element space on the mesh. The polynomial order is
|
||||
// one (linear) by default, but this can be changed on the command line.
|
||||
H1_FECollection fec(order, dim);
|
||||
ParFiniteElementSpace fespace(&pmesh, &fec);
|
||||
|
||||
// 7. Set device config parameters from the command line options.
|
||||
Device::Configure(device);
|
||||
if (myid == 0) { Device::Print(); }
|
||||
|
||||
// 8. As in Example 1p, we set up bilinear and linear forms corresponding to
|
||||
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
|
||||
// problem yet, this will be done in the main loop.
|
||||
@@ -201,10 +200,11 @@ int main(int argc, char *argv[])
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
b.Assemble();
|
||||
|
||||
// 15. Assemble the stiffness matrix. Note that MFEM doesn't care at this
|
||||
// point that the mesh is nonconforming and parallel. The FE space is
|
||||
// considered 'cut' along hanging edges/faces, and also across
|
||||
// processor boundaries.
|
||||
// 15. Switch to the device and assemble the stiffness matrix. Note that
|
||||
// MFEM doesn't care at this point that the mesh is nonconforming and
|
||||
// parallel. The FE space is considered 'cut' along hanging
|
||||
// edges/faces, and also across processor boundaries.
|
||||
Device::Enable();
|
||||
a.Assemble();
|
||||
|
||||
// 16. Create the parallel linear system: eliminate boundary conditions.
|
||||
@@ -232,6 +232,7 @@ int main(int argc, char *argv[])
|
||||
// 18. Switch back to the host and extract the parallel grid function
|
||||
// corresponding to the finite element approximation X. This is the
|
||||
// local solution on each processor.
|
||||
Device::Disable();
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 19. Send the solution by socket to a GLVis server.
|
||||
|
||||
+5
-4
@@ -22,9 +22,9 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 ex17\
|
||||
ex18 ex19 ex20 ex21
|
||||
ex18 ex19 ex20 ex22 ex23
|
||||
PAR_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p ex12p\
|
||||
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p
|
||||
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex22p ex23p
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
@@ -117,7 +117,7 @@ clean-build:
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
|
||||
@rm -rf Example5* Example9* Example15* Example16*
|
||||
@rm -rf Example5* Example9* Example15* Example16* Example23*
|
||||
@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_*
|
||||
@@ -125,4 +125,5 @@ clean-exec:
|
||||
@rm -f vortex-mesh.* vortex.mesh vortex-?-init.* vortex-?-final.*
|
||||
@rm -f deformation.* pressure.*
|
||||
@rm -f ex20.dat ex20p_?????.dat gnuplot_ex20.inp gnuplot_ex20p.inp
|
||||
@rm -f ex21*.mesh ex21*.sol ex21p_*.*
|
||||
@rm -f ex22*.mesh ex22*.sol ex22p_*.*
|
||||
@rm -f ex23.mesh ex23-mesh.* ex23-init.* ex23-final.*
|
||||
|
||||
@@ -27,11 +27,8 @@
|
||||
// method HyperelasticOperator::ImplicitSolve is the only
|
||||
// requirement for high-order implicit (SDIRK) time integration.
|
||||
// If using PETSc to solve the nonlinear problem, use the option
|
||||
// files provided (see rc_ex10p, rc_ex10p_mf, rc_ex10p_mfop) that
|
||||
// customize the Newton-Krylov method.
|
||||
// When option --jfnk is used, PETSc will use a Jacobian-free
|
||||
// Newton-Krylov method, using a user-defined preconditioner
|
||||
// constructed with the PetscPreconditionerFactory class.
|
||||
// file provided (rc_ex10p) that customizes the
|
||||
// Newton-Krylov method.
|
||||
//
|
||||
// We recommend viewing examples 2 and 9 before viewing this
|
||||
// example.
|
||||
@@ -89,15 +86,12 @@ protected:
|
||||
Solver *J_solver;
|
||||
/// Preconditioner for the Jacobian solve in the Newton method
|
||||
Solver *J_prec;
|
||||
/// Preconditioner factory for JFNK
|
||||
PetscPreconditionerFactory *J_factory;
|
||||
|
||||
mutable Vector z; // auxiliary vector
|
||||
|
||||
public:
|
||||
HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
|
||||
double visc, double mu, double K,
|
||||
bool use_petsc, bool petsc_use_jfnk);
|
||||
double visc, double mu, double K, bool use_petsc);
|
||||
|
||||
/// Compute the right-hand side of the ODE system.
|
||||
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
|
||||
@@ -142,21 +136,8 @@ public:
|
||||
virtual Operator &GetGradient(const Vector &k) const;
|
||||
|
||||
virtual ~ReducedSystemOperator();
|
||||
|
||||
};
|
||||
|
||||
/** Auxiliary class to provide preconditioners for matrix-free methods */
|
||||
class PreconditionerFactory : public PetscPreconditionerFactory
|
||||
{
|
||||
private:
|
||||
// const ReducedSystemOperator& op; // unused for now (generates warning)
|
||||
|
||||
public:
|
||||
PreconditionerFactory(const ReducedSystemOperator& op_, const string& name_)
|
||||
: PetscPreconditionerFactory(name_) /* , op(op_) */ {}
|
||||
virtual mfem::Solver* NewPreconditioner(const mfem::OperatorHandle&);
|
||||
virtual ~PreconditionerFactory() {}
|
||||
};
|
||||
|
||||
/** Function representing the elastic energy density for the given hyperelastic
|
||||
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
|
||||
@@ -206,7 +187,6 @@ int main(int argc, char *argv[])
|
||||
int vis_steps = 1;
|
||||
bool use_petsc = true;
|
||||
const char *petscrc_file = "";
|
||||
bool petsc_use_jfnk = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -241,9 +221,6 @@ int main(int argc, char *argv[])
|
||||
"Use or not PETSc to solve the nonlinear system.");
|
||||
args.AddOption(&petscrc_file, "-petscopts", "--petscopts",
|
||||
"PetscOptions file to use.");
|
||||
args.AddOption(&petsc_use_jfnk, "-jfnk", "--jfnk", "-no-jfnk",
|
||||
"--no-jfnk",
|
||||
"Use JFNK with user-defined preconditioner factory.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
@@ -367,8 +344,7 @@ int main(int argc, char *argv[])
|
||||
// 9. Initialize the hyperelastic operator, the GLVis visualization and print
|
||||
// the initial energies.
|
||||
HyperelasticOperator *oper = new HyperelasticOperator(fespace, ess_bdr, visc,
|
||||
mu, K, use_petsc,
|
||||
petsc_use_jfnk);
|
||||
mu, K, use_petsc);
|
||||
|
||||
socketstream vis_v, vis_w;
|
||||
if (visualization)
|
||||
@@ -544,7 +520,7 @@ Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
|
||||
add(*v, dt, k, w);
|
||||
add(*x, dt, w, z);
|
||||
localJ->Add(dt*dt, H->GetLocalGradient(z));
|
||||
// if we are using PETSc, the HypreParCSR Jacobian will be converted to
|
||||
// if we are using PETSc, the HypreParCSR jacobian will be converted to
|
||||
// PETSc's AIJ on the fly
|
||||
Jacobian = M->ParallelAssemble(localJ);
|
||||
delete localJ;
|
||||
@@ -561,8 +537,7 @@ ReducedSystemOperator::~ReducedSystemOperator()
|
||||
|
||||
HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
|
||||
Array<int> &ess_bdr, double visc,
|
||||
double mu, double K, bool use_petsc,
|
||||
bool use_petsc_factory)
|
||||
double mu, double K, bool use_petsc)
|
||||
: TimeDependentOperator(2*f.TrueVSize(), 0.0), fespace(f),
|
||||
M(&fespace), S(&fespace), H(&fespace),
|
||||
viscosity(visc), M_solver(f.GetComm()),
|
||||
@@ -615,8 +590,6 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
|
||||
J_minres->SetPreconditioner(*J_prec);
|
||||
J_solver = J_minres;
|
||||
|
||||
J_factory = NULL;
|
||||
|
||||
newton_solver.iterative_mode = false;
|
||||
newton_solver.SetSolver(*J_solver);
|
||||
newton_solver.SetOperator(*reduced_oper);
|
||||
@@ -627,20 +600,12 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
|
||||
}
|
||||
else
|
||||
{
|
||||
// if using PETSc, we create the same solver (Newton + MINRES + Jacobi)
|
||||
// if using PETSc, we create the same solver (NEWTON+MINRES+Jacobi)
|
||||
// by command line options (see rc_ex10p)
|
||||
J_solver = NULL;
|
||||
J_prec = NULL;
|
||||
J_factory = NULL;
|
||||
pnewton_solver = new PetscNonlinearSolver(f.GetComm(),
|
||||
*reduced_oper);
|
||||
|
||||
// we can setup a factory to construct a "physics-based" preconditioner
|
||||
if (use_petsc_factory)
|
||||
{
|
||||
J_factory = new PreconditionerFactory(*reduced_oper, "JFNK preconditioner");
|
||||
pnewton_solver->SetPreconditionerFactory(J_factory);
|
||||
}
|
||||
pnewton_solver->SetPrintLevel(1); // print Newton iterations
|
||||
pnewton_solver->SetRelTol(rel_tol);
|
||||
pnewton_solver->SetAbsTol(0.0);
|
||||
@@ -726,26 +691,12 @@ HyperelasticOperator::~HyperelasticOperator()
|
||||
{
|
||||
delete J_solver;
|
||||
delete J_prec;
|
||||
delete J_factory;
|
||||
delete reduced_oper;
|
||||
delete model;
|
||||
delete Mmat;
|
||||
delete pnewton_solver;
|
||||
}
|
||||
|
||||
// This method gets called every time we need a preconditioner "oh"
|
||||
// contains the PetscParMatrix that wraps the operator constructed in
|
||||
// the GetGradient() method (see also PetscSolver::SetJacobianType()).
|
||||
// In this example, we just return a customizable PetscPreconditioner
|
||||
// using that matrix. However, the OperatorHandle argument can be
|
||||
// ignored, and any "physics-based" solver can be constructed since we
|
||||
// have access to the HyperElasticOperator class.
|
||||
Solver* PreconditionerFactory::NewPreconditioner(const mfem::OperatorHandle& oh)
|
||||
{
|
||||
PetscParMatrix *pP;
|
||||
oh.Get(pP);
|
||||
return new PetscPreconditioner(*pP,"jfnk_");
|
||||
}
|
||||
|
||||
double ElasticEnergyCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
@@ -759,8 +710,8 @@ double ElasticEnergyCoefficient::Eval(ElementTransformation &T,
|
||||
|
||||
void InitialDeformation(const Vector &x, Vector &y)
|
||||
{
|
||||
// set the initial configuration to be the same as the reference,
|
||||
// stress free, configuration
|
||||
// set the initial configuration to be the same as the reference, stress
|
||||
// free, configuration
|
||||
y = x;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,6 @@ EX9_E_ARGS := -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts r
|
||||
EX9_ES_ARGS := -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts rc_ex9p_expl --no-step
|
||||
EX9_IS_ARGS := -m ../../data/periodic-hexagon.mesh --usepetsc --petscopts rc_ex9p_impl --implicit -tf 0.5
|
||||
EX10_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p -tf 30 -s 3 -rs 2 -dt 3
|
||||
EX10_MF_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_mf -tf 6 -s 3 -rs 0 -dt 3
|
||||
EX10_MFOP_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_mfop -tf 6 -s 3 -rs 0 -dt 3
|
||||
EX10_JFNK_ARGS := -m ../../data/beam-quad.mesh --usepetsc --petscopts rc_ex10p_jfnk --jfnk -tf 6 -s 3 -rs 0 -dt 3
|
||||
|
||||
ex1p-test-par: ex1p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX1_ARGS_W))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX1_ARGS_P))
|
||||
@@ -111,9 +107,6 @@ ex9p-test-par: ex9p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX9_IS_ARGS))
|
||||
ex10p-test-par: ex10p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_ARGS))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_MF_ARGS))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_MFOP_ARGS))
|
||||
@$(call mfem-test,$<, $(RUN_MPI), $(TESTNAME),$(EX10_JFNK_ARGS))
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# matrix-free Jacobian action, preconditioner constructed using PetscPreconditionerFactory
|
||||
-snes_monitor
|
||||
-snes_mf_operator
|
||||
-ksp_type minres
|
||||
-jfnk_pc_type jacobi
|
||||
@@ -1,4 +0,0 @@
|
||||
# matrix free -> no preconditioner
|
||||
-snes_monitor
|
||||
-snes_mf
|
||||
-ksp_type minres
|
||||
@@ -1,5 +0,0 @@
|
||||
# matrix-free Jacobian action, preconditioner constructed from the matrix obtained by the GetGradient() method
|
||||
-snes_monitor
|
||||
-snes_mf_operator
|
||||
-ksp_type minres
|
||||
-pc_type jacobi
|
||||
+2
-2
@@ -13,8 +13,7 @@ set(SRCS
|
||||
bilinearform.cpp
|
||||
bilinearform_ext.cpp
|
||||
bilininteg.cpp
|
||||
bilininteg_diffusion.cpp
|
||||
bilininteg_mass.cpp
|
||||
bilininteg_ext.cpp
|
||||
coefficient.cpp
|
||||
datacollection.cpp
|
||||
eltrans.cpp
|
||||
@@ -38,6 +37,7 @@ set(HDRS
|
||||
bilinearform.hpp
|
||||
bilinearform_ext.hpp
|
||||
bilininteg.hpp
|
||||
bilininteg_ext.hpp
|
||||
coefficient.hpp
|
||||
datacollection.hpp
|
||||
eltrans.hpp
|
||||
|
||||
+20
-11
@@ -55,7 +55,7 @@ void BilinearForm::AllocMat()
|
||||
|
||||
int *I = dof_dof.GetI();
|
||||
int *J = dof_dof.GetJ();
|
||||
double *data = new double[I[height]];
|
||||
double *data = mfem::New<double>(I[height]);
|
||||
|
||||
mat = new SparseMatrix(I, J, data, height, height, true, true, true);
|
||||
*mat = 0.0;
|
||||
@@ -122,7 +122,11 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
switch (assembly)
|
||||
{
|
||||
case AssemblyLevel::FULL:
|
||||
// ext = new FABilinearFormExtension(this);
|
||||
if (Device::IsEnabled())
|
||||
{
|
||||
mfem_error("Full assembly not supported yet in device mode!");
|
||||
// ext = new FABilinearFormExtension(this);
|
||||
}
|
||||
// Use the original BilinearForm implementation for now
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
@@ -340,6 +344,11 @@ void BilinearForm::AssembleBdrElementMatrix(
|
||||
|
||||
void BilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
if (Device::IsEnabled() && (assembly != AssemblyLevel::PARTIAL))
|
||||
{
|
||||
mfem_error("Chosen assembly level not supported yet in device mode!");
|
||||
}
|
||||
|
||||
if (ext)
|
||||
{
|
||||
ext->Assemble();
|
||||
@@ -583,6 +592,10 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
|
||||
if (ext)
|
||||
{
|
||||
if (P != NULL && assembly != AssemblyLevel::FULL && Device::IsEnabled())
|
||||
{
|
||||
P->BuildTranspose();
|
||||
}
|
||||
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
|
||||
return;
|
||||
}
|
||||
@@ -612,8 +625,8 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
{
|
||||
// A, X and B point to the same data as mat, x and b
|
||||
EliminateVDofsInRHS(ess_tdof_list, x, b);
|
||||
X.NewMemoryAndSize(x.GetMemory(), x.Size(), false);
|
||||
B.NewMemoryAndSize(b.GetMemory(), b.Size(), false);
|
||||
X.NewDataAndSize(x.GetData(), x.Size());
|
||||
B.NewDataAndSize(b.GetData(), b.Size());
|
||||
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
|
||||
}
|
||||
}
|
||||
@@ -714,10 +727,6 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
|
||||
else
|
||||
{
|
||||
// X and x point to the same data
|
||||
|
||||
// If the validity flags of X's Memory were changed (e.g. if it was
|
||||
// moved to device memory) then we need to tell x about that.
|
||||
x.SyncMemory(X);
|
||||
}
|
||||
}
|
||||
else // non-conforming space
|
||||
@@ -1197,7 +1206,7 @@ void MixedBilinearForm::ConformingAssemble()
|
||||
}
|
||||
|
||||
void MixedBilinearForm::EliminateTrialDofs (
|
||||
const Array<int> &bdr_attr_is_ess, const Vector &sol, Vector &rhs )
|
||||
Array<int> &bdr_attr_is_ess, const Vector &sol, Vector &rhs )
|
||||
{
|
||||
int i, j, k;
|
||||
Array<int> tr_vdofs, cols_marker (trial_fes -> GetVSize());
|
||||
@@ -1220,12 +1229,12 @@ void MixedBilinearForm::EliminateTrialDofs (
|
||||
}
|
||||
|
||||
void MixedBilinearForm::EliminateEssentialBCFromTrialDofs (
|
||||
const Array<int> &marked_vdofs, const Vector &sol, Vector &rhs)
|
||||
Array<int> &marked_vdofs, const Vector &sol, Vector &rhs)
|
||||
{
|
||||
mat -> EliminateCols (marked_vdofs, &sol, &rhs);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
|
||||
void MixedBilinearForm::EliminateTestDofs (Array<int> &bdr_attr_is_ess)
|
||||
{
|
||||
int i, j, k;
|
||||
Array<int> te_vdofs;
|
||||
|
||||
@@ -613,13 +613,13 @@ public:
|
||||
MixedBilinearForm becomes an operator on the conforming FE spaces. */
|
||||
void ConformingAssemble();
|
||||
|
||||
void EliminateTrialDofs(const Array<int> &bdr_attr_is_ess,
|
||||
void EliminateTrialDofs(Array<int> &bdr_attr_is_ess,
|
||||
const Vector &sol, Vector &rhs);
|
||||
|
||||
void EliminateEssentialBCFromTrialDofs(const Array<int> &marked_vdofs,
|
||||
void EliminateEssentialBCFromTrialDofs(Array<int> &marked_vdofs,
|
||||
const Vector &sol, Vector &rhs);
|
||||
|
||||
virtual void EliminateTestDofs(const Array<int> &bdr_attr_is_ess);
|
||||
virtual void EliminateTestDofs(Array<int> &bdr_attr_is_ess);
|
||||
|
||||
void Update();
|
||||
|
||||
|
||||
+135
-50
@@ -36,18 +36,16 @@ const Operator *BilinearFormExtension::GetRestriction() const
|
||||
|
||||
|
||||
// Data and methods for partially-assembled bilinear forms
|
||||
PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
|
||||
: BilinearFormExtension(form),
|
||||
trialFes(a->FESpace()), testFes(a->FESpace())
|
||||
PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form) :
|
||||
BilinearFormExtension(form),
|
||||
trialFes(a->FESpace()), testFes(a->FESpace()),
|
||||
localX(trialFes->GetNE() * trialFes->GetFE(0)->GetDof() * trialFes->GetVDim()),
|
||||
localY( testFes->GetNE() * testFes->GetFE(0)->GetDof() * testFes->GetVDim()),
|
||||
elem_restrict(new ElemRestriction(*a->FESpace())) { }
|
||||
|
||||
PABilinearFormExtension::~PABilinearFormExtension()
|
||||
{
|
||||
elem_restrict_lex = trialFes->GetElementRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC);
|
||||
if (elem_restrict_lex)
|
||||
{
|
||||
localX.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType());
|
||||
localY.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType());
|
||||
localY.UseDevice(true); // ensure 'localY = 0.0' is done on device
|
||||
}
|
||||
delete elem_restrict;
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::Assemble()
|
||||
@@ -56,7 +54,7 @@ void PABilinearFormExtension::Assemble()
|
||||
const int integratorCount = integrators.Size();
|
||||
for (int i = 0; i < integratorCount; ++i)
|
||||
{
|
||||
integrators[i]->AssemblePA(*a->FESpace());
|
||||
integrators[i]->Assemble(*a->FESpace());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,13 +64,12 @@ void PABilinearFormExtension::Update()
|
||||
height = width = fes->GetVSize();
|
||||
trialFes = fes;
|
||||
testFes = fes;
|
||||
elem_restrict_lex = trialFes->GetElementRestriction(
|
||||
ElementDofOrdering::LEXICOGRAPHIC);
|
||||
if (elem_restrict_lex)
|
||||
{
|
||||
localX.SetSize(elem_restrict_lex->Height());
|
||||
localY.SetSize(elem_restrict_lex->Height());
|
||||
}
|
||||
localX.SetSize(trialFes->GetNE() * trialFes->GetFE(0)->GetDof() *
|
||||
trialFes->GetVDim());
|
||||
localY.SetSize(testFes->GetNE() * testFes->GetFE(0)->GetDof() *
|
||||
testFes->GetVDim());
|
||||
delete elem_restrict;
|
||||
elem_restrict = new ElemRestriction(*fes);
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::FormSystemMatrix(const Array<int> &ess_tdof_list,
|
||||
@@ -100,52 +97,140 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
|
||||
elem_restrict->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
const int iSz = integrators.Size();
|
||||
if (elem_restrict_lex)
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
elem_restrict_lex->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultPA(localX, localY);
|
||||
}
|
||||
elem_restrict_lex->MultTranspose(localY, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
y.UseDevice(true); // typically this is a large vector, so store on device
|
||||
y = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultPA(x, y);
|
||||
}
|
||||
integrators[i]->MultAssembled(localX, localY);
|
||||
}
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
|
||||
void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
elem_restrict->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
const int iSz = integrators.Size();
|
||||
if (elem_restrict_lex)
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
elem_restrict_lex->Mult(x, localX);
|
||||
localY = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultTransposePA(localX, localY);
|
||||
}
|
||||
elem_restrict_lex->MultTranspose(localY, y);
|
||||
integrators[i]->MultAssembledTranspose(localX, localY);
|
||||
}
|
||||
else
|
||||
elem_restrict->MultTranspose(localY, y);
|
||||
}
|
||||
|
||||
|
||||
ElemRestriction::ElemRestriction(const FiniteElementSpace &f)
|
||||
: fes(f),
|
||||
ne(fes.GetNE()),
|
||||
vdim(fes.GetVDim()),
|
||||
byvdim(fes.GetOrdering() == Ordering::byVDIM),
|
||||
ndofs(fes.GetNDofs()),
|
||||
dof(fes.GetFE(0)->GetDof()),
|
||||
nedofs(ne*dof),
|
||||
offsets(ndofs+1),
|
||||
indices(ne*dof)
|
||||
{
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
y.UseDevice(true);
|
||||
y = 0.0;
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
const FiniteElement *fe = fes.GetFE(e);
|
||||
const TensorBasisElement* el =
|
||||
dynamic_cast<const TensorBasisElement*>(fe);
|
||||
if (el) { continue; }
|
||||
mfem_error("Finite element not supported with partial assembly");
|
||||
}
|
||||
const FiniteElement *fe = fes.GetFE(0);
|
||||
const TensorBasisElement* el = dynamic_cast<const TensorBasisElement*>(fe);
|
||||
const Array<int> &dof_map = el->GetDofMap();
|
||||
const bool dof_map_is_identity = (dof_map.Size()==0);
|
||||
const Table& e2dTable = fes.GetElementToDofTable();
|
||||
const int* elementMap = e2dTable.GetJ();
|
||||
// We'll be keeping a count of how many local nodes point to its global dof
|
||||
for (int i = 0; i <= ndofs; ++i)
|
||||
{
|
||||
offsets[i] = 0;
|
||||
}
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
for (int d = 0; d < dof; ++d)
|
||||
{
|
||||
integrators[i]->AddMultTransposePA(x, y);
|
||||
const int gid = elementMap[dof*e + d];
|
||||
++offsets[gid + 1];
|
||||
}
|
||||
}
|
||||
// Aggregate to find offsets for each global dof
|
||||
for (int i = 1; i <= ndofs; ++i)
|
||||
{
|
||||
offsets[i] += offsets[i - 1];
|
||||
}
|
||||
// For each global dof, fill in all local nodes that point to it
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
for (int d = 0; d < dof; ++d)
|
||||
{
|
||||
const int did = dof_map_is_identity?d:dof_map[d];
|
||||
const int gid = elementMap[dof*e + did];
|
||||
const int lid = dof*e + d;
|
||||
indices[offsets[gid]++] = lid;
|
||||
}
|
||||
}
|
||||
// We shifted the offsets vector by 1 by using it as a counter
|
||||
// Now we shift it back.
|
||||
for (int i = ndofs; i > 0; --i)
|
||||
{
|
||||
offsets[i] = offsets[i - 1];
|
||||
}
|
||||
offsets[0] = 0;
|
||||
}
|
||||
|
||||
void ElemRestriction::Mult(const Vector& x, Vector& y) const
|
||||
{
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const DeviceArray d_offsets(offsets, ndofs+1);
|
||||
const DeviceArray d_indices(indices, nedofs);
|
||||
const DeviceMatrix d_x(x, t?vd:ndofs, t?ndofs:vd);
|
||||
DeviceMatrix d_y(y, t?vd:nedofs, t?nedofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
const int nextOffset = d_offsets[i+1];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
const double dofValue = d_x(t?c:i,t?i:c);
|
||||
for (int j = offset; j < nextOffset; ++j)
|
||||
{
|
||||
const int idx_j = d_indices[j];
|
||||
d_y(t?c:idx_j,t?idx_j:c) = dofValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ElemRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
{
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const DeviceArray d_offsets(offsets, ndofs+1);
|
||||
const DeviceArray d_indices(indices, nedofs);
|
||||
const DeviceMatrix d_x(x, t?vd:nedofs, t?nedofs:vd);
|
||||
DeviceMatrix d_y(y, t?vd:ndofs, t?ndofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
const int nextOffset = d_offsets[i + 1];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
double dofValue = 0;
|
||||
for (int j = offset; j < nextOffset; ++j)
|
||||
{
|
||||
const int idx_j = d_indices[j];
|
||||
dofValue += d_x(t?c:idx_j,t?idx_j:c);
|
||||
}
|
||||
d_y(t?c:i,t?i:c) = dofValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+23
-11
@@ -14,16 +14,32 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fespace.hpp"
|
||||
#include "../general/device.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class BilinearForm;
|
||||
|
||||
/// Element restriction operator
|
||||
class ElemRestriction: public Operator
|
||||
{
|
||||
public:
|
||||
const FiniteElementSpace &fes;
|
||||
const int ne;
|
||||
const int vdim;
|
||||
const bool byvdim;
|
||||
const int ndofs;
|
||||
const int dof;
|
||||
const int nedofs;
|
||||
Array<int> offsets;
|
||||
Array<int> indices;
|
||||
public:
|
||||
ElemRestriction(const FiniteElementSpace&);
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Class extending the BilinearForm class to support the different
|
||||
AssemblyLevel%s. */
|
||||
class BilinearFormExtension : public Operator
|
||||
{
|
||||
protected:
|
||||
@@ -32,9 +48,6 @@ protected:
|
||||
public:
|
||||
BilinearFormExtension(BilinearForm *form);
|
||||
|
||||
virtual MemoryClass GetMemoryClass() const
|
||||
{ return Device::GetMemoryClass(); }
|
||||
|
||||
/// Get the finite element space prolongation matrix
|
||||
virtual const Operator *GetProlongation() const;
|
||||
|
||||
@@ -67,7 +80,6 @@ public:
|
||||
int copy_interior = 0) {}
|
||||
void Mult(const Vector &x, Vector &y) const {}
|
||||
void MultTranspose(const Vector &x, Vector &y) const {}
|
||||
void Update() {}
|
||||
~FABilinearFormExtension() {}
|
||||
};
|
||||
|
||||
@@ -87,7 +99,6 @@ public:
|
||||
int copy_interior = 0) {}
|
||||
void Mult(const Vector &x, Vector &y) const {}
|
||||
void MultTranspose(const Vector &x, Vector &y) const {}
|
||||
void Update() {}
|
||||
~EABilinearFormExtension() {}
|
||||
};
|
||||
|
||||
@@ -95,9 +106,9 @@ public:
|
||||
class PABilinearFormExtension : public BilinearFormExtension
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace *trialFes, *testFes; // Not owned
|
||||
const FiniteElementSpace *trialFes, *testFes;
|
||||
mutable Vector localX, localY;
|
||||
const Operator *elem_restrict_lex; // Not owned
|
||||
ElemRestriction *elem_restrict;
|
||||
|
||||
public:
|
||||
PABilinearFormExtension(BilinearForm*);
|
||||
@@ -112,6 +123,8 @@ public:
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
void Update();
|
||||
|
||||
~PABilinearFormExtension();
|
||||
};
|
||||
|
||||
/// Data and methods for matrix-free bilinear forms
|
||||
@@ -130,7 +143,6 @@ public:
|
||||
int copy_interior = 0) {}
|
||||
void Mult(const Vector &x, Vector &y) const {}
|
||||
void MultTranspose(const Vector &x, Vector &y) const {}
|
||||
void Update() {}
|
||||
~MFBilinearFormExtension() {}
|
||||
};
|
||||
|
||||
|
||||
+102
-55
@@ -19,20 +19,19 @@ using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
|
||||
void BilinearFormIntegrator::Assemble(const FiniteElementSpace&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::Assemble (...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
|
||||
void BilinearFormIntegrator::MultAssembled(Vector&, Vector&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::MultAssembled (...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
|
||||
void BilinearFormIntegrator::MultAssembledTranspose(Vector&, Vector&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose (...)\n"
|
||||
" is not implemented for this class.");
|
||||
@@ -379,7 +378,6 @@ void MixedScalarVectorIntegrator::AssembleElementMatrix2(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DiffusionIntegrator::AssembleElementMatrix
|
||||
( const FiniteElement &el, ElementTransformation &Trans,
|
||||
DenseMatrix &elmat )
|
||||
@@ -399,7 +397,29 @@ void DiffusionIntegrator::AssembleElementMatrix
|
||||
#endif
|
||||
elmat.SetSize(nd);
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order;
|
||||
if (el.Space() == FunctionSpace::Pk)
|
||||
{
|
||||
order = 2*el.GetOrder() - 2;
|
||||
}
|
||||
else
|
||||
// order = 2*el.GetOrder() - 2; // <-- this seems to work fine too
|
||||
{
|
||||
order = 2*el.GetOrder() + dim - 1;
|
||||
}
|
||||
|
||||
if (el.Space() == FunctionSpace::rQk)
|
||||
{
|
||||
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
ir = &IntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -455,7 +475,28 @@ void DiffusionIntegrator::AssembleElementMatrix2(
|
||||
#endif
|
||||
elmat.SetSize(te_nd, tr_nd);
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(trial_fe, test_fe);
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order;
|
||||
if (trial_fe.Space() == FunctionSpace::Pk)
|
||||
{
|
||||
order = trial_fe.GetOrder() + test_fe.GetOrder() - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
order = trial_fe.GetOrder() + test_fe.GetOrder() + dim - 1;
|
||||
}
|
||||
|
||||
if (trial_fe.Space() == FunctionSpace::rQk)
|
||||
{
|
||||
ir = &RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -510,7 +551,29 @@ void DiffusionIntegrator::AssembleElementVector(
|
||||
|
||||
elvect.SetSize(nd);
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order;
|
||||
if (el.Space() == FunctionSpace::Pk)
|
||||
{
|
||||
order = 2*el.GetOrder() - 2;
|
||||
}
|
||||
else
|
||||
// order = 2*el.GetOrder() - 2; // <-- this seems to work fine too
|
||||
{
|
||||
order = 2*el.GetOrder() + dim - 1;
|
||||
}
|
||||
|
||||
if (el.Space() == FunctionSpace::rQk)
|
||||
{
|
||||
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
ir = &IntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
}
|
||||
|
||||
elvect = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -670,27 +733,6 @@ double DiffusionIntegrator::ComputeFluxEnergy
|
||||
return energy;
|
||||
}
|
||||
|
||||
const IntegrationRule &DiffusionIntegrator::GetRule(
|
||||
const FiniteElement &trial_fe, const FiniteElement &test_fe)
|
||||
{
|
||||
int order;
|
||||
if (trial_fe.Space() == FunctionSpace::Pk)
|
||||
{
|
||||
order = trial_fe.GetOrder() + test_fe.GetOrder() - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// order = 2*el.GetOrder() - 2; // <-- this seems to work fine too
|
||||
order = trial_fe.GetOrder() + test_fe.GetOrder() + trial_fe.GetDim() - 1;
|
||||
}
|
||||
|
||||
if (trial_fe.Space() == FunctionSpace::rQk)
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
|
||||
void MassIntegrator::AssembleElementMatrix
|
||||
( const FiniteElement &el, ElementTransformation &Trans,
|
||||
@@ -706,7 +748,21 @@ void MassIntegrator::AssembleElementMatrix
|
||||
elmat.SetSize(nd);
|
||||
shape.SetSize(nd);
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, Trans);
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
// int order = 2 * el.GetOrder();
|
||||
int order = 2 * el.GetOrder() + Trans.OrderW();
|
||||
|
||||
if (el.Space() == FunctionSpace::rQk)
|
||||
{
|
||||
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
else
|
||||
{
|
||||
ir = &IntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -741,8 +797,13 @@ void MassIntegrator::AssembleElementMatrix2(
|
||||
shape.SetSize(tr_nd);
|
||||
te_shape.SetSize(te_nd);
|
||||
|
||||
const IntegrationRule *ir = IntRule ? IntRule :
|
||||
&GetRule(trial_fe, test_fe, Trans);
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
|
||||
|
||||
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -763,20 +824,6 @@ void MassIntegrator::AssembleElementMatrix2(
|
||||
}
|
||||
}
|
||||
|
||||
const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans)
|
||||
{
|
||||
// int order = trial_fe.GetOrder() + test_fe.GetOrder();
|
||||
const int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
|
||||
|
||||
if (trial_fe.Space() == FunctionSpace::rQk)
|
||||
{
|
||||
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
return IntRules.Get(trial_fe.GetGeomType(), order);
|
||||
}
|
||||
|
||||
|
||||
void BoundaryMassIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
@@ -848,7 +895,7 @@ void ConvectionIntegrator::AssembleElementMatrix(
|
||||
ir = &IntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
|
||||
Q->Eval(Q_ir, Trans, *ir);
|
||||
Q.Eval(Q_ir, Trans, *ir);
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -889,7 +936,7 @@ void GroupConvectionIntegrator::AssembleElementMatrix(
|
||||
ir = &IntRules.Get(el.GetGeomType(), order);
|
||||
}
|
||||
|
||||
Q->Eval(Q_nodal, Trans, el.GetNodes()); // sets the size of Q_nodal
|
||||
Q.Eval(Q_nodal, Trans, el.GetNodes()); // sets the size of Q_nodal
|
||||
|
||||
elmat = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
@@ -1370,7 +1417,7 @@ void DerivativeIntegrator::AssembleElementMatrix2 (
|
||||
dshapedxi(l) = dshapedxt(l,xi);
|
||||
}
|
||||
|
||||
shape *= Q->Eval(Trans,ip) * det * ip.weight;
|
||||
shape *= Q.Eval(Trans,ip) * det * ip.weight;
|
||||
AddMultVWt (shape, dshapedxi, elmat);
|
||||
}
|
||||
}
|
||||
@@ -3216,7 +3263,7 @@ ScalarProductInterpolator::AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
internal::ShapeCoefficient dom_shape_coeff(*Q, dom_fe);
|
||||
internal::ShapeCoefficient dom_shape_coeff(Q, dom_fe);
|
||||
|
||||
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
|
||||
|
||||
@@ -3251,7 +3298,7 @@ ScalarVectorProductInterpolator::AssembleElementMatrix2(
|
||||
}
|
||||
};
|
||||
|
||||
VShapeCoefficient dom_shape_coeff(*Q, dom_fe, Trans.GetSpaceDim());
|
||||
VShapeCoefficient dom_shape_coeff(Q, dom_fe, Trans.GetSpaceDim());
|
||||
|
||||
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
|
||||
|
||||
@@ -3289,7 +3336,7 @@ VectorScalarProductInterpolator::AssembleElementMatrix2(
|
||||
}
|
||||
};
|
||||
|
||||
VecShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
|
||||
VecShapeCoefficient dom_shape_coeff(VQ, dom_fe);
|
||||
|
||||
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
|
||||
|
||||
@@ -3336,11 +3383,11 @@ VectorCrossProductInterpolator::AssembleElementMatrix2(
|
||||
}
|
||||
};
|
||||
|
||||
VCrossVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
|
||||
VCrossVShapeCoefficient dom_shape_coeff(VQ, dom_fe);
|
||||
|
||||
if (ran_fe.GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
elmat.SetSize(ran_fe.GetDof()*VQ->GetVDim(),dom_fe.GetDof());
|
||||
elmat.SetSize(ran_fe.GetDof()*VQ.GetVDim(),dom_fe.GetDof());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3389,7 +3436,7 @@ VectorInnerProductInterpolator::AssembleElementMatrix2(
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
internal::VDotVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
|
||||
internal::VDotVShapeCoefficient dom_shape_coeff(VQ, dom_fe);
|
||||
|
||||
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
|
||||
|
||||
|
||||
+63
-128
@@ -15,6 +15,7 @@
|
||||
#include "../config/config.hpp"
|
||||
#include "nonlininteg.hpp"
|
||||
#include "fespace.hpp"
|
||||
#include "bilininteg_ext.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -22,45 +23,19 @@ namespace mfem
|
||||
/// Abstract base class BilinearFormIntegrator
|
||||
class BilinearFormIntegrator : public NonlinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
BilinearFormIntegrator(const IntegrationRule *ir = NULL)
|
||||
: NonlinearFormIntegrator(ir) { }
|
||||
public:
|
||||
BilinearFormIntegrator(const IntegrationRule *ir = NULL) :
|
||||
NonlinearFormIntegrator(ir) { }
|
||||
|
||||
public:
|
||||
// TODO: add support for other assembly levels (in addition to PA) and their
|
||||
// actions.
|
||||
|
||||
// TODO: for mixed meshes the quadrature rules to be used by methods like
|
||||
// AssemblePA() can be given as a QuadratureSpace, e.g. using a new method:
|
||||
// SetQuadratureSpace().
|
||||
|
||||
// TODO: the methods for the various assembly levels make sense even in the
|
||||
// base class NonlinearFormIntegrator, except that not all assembly levels
|
||||
// make sense for the action of the nonlinear operator (but they all make
|
||||
// sense for its Jacobian).
|
||||
|
||||
/// Method defining partial assembly.
|
||||
/** The result of the partial assembly is stored internally so that it can be
|
||||
used later in the methods AddMultPA() and AddMultTransposePA(). */
|
||||
virtual void AssemblePA(const FiniteElementSpace &fes);
|
||||
virtual void Assemble(const FiniteElementSpace&);
|
||||
|
||||
/// Method for partially assembled action.
|
||||
/** Perform the action of integrator on the input @a x and add the result to
|
||||
the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
|
||||
the element-wise discontinuous version of the FE space.
|
||||
|
||||
This method can be called only after the method AssemblePA() has been
|
||||
called. */
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void MultAssembled(Vector&, Vector&);
|
||||
|
||||
/// Method for partially assembled transposed action.
|
||||
/** Perform the transpose action of integrator on the input @a x and add the
|
||||
result to the output @a y. Both @a x and @a y are E-vectors, i.e. they
|
||||
represent the element-wise discontinuous version of the FE space.
|
||||
|
||||
This method can be called only after the method AssemblePA() has been
|
||||
called. */
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
virtual void MultAssembledTranspose(Vector&, Vector&);
|
||||
|
||||
/// Given a particular Finite Element computes the element matrix elmat.
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
@@ -309,10 +284,10 @@ protected:
|
||||
Vector & shape)
|
||||
{ trial_fe.CalcPhysShape(Trans, shape); }
|
||||
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
|
||||
Coefficient *Q;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector test_shape;
|
||||
Vector trial_shape;
|
||||
@@ -383,13 +358,13 @@ protected:
|
||||
DenseMatrix & shape)
|
||||
{ trial_fe.CalcVShape(Trans, shape); }
|
||||
|
||||
private:
|
||||
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
VectorCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
private:
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector V;
|
||||
Vector D;
|
||||
@@ -464,12 +439,12 @@ protected:
|
||||
Vector & shape)
|
||||
{ scalar_fe.CalcPhysShape(Trans, shape); }
|
||||
|
||||
private:
|
||||
|
||||
VectorCoefficient *VQ;
|
||||
bool transpose;
|
||||
bool cross_2d; // In 2D use a cross product rather than a dot product
|
||||
|
||||
private:
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector V;
|
||||
DenseMatrix vshape;
|
||||
@@ -1662,34 +1637,27 @@ protected:
|
||||
can be a scalar or a matrix coefficient. */
|
||||
class DiffusionIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
private:
|
||||
Vector vec, pointflux, shape;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
DenseMatrix dshape, dshapedxt, invdfdx, mq;
|
||||
DenseMatrix te_dshape, te_dshapedxt;
|
||||
#endif
|
||||
|
||||
Coefficient *Q;
|
||||
MatrixCoefficient *MQ;
|
||||
// PA extension
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
DofToQuad *maps;
|
||||
GeometryExtension *geom;
|
||||
int dim, ne, dofs1D, quad1D;
|
||||
Vector pa_data;
|
||||
|
||||
public:
|
||||
/// Construct a diffusion integrator with coefficient Q = 1
|
||||
DiffusionIntegrator() { Q = NULL; MQ = NULL; maps = NULL; geom = NULL; }
|
||||
|
||||
/// Construct a diffusion integrator with a scalar coefficient q
|
||||
DiffusionIntegrator(Coefficient &q)
|
||||
: Q(&q) { MQ = NULL; maps = NULL; geom = NULL; }
|
||||
DiffusionIntegrator (Coefficient &q) : Q(&q) { MQ = NULL; maps = NULL; geom = NULL; }
|
||||
|
||||
/// Construct a diffusion integrator with a matrix coefficient q
|
||||
DiffusionIntegrator(MatrixCoefficient &q)
|
||||
: MQ(&q) { Q = NULL; maps = NULL; geom = NULL; }
|
||||
DiffusionIntegrator (MatrixCoefficient &q) : MQ(&q) { Q = NULL; maps = NULL; geom = NULL; }
|
||||
|
||||
/** Given a particular Finite Element
|
||||
computes the element stiffness matrix elmat. */
|
||||
@@ -1717,12 +1685,11 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
Vector &flux, Vector *d_energy = NULL);
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace&);
|
||||
/// PA extension
|
||||
virtual void Assemble(const FiniteElementSpace&);
|
||||
virtual void MultAssembled(Vector&, Vector&);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe);
|
||||
virtual ~DiffusionIntegrator();
|
||||
};
|
||||
|
||||
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
|
||||
@@ -1734,15 +1701,13 @@ protected:
|
||||
#endif
|
||||
Coefficient *Q;
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
Vector vec;
|
||||
DofToQuad *maps;
|
||||
GeometryExtension *geom;
|
||||
int dim, ne, nq, dofs1D, quad1D;
|
||||
|
||||
public:
|
||||
MassIntegrator(const IntegrationRule *ir = NULL)
|
||||
: BilinearFormIntegrator(ir) { Q = NULL; maps = NULL; geom = NULL; }
|
||||
|
||||
/// Construct a mass integrator with coefficient q
|
||||
MassIntegrator(Coefficient &q, const IntegrationRule *ir = NULL)
|
||||
: BilinearFormIntegrator(ir), Q(&q) { maps = NULL; geom = NULL; }
|
||||
@@ -1756,14 +1721,11 @@ public:
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
/// PA extension
|
||||
virtual void Assemble(const FiniteElementSpace&);
|
||||
virtual void MultAssembled(Vector&, Vector&);
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace&);
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans);
|
||||
virtual ~MassIntegrator();
|
||||
};
|
||||
|
||||
class BoundaryMassIntegrator : public MassIntegrator
|
||||
@@ -1782,19 +1744,17 @@ public:
|
||||
/// alpha (q . grad u, v)
|
||||
class ConvectionIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
VectorCoefficient *Q;
|
||||
double alpha;
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
DenseMatrix dshape, adjJ, Q_ir;
|
||||
Vector shape, vec2, BdFidxT;
|
||||
#endif
|
||||
VectorCoefficient &Q;
|
||||
double alpha;
|
||||
|
||||
public:
|
||||
ConvectionIntegrator(VectorCoefficient &q, double a = 1.0)
|
||||
: Q(&q) { alpha = a; }
|
||||
: Q(q) { alpha = a; }
|
||||
virtual void AssembleElementMatrix(const FiniteElement &,
|
||||
ElementTransformation &,
|
||||
DenseMatrix &);
|
||||
@@ -1803,17 +1763,15 @@ public:
|
||||
/// alpha (q . grad u, v) using the "group" FE discretization
|
||||
class GroupConvectionIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
VectorCoefficient *Q;
|
||||
double alpha;
|
||||
|
||||
private:
|
||||
DenseMatrix dshape, adjJ, Q_nodal, grad;
|
||||
Vector shape;
|
||||
VectorCoefficient &Q;
|
||||
double alpha;
|
||||
|
||||
public:
|
||||
GroupConvectionIntegrator(VectorCoefficient &q, double a = 1.0)
|
||||
: Q(&q) { alpha = a; }
|
||||
: Q(q) { alpha = a; }
|
||||
virtual void AssembleElementMatrix(const FiniteElement &,
|
||||
ElementTransformation &,
|
||||
DenseMatrix &);
|
||||
@@ -1829,17 +1787,16 @@ private:
|
||||
Vector shape, te_shape, vec;
|
||||
DenseMatrix partelmat;
|
||||
DenseMatrix mcoeff;
|
||||
int Q_order;
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
int Q_order;
|
||||
|
||||
public:
|
||||
/// Construct an integrator with coefficient 1.0
|
||||
VectorMassIntegrator()
|
||||
: vdim(-1), Q_order(0), Q(NULL), VQ(NULL), MQ(NULL) { }
|
||||
: vdim(-1), Q(NULL), VQ(NULL), MQ(NULL), Q_order(0) { }
|
||||
/** Construct an integrator with scalar coefficient q.
|
||||
If possible, save memory by using a scalar integrator since
|
||||
the resulting matrix is block diagonal with the same diagonal
|
||||
@@ -1878,14 +1835,11 @@ public:
|
||||
does NOT depend on the ElementTransformation Trans. */
|
||||
class VectorFEDivergenceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
Coefficient *Q;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector divshape, shape;
|
||||
#endif
|
||||
|
||||
public:
|
||||
VectorFEDivergenceIntegrator() { Q = NULL; }
|
||||
VectorFEDivergenceIntegrator(Coefficient &q) { Q = &q; }
|
||||
@@ -1903,17 +1857,14 @@ public:
|
||||
This is equivalent to a weak divergence of the Nedelec basis functions. */
|
||||
class VectorFEWeakDivergenceIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
Coefficient *Q;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
DenseMatrix dshape;
|
||||
DenseMatrix dshapedxt;
|
||||
DenseMatrix vshape;
|
||||
DenseMatrix invdfdx;
|
||||
#endif
|
||||
|
||||
public:
|
||||
VectorFEWeakDivergenceIntegrator() { Q = NULL; }
|
||||
VectorFEWeakDivergenceIntegrator(Coefficient &q) { Q = &q; }
|
||||
@@ -1930,16 +1881,13 @@ public:
|
||||
test spaces are switched, assembles the form (u, curl v). */
|
||||
class VectorFECurlIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
Coefficient *Q;
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
DenseMatrix curlshapeTrial;
|
||||
DenseMatrix vshapeTest;
|
||||
DenseMatrix curlshapeTrial_dFT;
|
||||
#endif
|
||||
|
||||
public:
|
||||
VectorFECurlIntegrator() { Q = NULL; }
|
||||
VectorFECurlIntegrator(Coefficient &q) { Q = &q; }
|
||||
@@ -1952,19 +1900,17 @@ public:
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
|
||||
/// Class for integrating (Q D_i(u), v); u and v are scalars
|
||||
class DerivativeIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient* Q;
|
||||
|
||||
private:
|
||||
Coefficient & Q;
|
||||
int xi;
|
||||
DenseMatrix dshape, dshapedxt, invdfdx;
|
||||
Vector shape, dshapedxi;
|
||||
|
||||
public:
|
||||
DerivativeIntegrator(Coefficient &q, int i) : Q(&q), xi(i) { }
|
||||
DerivativeIntegrator(Coefficient &q, int i) : Q(q), xi(i) { }
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
@@ -1984,8 +1930,6 @@ private:
|
||||
DenseMatrix curlshape, curlshape_dFt, M;
|
||||
DenseMatrix vshape, projcurl;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
@@ -2019,8 +1963,6 @@ private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
DenseMatrix dshape_hat, dshape, curlshape, Jadj, grad_hat, grad;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
|
||||
public:
|
||||
@@ -2042,6 +1984,9 @@ public:
|
||||
class VectorFEMassIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
MatrixCoefficient *MQ;
|
||||
void Init(Coefficient *q, VectorCoefficient *vq, MatrixCoefficient *mq)
|
||||
{ Q = q; VQ = vq; MQ = mq; }
|
||||
|
||||
@@ -2053,11 +1998,6 @@ private:
|
||||
DenseMatrix trial_vshape;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
public:
|
||||
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
|
||||
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL); }
|
||||
@@ -2080,10 +2020,9 @@ public:
|
||||
scalar FE space; p is also in a (different) scalar FE space. */
|
||||
class VectorDivergenceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
Vector shape;
|
||||
Vector divshape;
|
||||
DenseMatrix dshape;
|
||||
@@ -2104,10 +2043,9 @@ public:
|
||||
/// (Q div u, div v) for RT elements
|
||||
class DivDivIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector divshape;
|
||||
#endif
|
||||
@@ -2129,10 +2067,9 @@ public:
|
||||
diffusion matrix in each diagonal block. */
|
||||
class VectorDiffusionIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
Coefficient *Q;
|
||||
|
||||
private:
|
||||
DenseMatrix Jinv;
|
||||
DenseMatrix dshape;
|
||||
DenseMatrix gshape;
|
||||
@@ -2157,11 +2094,10 @@ public:
|
||||
using multiple copies of a scalar FE space. */
|
||||
class ElasticityIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
double q_lambda, q_mu;
|
||||
Coefficient *lambda, *mu;
|
||||
|
||||
private:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector shape;
|
||||
DenseMatrix dshape, gshape, pelmat;
|
||||
@@ -2218,12 +2154,11 @@ public:
|
||||
points. */
|
||||
class DGTraceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
private:
|
||||
Coefficient *rho;
|
||||
VectorCoefficient *u;
|
||||
double alpha, beta;
|
||||
|
||||
private:
|
||||
Vector shape1, shape2;
|
||||
|
||||
public:
|
||||
@@ -2510,7 +2445,7 @@ public:
|
||||
class ScalarProductInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
ScalarProductInterpolator(Coefficient & sc) : Q(&sc) { }
|
||||
ScalarProductInterpolator(Coefficient & sc) : Q(sc) { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
@@ -2518,7 +2453,7 @@ public:
|
||||
DenseMatrix &elmat);
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
Coefficient &Q;
|
||||
};
|
||||
|
||||
/** Interpolator of a scalar coefficient multiplied by a vector field onto
|
||||
@@ -2528,14 +2463,14 @@ class ScalarVectorProductInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
ScalarVectorProductInterpolator(Coefficient & sc)
|
||||
: Q(&sc) { }
|
||||
: Q(sc) { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
Coefficient &Q;
|
||||
};
|
||||
|
||||
/** Interpolator of a vector coefficient multiplied by a scalar field onto
|
||||
@@ -2545,14 +2480,14 @@ class VectorScalarProductInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
VectorScalarProductInterpolator(VectorCoefficient & vc)
|
||||
: VQ(&vc) { }
|
||||
: VQ(vc) { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
protected:
|
||||
VectorCoefficient *VQ;
|
||||
VectorCoefficient &VQ;
|
||||
};
|
||||
|
||||
/** Interpolator of the cross product between a vector coefficient and an
|
||||
@@ -2562,14 +2497,14 @@ class VectorCrossProductInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
VectorCrossProductInterpolator(VectorCoefficient & vc)
|
||||
: VQ(&vc) { }
|
||||
: VQ(vc) { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &nd_fe,
|
||||
const FiniteElement &rt_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
protected:
|
||||
VectorCoefficient *VQ;
|
||||
VectorCoefficient &VQ;
|
||||
};
|
||||
|
||||
/** Interpolator of the inner product between a vector coefficient and an
|
||||
@@ -2578,14 +2513,14 @@ protected:
|
||||
class VectorInnerProductInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
VectorInnerProductInterpolator(VectorCoefficient & vc) : VQ(&vc) { }
|
||||
VectorInnerProductInterpolator(VectorCoefficient & vc) : VQ(vc) { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &rt_fe,
|
||||
const FiniteElement &l2_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
protected:
|
||||
VectorCoefficient *VQ;
|
||||
VectorCoefficient &VQ;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
||||
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
||||
// reserved. See file COPYRIGHT for details.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability see http://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU Lesser General Public License (as published by the Free
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
#ifndef MFEM_BILININTEG_EXT
|
||||
#define MFEM_BILININTEG_EXT
|
||||
|
||||
#include "fespace.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// GeometryExtension
|
||||
class GeometryExtension
|
||||
{
|
||||
public:
|
||||
Array<int> eMap;
|
||||
Array<double> nodes;
|
||||
Array<double> X, J, invJ, detJ;
|
||||
static GeometryExtension* Get(const FiniteElementSpace&,
|
||||
const IntegrationRule&);
|
||||
static GeometryExtension* Get(const FiniteElementSpace&,
|
||||
const IntegrationRule&,
|
||||
const Vector&);
|
||||
static void ReorderByVDim(const GridFunction*);
|
||||
static void ReorderByNodes(const GridFunction*);
|
||||
};
|
||||
|
||||
/// DofToQuad
|
||||
class DofToQuad
|
||||
{
|
||||
private:
|
||||
std::string hash;
|
||||
public:
|
||||
~DofToQuad();
|
||||
void operator=(DofToQuad&);
|
||||
void operator=(DofToQuad const&);
|
||||
public:
|
||||
Array<double> W, B, G, Bt, Gt;
|
||||
public:
|
||||
static DofToQuad* Get(const FiniteElementSpace&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* Get(const FiniteElementSpace&,
|
||||
const FiniteElementSpace&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* Get(const FiniteElement&,
|
||||
const FiniteElement&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* GetTensorMaps(const FiniteElement&,
|
||||
const FiniteElement&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* GetD2QTensorMaps(const FiniteElement&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* GetSimplexMaps(const FiniteElement&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* GetSimplexMaps(const FiniteElement&,
|
||||
const FiniteElement&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
static DofToQuad* GetD2QSimplexMaps(const FiniteElement&,
|
||||
const IntegrationRule&,
|
||||
const bool = false);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,822 +0,0 @@
|
||||
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
||||
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
||||
// reserved. See file COPYRIGHT for details.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability see http://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU Lesser General Public License (as published by the Free
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
#include "../general/forall.hpp"
|
||||
#include "bilininteg.hpp"
|
||||
#include "gridfunc.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// PA Mass Integrator
|
||||
|
||||
// PA Mass Assemble kernel
|
||||
void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
{
|
||||
// Assuming the same element type
|
||||
Mesh *mesh = fes.GetMesh();
|
||||
if (mesh->GetNE() == 0) { return; }
|
||||
const FiniteElement &el = *fes.GetFE(0);
|
||||
ElementTransformation *T = mesh->GetElementTransformation(0);
|
||||
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T);
|
||||
dim = mesh->Dimension();
|
||||
ne = fes.GetMesh()->GetNE();
|
||||
nq = ir->GetNPoints();
|
||||
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::COORDINATES |
|
||||
GeometricFactors::JACOBIANS);
|
||||
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
|
||||
dofs1D = maps->ndof;
|
||||
quad1D = maps->nqpt;
|
||||
pa_data.SetSize(ne*nq, Device::GetMemoryType());
|
||||
ConstantCoefficient *const_coeff = dynamic_cast<ConstantCoefficient*>(Q);
|
||||
// TODO: other types of coefficients ...
|
||||
if (dim==1) { MFEM_ABORT("Not supported yet... stay tuned!"); }
|
||||
if (dim==2)
|
||||
{
|
||||
double constant = 0.0;
|
||||
if (const_coeff)
|
||||
{
|
||||
constant = const_coeff->constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Coefficient type not supported");
|
||||
}
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
auto w = ir->GetWeights().Read();
|
||||
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
for (int q = 0; q < NQ; ++q)
|
||||
{
|
||||
const double J11 = J(q,0,0,e);
|
||||
const double J12 = J(q,1,0,e);
|
||||
const double J21 = J(q,0,1,e);
|
||||
const double J22 = J(q,1,1,e);
|
||||
const double detJ = (J11*J22)-(J21*J12);
|
||||
v(q,e) = w[q] * constant * detJ;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (dim==3)
|
||||
{
|
||||
double constant = 0.0;
|
||||
if (const_coeff)
|
||||
{
|
||||
constant = const_coeff->constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Coefficient type not supported");
|
||||
}
|
||||
const int NE = ne;
|
||||
const int NQ = nq;
|
||||
auto W = ir->GetWeights().Read();
|
||||
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
|
||||
auto v = Reshape(pa_data.Write(), NQ,NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
for (int q = 0; q < NQ; ++q)
|
||||
{
|
||||
const double J11 = J(q,0,0,e), J12 = J(q,0,1,e), J13 = J(q,0,2,e);
|
||||
const double J21 = J(q,1,0,e), J22 = J(q,1,1,e), J23 = J(q,1,2,e);
|
||||
const double J31 = J(q,2,0,e), J32 = J(q,2,1,e), J33 = J(q,2,2,e);
|
||||
const double detJ = J11 * (J22 * J33 - J32 * J23) -
|
||||
/* */ J21 * (J12 * J33 - J32 * J13) +
|
||||
/* */ J31 * (J12 * J23 - J22 * J13);
|
||||
v(q,e) = W[q] * constant * detJ;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_OCCA
|
||||
// OCCA PA Mass Apply 2D kernel
|
||||
static void OccaPAMassApply2D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const Array<double> &B,
|
||||
const Array<double> &Bt,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
occa::properties props;
|
||||
props["defines/D1D"] = D1D;
|
||||
props["defines/Q1D"] = Q1D;
|
||||
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
|
||||
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
|
||||
const occa::memory o_op = OccaMemoryRead(op.GetMemory(), op.Size());
|
||||
const occa::memory o_x = OccaMemoryRead(x.GetMemory(), x.Size());
|
||||
occa::memory o_y = OccaMemoryReadWrite(y.GetMemory(), y.Size());
|
||||
const occa_id_t id = std::make_pair(D1D,Q1D);
|
||||
if (!Device::Allows(Backend::OCCA_CUDA))
|
||||
{
|
||||
static occa_kernel_t OccaMassApply2D_cpu;
|
||||
if (OccaMassApply2D_cpu.find(id) == OccaMassApply2D_cpu.end())
|
||||
{
|
||||
const occa::kernel MassApply2D_CPU =
|
||||
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
|
||||
"MassApply2D_CPU", props);
|
||||
OccaMassApply2D_cpu.emplace(id, MassApply2D_CPU);
|
||||
}
|
||||
OccaMassApply2D_cpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
static occa_kernel_t OccaMassApply2D_gpu;
|
||||
if (OccaMassApply2D_gpu.find(id) == OccaMassApply2D_gpu.end())
|
||||
{
|
||||
const occa::kernel MassApply2D_GPU =
|
||||
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
|
||||
"MassApply2D_GPU", props);
|
||||
OccaMassApply2D_gpu.emplace(id, MassApply2D_GPU);
|
||||
}
|
||||
OccaMassApply2D_gpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
|
||||
}
|
||||
}
|
||||
|
||||
// OCCA PA Mass Apply 3D kernel
|
||||
static void OccaPAMassApply3D(const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const Array<double> &B,
|
||||
const Array<double> &Bt,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
occa::properties props;
|
||||
props["defines/D1D"] = D1D;
|
||||
props["defines/Q1D"] = Q1D;
|
||||
const occa::memory o_B = OccaMemoryRead(B.GetMemory(), B.Size());
|
||||
const occa::memory o_Bt = OccaMemoryRead(Bt.GetMemory(), Bt.Size());
|
||||
const occa::memory o_op = OccaMemoryRead(op.GetMemory(), op.Size());
|
||||
const occa::memory o_x = OccaMemoryRead(x.GetMemory(), x.Size());
|
||||
occa::memory o_y = OccaMemoryReadWrite(y.GetMemory(), y.Size());
|
||||
const occa_id_t id = std::make_pair(D1D,Q1D);
|
||||
if (!Device::Allows(Backend::OCCA_CUDA))
|
||||
{
|
||||
static occa_kernel_t OccaMassApply3D_cpu;
|
||||
if (OccaMassApply3D_cpu.find(id) == OccaMassApply3D_cpu.end())
|
||||
{
|
||||
const occa::kernel MassApply3D_CPU =
|
||||
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
|
||||
"MassApply3D_CPU", props);
|
||||
OccaMassApply3D_cpu.emplace(id, MassApply3D_CPU);
|
||||
}
|
||||
OccaMassApply3D_cpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
static occa_kernel_t OccaMassApply3D_gpu;
|
||||
if (OccaMassApply3D_gpu.find(id) == OccaMassApply3D_gpu.end())
|
||||
{
|
||||
const occa::kernel MassApply3D_GPU =
|
||||
mfem::OccaDev().buildKernel("occa://mfem/fem/occa.okl",
|
||||
"MassApply3D_GPU", props);
|
||||
OccaMassApply3D_gpu.emplace(id, MassApply3D_GPU);
|
||||
}
|
||||
OccaMassApply3D_gpu.at(id)(NE, o_B, o_Bt, o_op, o_x, o_y);
|
||||
}
|
||||
}
|
||||
#endif // MFEM_USE_OCCA
|
||||
|
||||
template<const int T_D1D = 0,
|
||||
const int T_Q1D = 0>
|
||||
static void PAMassApply2D(const int NE,
|
||||
const Array<double> &_B,
|
||||
const Array<double> &_Bt,
|
||||
const Vector &_op,
|
||||
const Vector &_x,
|
||||
Vector &_y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
|
||||
auto B = Reshape(_B.Read(), Q1D, D1D);
|
||||
auto Bt = Reshape(_Bt.Read(), D1D, Q1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
|
||||
auto x = Reshape(_x.Read(), D1D, D1D, NE);
|
||||
auto y = Reshape(_y.ReadWrite(), D1D, D1D, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
// the following variables are evaluated at compile time
|
||||
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
|
||||
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
|
||||
double sol_xy[max_Q1D][max_Q1D];
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xy[qy][qx] = 0.0;
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
double sol_x[max_Q1D];
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
sol_x[qy] = 0.0;
|
||||
}
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const double s = x(dx,dy,e);
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_x[qx] += B(qx,dx)* s;
|
||||
}
|
||||
}
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const double d2q = B(qy,dy);
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xy[qy][qx] += d2q * sol_x[qx];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xy[qy][qx] *= op(qx,qy,e);
|
||||
}
|
||||
}
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
double sol_x[max_D1D];
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
sol_x[dx] = 0.0;
|
||||
}
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const double s = sol_xy[qy][qx];
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
sol_x[dx] += Bt(dx,qx) * s;
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
const double q2d = Bt(dy,qy);
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
y(dx,dy,e) += q2d * sol_x[dx];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<const int T_D1D = 0,
|
||||
const int T_Q1D = 0,
|
||||
const int T_NBZ = 0>
|
||||
static void SmemPAMassApply2D(const int NE,
|
||||
const Array<double> &_b,
|
||||
const Array<double> &_bt,
|
||||
const Vector &_op,
|
||||
const Vector &_x,
|
||||
Vector &_y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
|
||||
MFEM_VERIFY(D1D <= MD1, "");
|
||||
MFEM_VERIFY(Q1D <= MQ1, "");
|
||||
auto b = Reshape(_b.Read(), Q1D, D1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
|
||||
auto x = Reshape(_x.Read(), D1D, D1D, NE);
|
||||
auto y = Reshape(_y.ReadWrite(), D1D, D1D, NE);
|
||||
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
|
||||
{
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int NBZ = T_NBZ ? T_NBZ : 1;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
|
||||
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
|
||||
MFEM_SHARED double BBt[MQ1*MD1];
|
||||
double (*B)[MD1] = (double (*)[MD1]) BBt;
|
||||
double (*Bt)[MQ1] = (double (*)[MQ1]) BBt;
|
||||
MFEM_SHARED double sm0[NBZ][MDQ*MDQ];
|
||||
MFEM_SHARED double sm1[NBZ][MDQ*MDQ];
|
||||
double (*X)[MD1] = (double (*)[MD1]) (sm0 + tidz);
|
||||
double (*DQ)[MQ1] = (double (*)[MQ1]) (sm1 + tidz);
|
||||
double (*QQ)[MQ1] = (double (*)[MQ1]) (sm0 + tidz);
|
||||
double (*QD)[MD1] = (double (*)[MD1]) (sm1 + tidz);
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
X[dy][dx] = x(dx,dy,e);
|
||||
}
|
||||
}
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
B[q][d] = b(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double dq = 0.0;
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
dq += X[dy][dx] * B[qx][dx];
|
||||
}
|
||||
DQ[dy][qx] = dq;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double qq = 0.0;
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
qq += DQ[dy][qx] * B[qy][dy];
|
||||
}
|
||||
QQ[qy][qx] = qq * op(qx, qy, e);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
Bt[d][q] = b(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double dq = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
dq += QQ[qy][qx] * Bt[dx][qx];
|
||||
}
|
||||
QD[qy][dx] = dq;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double dd = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
dd += (QD[qy][dx] * Bt[dy][qy]);
|
||||
}
|
||||
y(dx, dy, e) += dd;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<const int T_D1D = 0,
|
||||
const int T_Q1D = 0>
|
||||
static void PAMassApply3D(const int NE,
|
||||
const Array<double> &_B,
|
||||
const Array<double> &_Bt,
|
||||
const Vector &_op,
|
||||
const Vector &_x,
|
||||
Vector &_y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
|
||||
auto B = Reshape(_B.Read(), Q1D, D1D);
|
||||
auto Bt = Reshape(_Bt.Read(), D1D, Q1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
|
||||
auto x = Reshape(_x.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(_y.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
|
||||
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
|
||||
double sol_xyz[max_Q1D][max_Q1D][max_Q1D];
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xyz[qz][qy][qx] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
double sol_xy[max_Q1D][max_Q1D];
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xy[qy][qx] = 0.0;
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
double sol_x[max_Q1D];
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_x[qx] = 0;
|
||||
}
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
const double s = x(dx,dy,dz,e);
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_x[qx] += B(qx,dx) * s;
|
||||
}
|
||||
}
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const double wy = B(qy,dy);
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xy[qy][qx] += wy * sol_x[qx];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
const double wz = B(qz,dz);
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xyz[qz][qy][qx] += wz * sol_xy[qy][qx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
sol_xyz[qz][qy][qx] *= op(qx,qy,qz,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
double sol_xy[max_D1D][max_D1D];
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
sol_xy[dy][dx] = 0;
|
||||
}
|
||||
}
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
double sol_x[max_D1D];
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
sol_x[dx] = 0;
|
||||
}
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const double s = sol_xyz[qz][qy][qx];
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
sol_x[dx] += Bt(dx,qx) * s;
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
const double wy = Bt(dy,qy);
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
sol_xy[dy][dx] += wy * sol_x[dx];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
const double wz = Bt(dz,qz);
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
y(dx,dy,dz,e) += wz * sol_xy[dy][dx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<const int T_D1D = 0,
|
||||
const int T_Q1D = 0>
|
||||
static void SmemPAMassApply3D(const int NE,
|
||||
const Array<double> &_b,
|
||||
const Array<double> &_bt,
|
||||
const Vector &_op,
|
||||
const Vector &_x,
|
||||
Vector &_y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int M1Q = T_Q1D ? T_Q1D : MAX_Q1D;
|
||||
constexpr int M1D = T_D1D ? T_D1D : MAX_D1D;
|
||||
MFEM_VERIFY(D1D <= M1D, "");
|
||||
MFEM_VERIFY(Q1D <= M1Q, "");
|
||||
auto b = Reshape(_b.Read(), Q1D, D1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
|
||||
auto x = Reshape(_x.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(_y.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
|
||||
{
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
|
||||
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
|
||||
MFEM_SHARED double sDQ[MQ1*MD1];
|
||||
double (*B)[MD1] = (double (*)[MD1]) sDQ;
|
||||
double (*Bt)[MQ1] = (double (*)[MQ1]) sDQ;
|
||||
MFEM_SHARED double sm0[MDQ*MDQ*MDQ];
|
||||
MFEM_SHARED double sm1[MDQ*MDQ*MDQ];
|
||||
double (*X)[MD1][MD1] = (double (*)[MD1][MD1]) sm0;
|
||||
double (*DDQ)[MD1][MQ1] = (double (*)[MD1][MQ1]) sm1;
|
||||
double (*DQQ)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) sm0;
|
||||
double (*QQQ)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) sm1;
|
||||
double (*QQD)[MQ1][MD1] = (double (*)[MQ1][MD1]) sm0;
|
||||
double (*QDD)[MD1][MD1] = (double (*)[MD1][MD1]) sm1;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
B[q][d] = b(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u = 0.0;
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
u += X[dz][dy][dx] * B[qx][dx];
|
||||
}
|
||||
DDQ[dz][dy][qx] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u = 0.0;
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
u += DDQ[dz][dy][qx] * B[qy][dy];
|
||||
}
|
||||
DQQ[dz][qy][qx] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
{
|
||||
double u = 0.0;
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
u += DQQ[dz][qy][qx] * B[qz][dz];
|
||||
}
|
||||
QQQ[qz][qy][qx] = u * op(qx,qy,qz,e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(d,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
{
|
||||
Bt[d][q] = b(q,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
u += QQQ[qz][qy][qx] * Bt[dx][qx];
|
||||
}
|
||||
QQD[qz][qy][dx] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qz,z,Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
u += QQD[qz][qy][dx] * Bt[dy][qy];
|
||||
}
|
||||
QDD[qz][dy][dx] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
double u = 0.0;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u += QDD[qz][dy][dx] * Bt[dz][qz];
|
||||
}
|
||||
y(dx,dy,dz,e) += u;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void PAMassApply(const int dim,
|
||||
const int D1D,
|
||||
const int Q1D,
|
||||
const int NE,
|
||||
const Array<double> &B,
|
||||
const Array<double> &Bt,
|
||||
const Vector &op,
|
||||
const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
#ifdef MFEM_USE_OCCA
|
||||
if (DeviceCanUseOcca())
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
OccaPAMassApply2D(D1D, Q1D, NE, B, Bt, op, x, y);
|
||||
return;
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
OccaPAMassApply3D(D1D, Q1D, NE, B, Bt, op, x, y);
|
||||
return;
|
||||
}
|
||||
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
|
||||
}
|
||||
#endif // MFEM_USE_OCCA
|
||||
|
||||
if (Device::Allows(Backend::RAJA_CUDA))
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return PAMassApply2D<2,2>(NE, B, Bt, op, x, y);
|
||||
case 0x33: return PAMassApply2D<3,3>(NE, B, Bt, op, x, y);
|
||||
case 0x44: return PAMassApply2D<4,4>(NE, B, Bt, op, x, y);
|
||||
case 0x55: return PAMassApply2D<5,5>(NE, B, Bt, op, x, y);
|
||||
case 0x66: return PAMassApply2D<6,6>(NE, B, Bt, op, x, y);
|
||||
case 0x77: return PAMassApply2D<7,7>(NE, B, Bt, op, x, y);
|
||||
case 0x88: return PAMassApply2D<8,8>(NE, B, Bt, op, x, y);
|
||||
case 0x99: return PAMassApply2D<9,9>(NE, B, Bt, op, x, y);
|
||||
default: return PAMassApply2D(NE, B, Bt, op, x, y, D1D, Q1D);
|
||||
}
|
||||
}
|
||||
if (dim == 3)
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x23: return PAMassApply3D<2,3>(NE, B, Bt, op, x, y);
|
||||
case 0x34: return PAMassApply3D<3,4>(NE, B, Bt, op, x, y);
|
||||
case 0x45: return PAMassApply3D<4,5>(NE, B, Bt, op, x, y);
|
||||
case 0x56: return PAMassApply3D<5,6>(NE, B, Bt, op, x, y);
|
||||
case 0x67: return PAMassApply3D<6,7>(NE, B, Bt, op, x, y);
|
||||
case 0x78: return PAMassApply3D<7,8>(NE, B, Bt, op, x, y);
|
||||
case 0x89: return PAMassApply3D<8,9>(NE, B, Bt, op, x, y);
|
||||
default: return PAMassApply3D(NE, B, Bt, op, x, y, D1D, Q1D);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x22: return SmemPAMassApply2D<2,2,16>(NE, B, Bt, op, x, y);
|
||||
case 0x33: return SmemPAMassApply2D<3,3,16>(NE, B, Bt, op, x, y);
|
||||
case 0x44: return SmemPAMassApply2D<4,4,8>(NE, B, Bt, op, x, y);
|
||||
case 0x55: return SmemPAMassApply2D<5,5,8>(NE, B, Bt, op, x, y);
|
||||
case 0x66: return SmemPAMassApply2D<6,6,4>(NE, B, Bt, op, x, y);
|
||||
case 0x77: return SmemPAMassApply2D<7,7,4>(NE, B, Bt, op, x, y);
|
||||
case 0x88: return SmemPAMassApply2D<8,8,2>(NE, B, Bt, op, x, y);
|
||||
case 0x99: return SmemPAMassApply2D<9,9,2>(NE, B, Bt, op, x, y);
|
||||
default: return PAMassApply2D(NE, B, Bt, op, x, y, D1D, Q1D);
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch ((D1D << 4 ) | Q1D)
|
||||
{
|
||||
case 0x23: return SmemPAMassApply3D<2,3>(NE, B, Bt, op, x, y);
|
||||
case 0x34: return SmemPAMassApply3D<3,4>(NE, B, Bt, op, x, y);
|
||||
case 0x45: return SmemPAMassApply3D<4,5>(NE, B, Bt, op, x, y);
|
||||
case 0x56: return SmemPAMassApply3D<5,6>(NE, B, Bt, op, x, y);
|
||||
case 0x67: return SmemPAMassApply3D<6,7>(NE, B, Bt, op, x, y);
|
||||
case 0x78: return SmemPAMassApply3D<7,8>(NE, B, Bt, op, x, y);
|
||||
case 0x89: return SmemPAMassApply3D<8,9>(NE, B, Bt, op, x, y);
|
||||
default: return PAMassApply3D(NE, B, Bt, op, x, y, D1D, Q1D);
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
|
||||
void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
{
|
||||
PAMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
+12
-20
@@ -28,6 +28,11 @@ double PWConstCoefficient::Eval(ElementTransformation & T,
|
||||
return (constants(att-1));
|
||||
}
|
||||
|
||||
DeviceFunctionCoefficientPtr FunctionCoefficient::GetDeviceFunction()
|
||||
{
|
||||
return DeviceFunction;
|
||||
}
|
||||
|
||||
double FunctionCoefficient::Eval(ElementTransformation & T,
|
||||
const IntegrationPoint & ip)
|
||||
{
|
||||
@@ -40,6 +45,10 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
|
||||
{
|
||||
return ((*Function)(transip));
|
||||
}
|
||||
else if (DeviceFunction)
|
||||
{
|
||||
return ((*DeviceFunction)(Vector3(x)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*TDFunction)(transip, GetTime());
|
||||
@@ -125,27 +134,19 @@ void VectorFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
}
|
||||
|
||||
VectorArrayCoefficient::VectorArrayCoefficient (int dim)
|
||||
: VectorCoefficient(dim), Coeff(dim), ownCoeff(dim)
|
||||
: VectorCoefficient(dim), Coeff(dim)
|
||||
{
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
Coeff[i] = NULL;
|
||||
ownCoeff[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VectorArrayCoefficient::Set(int i, Coefficient *c, bool own)
|
||||
{
|
||||
if (ownCoeff[i]) { delete Coeff[i]; }
|
||||
Coeff[i] = c;
|
||||
ownCoeff[i] = own;
|
||||
}
|
||||
|
||||
VectorArrayCoefficient::~VectorArrayCoefficient()
|
||||
{
|
||||
for (int i = 0; i < vdim; i++)
|
||||
{
|
||||
if (ownCoeff[i]) { delete Coeff[i]; }
|
||||
delete Coeff[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,26 +318,17 @@ MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
|
||||
: MatrixCoefficient (dim)
|
||||
{
|
||||
Coeff.SetSize(height*width);
|
||||
ownCoeff.SetSize(height*width);
|
||||
for (int i = 0; i < (height*width); i++)
|
||||
{
|
||||
Coeff[i] = NULL;
|
||||
ownCoeff[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MatrixArrayCoefficient::Set(int i, int j, Coefficient * c, bool own)
|
||||
{
|
||||
if (ownCoeff[i*width+j]) { delete Coeff[i*width+j]; }
|
||||
Coeff[i*width+j] = c;
|
||||
ownCoeff[i*width+j] = own;
|
||||
}
|
||||
|
||||
MatrixArrayCoefficient::~MatrixArrayCoefficient ()
|
||||
{
|
||||
for (int i=0; i < height*width; i++)
|
||||
{
|
||||
if (ownCoeff[i]) { delete Coeff[i]; }
|
||||
delete Coeff[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-8
@@ -112,6 +112,7 @@ public:
|
||||
const IntegrationPoint &ip);
|
||||
};
|
||||
|
||||
typedef double (*DeviceFunctionCoefficientPtr)(const Vector3&);
|
||||
|
||||
/// class for C-function coefficient
|
||||
class FunctionCoefficient : public Coefficient
|
||||
@@ -119,6 +120,7 @@ class FunctionCoefficient : public Coefficient
|
||||
protected:
|
||||
double (*Function)(const Vector &);
|
||||
double (*TDFunction)(const Vector &, double);
|
||||
double (*DeviceFunction)(const Vector3&);
|
||||
|
||||
public:
|
||||
/// Define a time-independent coefficient from a C-function
|
||||
@@ -126,6 +128,7 @@ public:
|
||||
{
|
||||
Function = f;
|
||||
TDFunction = NULL;
|
||||
DeviceFunction = NULL;
|
||||
}
|
||||
|
||||
/// Define a time-dependent coefficient from a C-function
|
||||
@@ -133,6 +136,16 @@ public:
|
||||
{
|
||||
Function = NULL;
|
||||
TDFunction = tdf;
|
||||
DeviceFunction = NULL;
|
||||
}
|
||||
|
||||
/// Define a time-independent coefficient from a C-function using
|
||||
/// Vector3 instead of a Vector.
|
||||
FunctionCoefficient(double (*df)(const Vector3 &))
|
||||
{
|
||||
Function = NULL;
|
||||
TDFunction = NULL;
|
||||
DeviceFunction = df;
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Define a time-independent coefficient from a C-function
|
||||
@@ -142,6 +155,7 @@ public:
|
||||
{
|
||||
Function = reinterpret_cast<double(*)(const Vector&)>(f);
|
||||
TDFunction = NULL;
|
||||
DeviceFunction = NULL;
|
||||
}
|
||||
|
||||
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
|
||||
@@ -151,11 +165,17 @@ public:
|
||||
{
|
||||
Function = NULL;
|
||||
TDFunction = reinterpret_cast<double(*)(const Vector&,double)>(tdf);
|
||||
DeviceFunction = NULL;
|
||||
}
|
||||
|
||||
/// Evaluate coefficient
|
||||
virtual double Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/// Return the coefficient's C-function that uses Vector3.
|
||||
/// Warning: for now, the returned function can only be used on the
|
||||
/// host inside a MFEM_FORALL.
|
||||
DeviceFunctionCoefficientPtr GetDeviceFunction();
|
||||
};
|
||||
|
||||
class GridFunction;
|
||||
@@ -369,7 +389,6 @@ class VectorArrayCoefficient : public VectorCoefficient
|
||||
{
|
||||
private:
|
||||
Array<Coefficient*> Coeff;
|
||||
Array<bool> ownCoeff;
|
||||
|
||||
public:
|
||||
/// Construct vector of dim coefficients.
|
||||
@@ -381,7 +400,7 @@ public:
|
||||
Coefficient **GetCoeffs() { return Coeff; }
|
||||
|
||||
/// Sets coefficient in the vector.
|
||||
void Set(int i, Coefficient *c, bool own=true);
|
||||
void Set(int i, Coefficient *c) { delete Coeff[i]; Coeff[i] = c; }
|
||||
|
||||
/// Evaluates i'th component of the vector.
|
||||
double Eval(int i, ElementTransformation &T, const IntegrationPoint &ip)
|
||||
@@ -501,13 +520,9 @@ public:
|
||||
void SetDeltaCoefficient(const DeltaCoefficient& _d) { d = _d; }
|
||||
/// Return the associated scalar DeltaCoefficient.
|
||||
DeltaCoefficient& GetDeltaCoefficient() { return d; }
|
||||
|
||||
void SetScale(double s) { d.SetScale(s); }
|
||||
void SetDirection(const Vector& _d);
|
||||
|
||||
void SetDeltaCenter(const Vector& center) { d.SetDeltaCenter(center); }
|
||||
void GetDeltaCenter(Vector& center) { d.GetDeltaCenter(center); }
|
||||
|
||||
/** @brief Return the specified direction vector multiplied by the value
|
||||
returned by DeltaCoefficient::EvalDelta() of the associated scalar
|
||||
DeltaCoefficient. */
|
||||
@@ -633,7 +648,6 @@ class MatrixArrayCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
Array<Coefficient *> Coeff;
|
||||
Array<bool> ownCoeff;
|
||||
|
||||
public:
|
||||
|
||||
@@ -641,7 +655,7 @@ public:
|
||||
|
||||
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
|
||||
|
||||
void Set(int i, int j, Coefficient * c, bool own=true);
|
||||
void Set(int i, int j, Coefficient * c) { delete Coeff[i*width+j]; Coeff[i*width+j] = c; }
|
||||
|
||||
double Eval(int i, int j, ElementTransformation &T, const IntegrationPoint &ip)
|
||||
{ return Coeff[i*width+j] ? Coeff[i*width+j] -> Eval(T, ip, GetTime()) : 0.0; }
|
||||
|
||||
+4
-17
@@ -108,7 +108,6 @@ DataCollection::DataCollection(const std::string& collection_name, Mesh *mesh_)
|
||||
precision = precision_default;
|
||||
pad_digits_cycle = pad_digits_rank = pad_digits_default;
|
||||
format = SERIAL_FORMAT; // use serial mesh format
|
||||
compression = false;
|
||||
error = NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -162,14 +161,6 @@ void DataCollection::SetFormat(int fmt)
|
||||
format = fmt;
|
||||
}
|
||||
|
||||
void DataCollection::SetCompression(bool comp)
|
||||
{
|
||||
compression = comp;
|
||||
#ifdef MFEM_USE_GZSTREAM
|
||||
MFEM_ASSERT(!compression, "GZStream not enabled in MFEM build.");
|
||||
#endif
|
||||
}
|
||||
|
||||
void DataCollection::SetPrefixPath(const std::string& prefix)
|
||||
{
|
||||
if (!prefix.empty())
|
||||
@@ -228,8 +219,7 @@ void DataCollection::SaveMesh()
|
||||
}
|
||||
|
||||
std::string mesh_name = GetMeshFileName();
|
||||
const char *mode = (compression) ? "zwb6" : "w";
|
||||
ofgzstream mesh_file(mesh_name.c_str(), mode);
|
||||
std::ofstream mesh_file(mesh_name.c_str());
|
||||
mesh_file.precision(precision);
|
||||
#ifdef MFEM_USE_MPI
|
||||
const ParMesh *pmesh = dynamic_cast<const ParMesh*>(mesh);
|
||||
@@ -277,9 +267,7 @@ const
|
||||
|
||||
void DataCollection::SaveOneField(const FieldMapIterator &it)
|
||||
{
|
||||
const char *mode = (compression) ? "zwb6" : "w";
|
||||
ofgzstream field_file(GetFieldFileName(it->first).c_str(), mode);
|
||||
|
||||
std::ofstream field_file(GetFieldFileName(it->first).c_str());
|
||||
field_file.precision(precision);
|
||||
(it->second)->Save(field_file);
|
||||
if (!field_file)
|
||||
@@ -291,8 +279,7 @@ void DataCollection::SaveOneField(const FieldMapIterator &it)
|
||||
|
||||
void DataCollection::SaveOneQField(const QFieldMapIterator &it)
|
||||
{
|
||||
const char *mode = (compression) ? "zwb6" : "w";
|
||||
ofgzstream q_field_file(GetFieldFileName(it->first).c_str(), mode);
|
||||
std::ofstream q_field_file(GetFieldFileName(it->first).c_str());
|
||||
q_field_file.precision(precision);
|
||||
(it->second)->Save(q_field_file);
|
||||
if (!q_field_file)
|
||||
@@ -589,7 +576,7 @@ void VisItDataCollection::LoadFields()
|
||||
it != field_info_map.end(); ++it)
|
||||
{
|
||||
std::string fname = path_left + it->first + path_right;
|
||||
ifgzstream file(fname.c_str());
|
||||
std::ifstream file(fname.c_str());
|
||||
// TODO: in parallel, check for errors on all processors
|
||||
if (!file)
|
||||
{
|
||||
|
||||
@@ -205,7 +205,6 @@ protected:
|
||||
|
||||
/// Output mesh format: see the #Format enumeration
|
||||
int format;
|
||||
bool compression;
|
||||
|
||||
/// Should the collection delete its mesh and fields
|
||||
bool own_data;
|
||||
@@ -347,9 +346,6 @@ public:
|
||||
validation. */
|
||||
virtual void SetFormat(int fmt);
|
||||
|
||||
/// Set the flag for use of gz compressed files
|
||||
void SetCompression(bool comp);
|
||||
|
||||
/// Set the path where the DataCollection will be saved.
|
||||
void SetPrefixPath(const std::string &prefix);
|
||||
|
||||
|
||||
-105
@@ -203,22 +203,6 @@ void FiniteElement::CalcPhysDShape(ElementTransformation &Trans,
|
||||
Mult(vshape, Trans.InverseJacobian(), dshape);
|
||||
}
|
||||
|
||||
const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &,
|
||||
DofToQuad::Mode) const
|
||||
{
|
||||
mfem_error("FiniteElement::GetDofToQuad(...) is not implemented for "
|
||||
"this element!");
|
||||
return *dof2quad_array[0]; // suppress a warning
|
||||
}
|
||||
|
||||
FiniteElement::~FiniteElement()
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
delete dof2quad_array[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScalarFiniteElement::NodalLocalInterpolation (
|
||||
ElementTransformation &Trans, DenseMatrix &I,
|
||||
@@ -294,95 +278,6 @@ void ScalarFiniteElement::ScalarLocalInterpolation(
|
||||
}
|
||||
}
|
||||
|
||||
const DofToQuad &ScalarFiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const
|
||||
{
|
||||
MFEM_VERIFY(mode == DofToQuad::FULL, "invalid mode requested");
|
||||
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
const DofToQuad &d2q = *dof2quad_array[i];
|
||||
if (d2q.IntRule == &ir && d2q.mode == mode) { return d2q; }
|
||||
}
|
||||
|
||||
DofToQuad *d2q = new DofToQuad;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
d2q->FE = this;
|
||||
d2q->IntRule = &ir;
|
||||
d2q->mode = mode;
|
||||
d2q->ndof = Dof;
|
||||
d2q->nqpt = nqpt;
|
||||
d2q->B.SetSize(nqpt*Dof);
|
||||
d2q->Bt.SetSize(Dof*nqpt);
|
||||
d2q->G.SetSize(nqpt*Dim*Dof);
|
||||
d2q->Gt.SetSize(Dof*nqpt*Dim);
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector c_shape(Dof);
|
||||
DenseMatrix vshape(Dof, Dim);
|
||||
#endif
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
CalcShape(ip, c_shape);
|
||||
for (int j = 0; j < Dof; j++)
|
||||
{
|
||||
d2q->B[i+nqpt*j] = d2q->Bt[j+Dof*i] = c_shape(j);
|
||||
}
|
||||
CalcDShape(ip, vshape);
|
||||
for (int d = 0; d < Dim; d++)
|
||||
{
|
||||
for (int j = 0; j < Dof; j++)
|
||||
{
|
||||
d2q->G[i+nqpt*(d+Dim*j)] = d2q->Gt[j+Dof*(i+nqpt*d)] = vshape(j,d);
|
||||
}
|
||||
}
|
||||
}
|
||||
dof2quad_array.Append(d2q);
|
||||
return *d2q;
|
||||
}
|
||||
|
||||
// protected method
|
||||
const DofToQuad &ScalarFiniteElement::GetTensorDofToQuad(
|
||||
const TensorBasisElement &tb,
|
||||
const IntegrationRule &ir, DofToQuad::Mode mode) const
|
||||
{
|
||||
MFEM_VERIFY(mode == DofToQuad::TENSOR, "invalid mode requested");
|
||||
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
const DofToQuad &d2q = *dof2quad_array[i];
|
||||
if (d2q.IntRule == &ir && d2q.mode == mode) { return d2q; }
|
||||
}
|
||||
|
||||
DofToQuad *d2q = new DofToQuad;
|
||||
const Poly_1D::Basis &basis_1d = tb.GetBasis1D();
|
||||
const int ndof = Order + 1;
|
||||
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/Dim) + 0.5);
|
||||
d2q->FE = this;
|
||||
d2q->IntRule = &ir;
|
||||
d2q->mode = mode;
|
||||
d2q->ndof = ndof;
|
||||
d2q->nqpt = nqpt;
|
||||
d2q->B.SetSize(nqpt*ndof);
|
||||
d2q->Bt.SetSize(ndof*nqpt);
|
||||
d2q->G.SetSize(nqpt*ndof);
|
||||
d2q->Gt.SetSize(ndof*nqpt);
|
||||
Vector val(ndof), grad(ndof);
|
||||
for (int i = 0; i < nqpt; i++)
|
||||
{
|
||||
// The first 'nqpt' points in 'ir' have the same x-coordinates as those
|
||||
// of the 1D rule.
|
||||
basis_1d.Eval(ir.IntPoint(i).x, val, grad);
|
||||
for (int j = 0; j < ndof; j++)
|
||||
{
|
||||
d2q->B[i+nqpt*j] = d2q->Bt[j+ndof*i] = val(j);
|
||||
d2q->G[i+nqpt*j] = d2q->Gt[j+ndof*i] = grad(j);
|
||||
}
|
||||
}
|
||||
dof2quad_array.Append(d2q);
|
||||
return *d2q;
|
||||
}
|
||||
|
||||
|
||||
void NodalFiniteElement::ProjectCurl_2D(
|
||||
const FiniteElement &fe, ElementTransformation &Trans,
|
||||
|
||||
+2
-124
@@ -116,92 +116,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @brief Structure representing the matrices/tensors needed to evaluate (in
|
||||
reference space) the values, gradients, divergences, or curls of a
|
||||
FiniteElement at a the quadrature points of a given IntegrationRule. */
|
||||
/** Object of this type are typically created and owned by the respective
|
||||
FiniteElement object. */
|
||||
class DofToQuad
|
||||
{
|
||||
public:
|
||||
/// The FiniteElement that created and owns this object.
|
||||
/** This pointer is not owned. */
|
||||
const class FiniteElement *FE;
|
||||
|
||||
/** @brief IntegrationRule that defines the quadrature points at which the
|
||||
basis functions of the #FE are evaluated. */
|
||||
/** This pointer is not owned. */
|
||||
const IntegrationRule *IntRule;
|
||||
|
||||
/// Type of data stored in the arrays #B, #Bt, #G, and #Gt.
|
||||
enum Mode
|
||||
{
|
||||
/** @brief Full multidimensional representation which does not use tensor
|
||||
product structure. The ordering of the degrees of freedom is as
|
||||
defined by #FE */
|
||||
FULL,
|
||||
|
||||
/** @brief Tensor product representation using 1D matrices/tensors with
|
||||
dimensions using 1D number of quadrature points and degrees of
|
||||
freedom. */
|
||||
/** When representing a vector-valued FiniteElement, two DofToQuad objects
|
||||
are used to describe the "closed" and "open" 1D basis functions
|
||||
(TODO). */
|
||||
TENSOR
|
||||
};
|
||||
|
||||
/// Describes the contents of the #B, #Bt, #G, and #Gt arrays, see #Mode.
|
||||
Mode mode;
|
||||
|
||||
/** @brief Number of degrees of freedom = number of basis functions. When
|
||||
#mode is TENSOR, this is the 1D number. */
|
||||
int ndof;
|
||||
|
||||
/** @brief Number of quadrature points. When #mode is TENSOR, this is the 1D
|
||||
number. */
|
||||
int nqpt;
|
||||
|
||||
/// Basis functions evaluated at quadrature points.
|
||||
/** The storage layout is column-major with dimensions:
|
||||
- #nqpt x #ndof, for scalar elements, or
|
||||
- #nqpt x dim x #ndof, for vector elements, (TODO)
|
||||
|
||||
where
|
||||
|
||||
- dim = dimension of the finite element reference space when #mode is
|
||||
FULL, and dim = 1 when #mode is TENSOR. */
|
||||
Array<double> B;
|
||||
|
||||
/// Transpose of #B.
|
||||
/** The storage layout is column-major with dimensions:
|
||||
- #ndof x #nqpt, for scalar elements, or
|
||||
- #ndof x #nqpt x dim, for vector elements (TODO). */
|
||||
Array<double> Bt;
|
||||
|
||||
/** @brief Gradients/divergences/curls of basis functions evaluated at
|
||||
quadrature points. */
|
||||
/** The storage layout is column-major with dimensions:
|
||||
- #nqpt x dim x #ndof, for scalar elements, or
|
||||
- #nqpt x #ndof, for H(div) vector elements (TODO), or
|
||||
- #nqpt x cdim x #ndof, for H(curl) vector elements (TODO),
|
||||
|
||||
where
|
||||
|
||||
- dim = dimension of the finite element reference space when #mode is
|
||||
FULL, and 1 when #mode is TENSOR,
|
||||
- cdim = 1/1/3 in 1D/2D/3D, respectively, when #mode is FULL, and cdim =
|
||||
1 when #mode is TENSOR. */
|
||||
Array<double> G;
|
||||
|
||||
/// Transpose of #G.
|
||||
/** The storage layout is column-major with dimensions:
|
||||
- #ndof x #nqpt x dim, for scalar elements, or
|
||||
- #ndof x #nqpt, for H(div) vector elements (TODO), or
|
||||
- #ndof x #nqpt x cdim, for H(curl) vector elements (TODO). */
|
||||
Array<double> Gt;
|
||||
};
|
||||
|
||||
// Base and derived classes for finite elements
|
||||
|
||||
/// Describes the space on each element
|
||||
class FunctionSpace
|
||||
@@ -221,10 +136,6 @@ class VectorCoefficient;
|
||||
class MatrixCoefficient;
|
||||
class KnotVector;
|
||||
|
||||
|
||||
// Base and derived classes for finite elements
|
||||
|
||||
|
||||
/// Abstract class for Finite Elements
|
||||
class FiniteElement
|
||||
{
|
||||
@@ -241,10 +152,6 @@ protected:
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
mutable DenseMatrix vshape; // Dof x Dim
|
||||
#endif
|
||||
/// Container for all DofToQuad objects created by the FiniteElement.
|
||||
/** Multiple DofToQuad objects may be needed when different quadrature rules
|
||||
or different DofToQuad::Mode are used. */
|
||||
mutable Array<DofToQuad*> dof2quad_array;
|
||||
|
||||
public:
|
||||
/// Enumeration for RangeType and DerivRangeType
|
||||
@@ -510,13 +417,7 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &div) const;
|
||||
|
||||
/** Return a DofToQuad structure corresponding to the given IntegrationRule
|
||||
using the given DofToQuad::Mode. */
|
||||
/** See the documentation for DofToQuad for more details. */
|
||||
virtual const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const;
|
||||
|
||||
virtual ~FiniteElement();
|
||||
virtual ~FiniteElement () { }
|
||||
|
||||
static bool IsClosedType(int b_type)
|
||||
{
|
||||
@@ -563,10 +464,6 @@ protected:
|
||||
return static_cast<const ScalarFiniteElement &>(fe);
|
||||
}
|
||||
|
||||
const DofToQuad &GetTensorDofToQuad(const class TensorBasisElement &tb,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const;
|
||||
|
||||
public:
|
||||
ScalarFiniteElement(int D, Geometry::Type G, int Do, int O,
|
||||
int F = FunctionSpace::Pk)
|
||||
@@ -597,9 +494,6 @@ public:
|
||||
void ScalarLocalInterpolation(ElementTransformation &Trans,
|
||||
DenseMatrix &I,
|
||||
const ScalarFiniteElement &fine_fe) const;
|
||||
|
||||
virtual const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const;
|
||||
};
|
||||
|
||||
class NodalFiniteElement : public ScalarFiniteElement
|
||||
@@ -1856,14 +1750,6 @@ class NodalTensorFiniteElement : public NodalFiniteElement,
|
||||
public:
|
||||
NodalTensorFiniteElement(const int dims, const int p, const int btype,
|
||||
const DofMapType dmtype);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const
|
||||
{
|
||||
return (mode == DofToQuad::FULL) ?
|
||||
ScalarFiniteElement::GetDofToQuad(ir, mode) :
|
||||
ScalarFiniteElement::GetTensorDofToQuad(*this, ir, mode);
|
||||
}
|
||||
};
|
||||
|
||||
class PositiveTensorFiniteElement : public PositiveFiniteElement,
|
||||
@@ -1872,14 +1758,6 @@ class PositiveTensorFiniteElement : public PositiveFiniteElement,
|
||||
public:
|
||||
PositiveTensorFiniteElement(const int dims, const int p,
|
||||
const DofMapType dmtype);
|
||||
|
||||
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const
|
||||
{
|
||||
return (mode == DofToQuad::FULL) ?
|
||||
ScalarFiniteElement::GetDofToQuad(ir, mode) :
|
||||
ScalarFiniteElement::GetTensorDofToQuad(*this, ir, mode);
|
||||
}
|
||||
};
|
||||
|
||||
class H1_SegmentElement : public NodalTensorFiniteElement
|
||||
|
||||
@@ -1875,7 +1875,6 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
L2_Elements[Geometry::CUBE]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::PRISM]->SetMapType(map_type);
|
||||
// All trace element use the default Gauss-Legendre nodal points
|
||||
Tr_Elements[Geometry::SEGMENT] = new L2_SegmentElement(p);
|
||||
Tr_Elements[Geometry::TRIANGLE] = new L2_TriangleElement(p);
|
||||
Tr_Elements[Geometry::SQUARE] = new L2_QuadrilateralElement(p);
|
||||
|
||||
|
||||
+6
-557
@@ -12,7 +12,6 @@
|
||||
// Implementation of FiniteElementSpace
|
||||
|
||||
#include "../general/text.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include "../mesh/mesh_headers.hpp"
|
||||
#include "fem.hpp"
|
||||
|
||||
@@ -386,7 +385,6 @@ void FiniteElementSpace::MarkerToList(const Array<int> &marker,
|
||||
Array<int> &list)
|
||||
{
|
||||
int num_marked = 0;
|
||||
marker.HostRead(); // make sure we can read the array on host
|
||||
for (int i = 0; i < marker.Size(); i++)
|
||||
{
|
||||
if (marker[i]) { num_marked++; }
|
||||
@@ -654,9 +652,9 @@ void FiniteElementSpace::BuildConformingInterpolation() const
|
||||
// create the conforming restriction matrix cR
|
||||
int *cR_J;
|
||||
{
|
||||
int *cR_I = new int[n_true_dofs+1];
|
||||
double *cR_A = new double[n_true_dofs];
|
||||
cR_J = new int[n_true_dofs];
|
||||
int *cR_I = mfem::New<int>(n_true_dofs+1);
|
||||
double *cR_A = mfem::New<double>(n_true_dofs);
|
||||
cR_J = mfem::New<int>(n_true_dofs);
|
||||
for (int i = 0; i < n_true_dofs; i++)
|
||||
{
|
||||
cR_I[i] = i;
|
||||
@@ -734,8 +732,6 @@ void FiniteElementSpace::BuildConformingInterpolation() const
|
||||
MakeVDimMatrix(*cP);
|
||||
MakeVDimMatrix(*cR);
|
||||
}
|
||||
|
||||
if (Device::IsEnabled()) { cP->BuildTranspose(); }
|
||||
}
|
||||
|
||||
void FiniteElementSpace::MakeVDimMatrix(SparseMatrix &mat) const
|
||||
@@ -786,57 +782,6 @@ int FiniteElementSpace::GetNConformingDofs() const
|
||||
return P ? (P->Width() / vdim) : ndofs;
|
||||
}
|
||||
|
||||
const Operator *FiniteElementSpace::GetElementRestriction(
|
||||
ElementDofOrdering e_ordering) const
|
||||
{
|
||||
// Check if we have a discontinuous space using the FE collection:
|
||||
const L2_FECollection *dg_space = dynamic_cast<const L2_FECollection*>(fec);
|
||||
if (dg_space) { return NULL; }
|
||||
// TODO: support other DG collections.
|
||||
if (e_ordering == ElementDofOrdering::LEXICOGRAPHIC)
|
||||
{
|
||||
if (L2E_lex.Ptr() == NULL)
|
||||
{
|
||||
L2E_lex.Reset(new ElementRestriction(*this, e_ordering));
|
||||
}
|
||||
return L2E_lex.Ptr();
|
||||
}
|
||||
// e_ordering == ElementDofOrdering::NATIVE
|
||||
if (L2E_nat.Ptr() == NULL)
|
||||
{
|
||||
L2E_nat.Reset(new ElementRestriction(*this, e_ordering));
|
||||
}
|
||||
return L2E_nat.Ptr();
|
||||
}
|
||||
|
||||
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
|
||||
const IntegrationRule &ir) const
|
||||
{
|
||||
for (int i = 0; i < E2Q_array.Size(); i++)
|
||||
{
|
||||
const QuadratureInterpolator *qi = E2Q_array[i];
|
||||
if (qi->IntRule == &ir) { return qi; }
|
||||
}
|
||||
|
||||
QuadratureInterpolator *qi = new QuadratureInterpolator(*this, ir);
|
||||
E2Q_array.Append(qi);
|
||||
return qi;
|
||||
}
|
||||
|
||||
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
|
||||
const QuadratureSpace &qs) const
|
||||
{
|
||||
for (int i = 0; i < E2Q_array.Size(); i++)
|
||||
{
|
||||
const QuadratureInterpolator *qi = E2Q_array[i];
|
||||
if (qi->qspace == &qs) { return qi; }
|
||||
}
|
||||
|
||||
QuadratureInterpolator *qi = new QuadratureInterpolator(*this, qs);
|
||||
E2Q_array.Append(qi);
|
||||
return qi;
|
||||
}
|
||||
|
||||
SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
|
||||
const int coarse_ndofs, const Table &coarse_elem_dof,
|
||||
const DenseTensor localP[]) const
|
||||
@@ -1540,10 +1485,6 @@ void FiniteElementSpace::GetElementDofs (int i, Array<int> &dofs) const
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
{
|
||||
if (i < 0 || !mesh->GetNE()) { return NULL; }
|
||||
MFEM_VERIFY(i < mesh->GetNE(),
|
||||
"Invalid element id " << i << ", maximum allowed " << mesh->GetNE()-1);
|
||||
|
||||
const FiniteElement *FE =
|
||||
fec->FiniteElementForGeometry(mesh->GetElementBaseGeometry(i));
|
||||
|
||||
@@ -1848,13 +1789,6 @@ void FiniteElementSpace::Destroy()
|
||||
delete cR;
|
||||
delete cP;
|
||||
Th.Clear();
|
||||
L2E_nat.Clear();
|
||||
L2E_lex.Clear();
|
||||
for (int i = 0; i < E2Q_array.Size(); i++)
|
||||
{
|
||||
delete E2Q_array[i];
|
||||
}
|
||||
E2Q_array.SetSize(0);
|
||||
|
||||
dof_elem_array.DeleteAll();
|
||||
dof_ldof_array.DeleteAll();
|
||||
@@ -2416,7 +2350,8 @@ const Operator &InterpolationGridTransfer::BackwardOperator()
|
||||
return *B.Ptr();
|
||||
}
|
||||
|
||||
// Construct B, if not set, define a suitable mass_integ
|
||||
// Construct B
|
||||
// If not set, define a suitable mass_integ
|
||||
if (!mass_integ && ran_fes.GetNE() > 0)
|
||||
{
|
||||
const FiniteElement *f_fe_0 = ran_fes.GetFE(0);
|
||||
@@ -2579,7 +2514,7 @@ void L2ProjectionGridTransfer::L2Projection::Mult(
|
||||
fes_ho.GetElementVDofs(iho, vdofs);
|
||||
x.GetSubVector(vdofs, xel_mat.GetData());
|
||||
mfem::Mult(R(iho), xel_mat, yel_mat);
|
||||
// Place result correctly into the low-order vector
|
||||
// Place result correctly into low-order vector
|
||||
for (int iref=0; iref<nref; ++iref)
|
||||
{
|
||||
int ilor = ho2lor.GetRow(iho)[iref];
|
||||
@@ -2637,490 +2572,4 @@ const Operator &L2ProjectionGridTransfer::BackwardOperator()
|
||||
return *B;
|
||||
}
|
||||
|
||||
|
||||
ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
|
||||
ElementDofOrdering e_ordering)
|
||||
: fes(f),
|
||||
ne(fes.GetNE()),
|
||||
vdim(fes.GetVDim()),
|
||||
byvdim(fes.GetOrdering() == Ordering::byVDIM),
|
||||
ndofs(fes.GetNDofs()),
|
||||
dof(ne > 0 ? fes.GetFE(0)->GetDof() : 0),
|
||||
nedofs(ne*dof),
|
||||
offsets(ndofs+1),
|
||||
indices(ne*dof)
|
||||
{
|
||||
// Assuming all finite elements are the same.
|
||||
height = vdim*ne*dof;
|
||||
width = fes.GetVSize();
|
||||
const bool dof_reorder = (e_ordering == ElementDofOrdering::LEXICOGRAPHIC);
|
||||
const int *dof_map = NULL;
|
||||
if (dof_reorder && ne > 0)
|
||||
{
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
const FiniteElement *fe = fes.GetFE(e);
|
||||
const TensorBasisElement* el =
|
||||
dynamic_cast<const TensorBasisElement*>(fe);
|
||||
if (el) { continue; }
|
||||
mfem_error("Finite element not suitable for lexicographic ordering");
|
||||
}
|
||||
const FiniteElement *fe = fes.GetFE(0);
|
||||
const TensorBasisElement* el =
|
||||
dynamic_cast<const TensorBasisElement*>(fe);
|
||||
const Array<int> &fe_dof_map = el->GetDofMap();
|
||||
MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
|
||||
dof_map = fe_dof_map.GetData();
|
||||
}
|
||||
const Table& e2dTable = fes.GetElementToDofTable();
|
||||
const int* elementMap = e2dTable.GetJ();
|
||||
// We will be keeping a count of how many local nodes point to its global dof
|
||||
for (int i = 0; i <= ndofs; ++i)
|
||||
{
|
||||
offsets[i] = 0;
|
||||
}
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
for (int d = 0; d < dof; ++d)
|
||||
{
|
||||
const int gid = elementMap[dof*e + d];
|
||||
++offsets[gid + 1];
|
||||
}
|
||||
}
|
||||
// Aggregate to find offsets for each global dof
|
||||
for (int i = 1; i <= ndofs; ++i)
|
||||
{
|
||||
offsets[i] += offsets[i - 1];
|
||||
}
|
||||
// For each global dof, fill in all local nodes that point to it
|
||||
for (int e = 0; e < ne; ++e)
|
||||
{
|
||||
for (int d = 0; d < dof; ++d)
|
||||
{
|
||||
const int did = (!dof_reorder)?d:dof_map[d];
|
||||
const int gid = elementMap[dof*e + did];
|
||||
const int lid = dof*e + d;
|
||||
indices[offsets[gid]++] = lid;
|
||||
}
|
||||
}
|
||||
// We shifted the offsets vector by 1 by using it as a counter.
|
||||
// Now we shift it back.
|
||||
for (int i = ndofs; i > 0; --i)
|
||||
{
|
||||
offsets[i] = offsets[i - 1];
|
||||
}
|
||||
offsets[0] = 0;
|
||||
}
|
||||
|
||||
void ElementRestriction::Mult(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
auto d_offsets = offsets.Read();
|
||||
auto d_indices = indices.Read();
|
||||
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_y = Reshape(y.Write(), nd, vd, ne);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
const int nextOffset = d_offsets[i+1];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
const double dofValue = d_x(t?c:i,t?i:c);
|
||||
for (int j = offset; j < nextOffset; ++j)
|
||||
{
|
||||
const int idx_j = d_indices[j];
|
||||
d_y(idx_j % nd, c, idx_j / nd) = dofValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ElementRestriction::MultTranspose(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
auto d_offsets = offsets.Read();
|
||||
auto d_indices = indices.Read();
|
||||
auto d_x = Reshape(x.Read(), nd, vd, ne);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int offset = d_offsets[i];
|
||||
const int nextOffset = d_offsets[i + 1];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
double dofValue = 0;
|
||||
for (int j = offset; j < nextOffset; ++j)
|
||||
{
|
||||
const int idx_j = d_indices[j];
|
||||
dofValue += d_x(idx_j % nd, c, idx_j / nd);
|
||||
}
|
||||
d_y(t?c:i,t?i:c) = dofValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
QuadratureInterpolator::QuadratureInterpolator(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &ir)
|
||||
{
|
||||
fespace = &fes;
|
||||
qspace = NULL;
|
||||
IntRule = &ir;
|
||||
use_tensor_products = true; // not implemented yet (not used)
|
||||
|
||||
if (fespace->GetNE() == 0) { return; }
|
||||
const FiniteElement *fe = fespace->GetFE(0);
|
||||
MFEM_VERIFY(dynamic_cast<const ScalarFiniteElement*>(fe) != NULL,
|
||||
"Only scalar finite elements are supported");
|
||||
}
|
||||
|
||||
QuadratureInterpolator::QuadratureInterpolator(const FiniteElementSpace &fes,
|
||||
const QuadratureSpace &qs)
|
||||
{
|
||||
fespace = &fes;
|
||||
qspace = &qs;
|
||||
IntRule = NULL;
|
||||
use_tensor_products = true; // not implemented yet (not used)
|
||||
|
||||
if (fespace->GetNE() == 0) { return; }
|
||||
const FiniteElement *fe = fespace->GetFE(0);
|
||||
MFEM_VERIFY(dynamic_cast<const ScalarFiniteElement*>(fe) != NULL,
|
||||
"Only scalar finite elements are supported");
|
||||
}
|
||||
|
||||
template<const int T_VDIM, const int T_ND, const int T_NQ>
|
||||
void QuadratureInterpolator::Eval2D(
|
||||
const int NE,
|
||||
const int vdim,
|
||||
const DofToQuad &maps,
|
||||
const Vector &e_vec,
|
||||
Vector &q_val,
|
||||
Vector &q_der,
|
||||
Vector &q_det,
|
||||
const int eval_flags)
|
||||
{
|
||||
const int nd = maps.ndof;
|
||||
const int nq = maps.nqpt;
|
||||
const int ND = T_ND ? T_ND : nd;
|
||||
const int NQ = T_NQ ? T_NQ : nq;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
MFEM_VERIFY(ND <= MAX_ND2D, "");
|
||||
MFEM_VERIFY(NQ <= MAX_NQ2D, "");
|
||||
MFEM_VERIFY(VDIM == 2 || !(eval_flags & DETERMINANTS), "");
|
||||
auto B = Reshape(maps.B.Read(), NQ, ND);
|
||||
auto G = Reshape(maps.G.Read(), NQ, 2, ND);
|
||||
auto E = Reshape(e_vec.Read(), ND, VDIM, NE);
|
||||
auto val = Reshape(q_val.Write(), NQ, VDIM, NE);
|
||||
auto der = Reshape(q_der.Write(), NQ, VDIM, 2, NE);
|
||||
auto det = Reshape(q_det.Write(), NQ, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
const int ND = T_ND ? T_ND : nd;
|
||||
const int NQ = T_NQ ? T_NQ : nq;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int max_ND = T_ND ? T_ND : MAX_ND2D;
|
||||
constexpr int max_VDIM = T_VDIM ? T_VDIM : MAX_VDIM2D;
|
||||
double s_E[max_VDIM*max_ND];
|
||||
for (int d = 0; d < ND; d++)
|
||||
{
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
s_E[c+d*VDIM] = E(d,c,e);
|
||||
}
|
||||
}
|
||||
for (int q = 0; q < NQ; ++q)
|
||||
{
|
||||
if (eval_flags & VALUES)
|
||||
{
|
||||
double ed[max_VDIM];
|
||||
for (int c = 0; c < VDIM; c++) { ed[c] = 0.0; }
|
||||
for (int d = 0; d < ND; ++d)
|
||||
{
|
||||
const double b = B(q,d);
|
||||
for (int c = 0; c < VDIM; c++) { ed[c] += b*s_E[c+d*VDIM]; }
|
||||
}
|
||||
for (int c = 0; c < VDIM; c++) { val(q,c,e) = ed[c]; }
|
||||
}
|
||||
if ((eval_flags & DERIVATIVES) || (eval_flags & DETERMINANTS))
|
||||
{
|
||||
// use MAX_VDIM2D to avoid "subscript out of range" warnings
|
||||
double D[MAX_VDIM2D*2];
|
||||
for (int i = 0; i < 2*VDIM; i++) { D[i] = 0.0; }
|
||||
for (int d = 0; d < ND; ++d)
|
||||
{
|
||||
const double wx = G(q,0,d);
|
||||
const double wy = G(q,1,d);
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
double s_e = s_E[c+d*VDIM];
|
||||
D[c+VDIM*0] += s_e * wx;
|
||||
D[c+VDIM*1] += s_e * wy;
|
||||
}
|
||||
}
|
||||
if (eval_flags & DERIVATIVES)
|
||||
{
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
der(q,c,0,e) = D[c+VDIM*0];
|
||||
der(q,c,1,e) = D[c+VDIM*1];
|
||||
}
|
||||
}
|
||||
if (VDIM == 2 && (eval_flags & DETERMINANTS))
|
||||
{
|
||||
// The check (VDIM == 2) should eliminate this block when VDIM is
|
||||
// known at compile time and (VDIM != 2).
|
||||
det(q,e) = D[0]*D[3] - D[1]*D[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<const int T_VDIM, const int T_ND, const int T_NQ>
|
||||
void QuadratureInterpolator::Eval3D(
|
||||
const int NE,
|
||||
const int vdim,
|
||||
const DofToQuad &maps,
|
||||
const Vector &e_vec,
|
||||
Vector &q_val,
|
||||
Vector &q_der,
|
||||
Vector &q_det,
|
||||
const int eval_flags)
|
||||
{
|
||||
const int nd = maps.ndof;
|
||||
const int nq = maps.nqpt;
|
||||
const int ND = T_ND ? T_ND : nd;
|
||||
const int NQ = T_NQ ? T_NQ : nq;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
MFEM_VERIFY(ND <= MAX_ND3D, "");
|
||||
MFEM_VERIFY(NQ <= MAX_NQ3D, "");
|
||||
MFEM_VERIFY(VDIM == 3 || !(eval_flags & DETERMINANTS), "");
|
||||
auto B = Reshape(maps.B.Read(), NQ, ND);
|
||||
auto G = Reshape(maps.G.Read(), NQ, 3, ND);
|
||||
auto E = Reshape(e_vec.Read(), ND, VDIM, NE);
|
||||
auto val = Reshape(q_val.Write(), NQ, VDIM, NE);
|
||||
auto der = Reshape(q_der.Write(), NQ, VDIM, 3, NE);
|
||||
auto det = Reshape(q_det.Write(), NQ, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
const int ND = T_ND ? T_ND : nd;
|
||||
const int NQ = T_NQ ? T_NQ : nq;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int max_ND = T_ND ? T_ND : MAX_ND2D;
|
||||
constexpr int max_VDIM = T_VDIM ? T_VDIM : MAX_VDIM2D;
|
||||
double s_E[max_VDIM*max_ND];
|
||||
for (int d = 0; d < ND; d++)
|
||||
{
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
s_E[c+d*VDIM] = E(d,c,e);
|
||||
}
|
||||
}
|
||||
for (int q = 0; q < NQ; ++q)
|
||||
{
|
||||
if (eval_flags & VALUES)
|
||||
{
|
||||
double ed[max_VDIM];
|
||||
for (int c = 0; c < VDIM; c++) { ed[c] = 0.0; }
|
||||
for (int d = 0; d < ND; ++d)
|
||||
{
|
||||
const double b = B(q,d);
|
||||
for (int c = 0; c < VDIM; c++) { ed[c] += b*s_E[c+d*VDIM]; }
|
||||
}
|
||||
for (int c = 0; c < VDIM; c++) { val(q,c,e) = ed[c]; }
|
||||
}
|
||||
if ((eval_flags & DERIVATIVES) || (eval_flags & DETERMINANTS))
|
||||
{
|
||||
// use MAX_VDIM3D to avoid "subscript out of range" warnings
|
||||
double D[MAX_VDIM3D*3];
|
||||
for (int i = 0; i < 3*VDIM; i++) { D[i] = 0.0; }
|
||||
for (int d = 0; d < ND; ++d)
|
||||
{
|
||||
const double wx = G(q,0,d);
|
||||
const double wy = G(q,1,d);
|
||||
const double wz = G(q,2,d);
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
double s_e = s_E[c+d*VDIM];
|
||||
D[c+VDIM*0] += s_e * wx;
|
||||
D[c+VDIM*1] += s_e * wy;
|
||||
D[c+VDIM*2] += s_e * wz;
|
||||
}
|
||||
}
|
||||
if (eval_flags & DERIVATIVES)
|
||||
{
|
||||
for (int c = 0; c < VDIM; c++)
|
||||
{
|
||||
der(q,c,0,e) = D[c+VDIM*0];
|
||||
der(q,c,1,e) = D[c+VDIM*1];
|
||||
der(q,c,2,e) = D[c+VDIM*2];
|
||||
}
|
||||
}
|
||||
if (VDIM == 3 && (eval_flags & DETERMINANTS))
|
||||
{
|
||||
// The check (VDIM == 3) should eliminate this block when VDIM is
|
||||
// known at compile time and (VDIM != 3).
|
||||
det(q,e) = D[0] * (D[4] * D[8] - D[5] * D[7]) +
|
||||
D[3] * (D[2] * D[7] - D[1] * D[8]) +
|
||||
D[6] * (D[1] * D[5] - D[2] * D[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void QuadratureInterpolator::Mult(
|
||||
const Vector &e_vec, unsigned eval_flags,
|
||||
Vector &q_val, Vector &q_der, Vector &q_det) const
|
||||
{
|
||||
const int ne = fespace->GetNE();
|
||||
if (ne == 0) { return; }
|
||||
const int vdim = fespace->GetVDim();
|
||||
const int dim = fespace->GetMesh()->Dimension();
|
||||
const FiniteElement *fe = fespace->GetFE(0);
|
||||
const IntegrationRule *ir =
|
||||
IntRule ? IntRule : &qspace->GetElementIntRule(0);
|
||||
const DofToQuad &maps = fe->GetDofToQuad(*ir, DofToQuad::FULL);
|
||||
const int nd = maps.ndof;
|
||||
const int nq = maps.nqpt;
|
||||
void (*eval_func)(
|
||||
const int NE,
|
||||
const int vdim,
|
||||
const DofToQuad &maps,
|
||||
const Vector &e_vec,
|
||||
Vector &q_val,
|
||||
Vector &q_der,
|
||||
Vector &q_det,
|
||||
const int eval_flags) = NULL;
|
||||
if (vdim == 1)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (100*nd + nq)
|
||||
{
|
||||
// Q0
|
||||
case 101: eval_func = &Eval2D<1,1,1>; break;
|
||||
case 104: eval_func = &Eval2D<1,1,4>; break;
|
||||
// Q1
|
||||
case 404: eval_func = &Eval2D<1,4,4>; break;
|
||||
case 409: eval_func = &Eval2D<1,4,9>; break;
|
||||
// Q2
|
||||
case 909: eval_func = &Eval2D<1,9,9>; break;
|
||||
case 916: eval_func = &Eval2D<1,9,16>; break;
|
||||
// Q3
|
||||
case 1616: eval_func = &Eval2D<1,16,16>; break;
|
||||
case 1625: eval_func = &Eval2D<1,16,25>; break;
|
||||
case 1636: eval_func = &Eval2D<1,16,36>; break;
|
||||
// Q4
|
||||
case 2525: eval_func = &Eval2D<1,25,25>; break;
|
||||
case 2536: eval_func = &Eval2D<1,25,36>; break;
|
||||
case 2549: eval_func = &Eval2D<1,25,49>; break;
|
||||
case 2564: eval_func = &Eval2D<1,25,64>; break;
|
||||
}
|
||||
if (nq >= 100 || !eval_func)
|
||||
{
|
||||
eval_func = &Eval2D<1>;
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch (1000*nd + nq)
|
||||
{
|
||||
// Q0
|
||||
case 1001: eval_func = &Eval3D<1,1,1>; break;
|
||||
case 1008: eval_func = &Eval3D<1,1,8>; break;
|
||||
// Q1
|
||||
case 8008: eval_func = &Eval3D<1,8,8>; break;
|
||||
case 8027: eval_func = &Eval3D<1,8,27>; break;
|
||||
// Q2
|
||||
case 27027: eval_func = &Eval3D<1,27,27>; break;
|
||||
case 27064: eval_func = &Eval3D<1,27,64>; break;
|
||||
// Q3
|
||||
case 64064: eval_func = &Eval3D<1,64,64>; break;
|
||||
case 64125: eval_func = &Eval3D<1,64,125>; break;
|
||||
case 64216: eval_func = &Eval3D<1,64,216>; break;
|
||||
// Q4
|
||||
case 125125: eval_func = &Eval3D<1,125,125>; break;
|
||||
case 125216: eval_func = &Eval3D<1,125,216>; break;
|
||||
}
|
||||
if (nq >= 1000 || !eval_func)
|
||||
{
|
||||
eval_func = &Eval3D<1>;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (vdim == dim)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (100*nd + nq)
|
||||
{
|
||||
// Q1
|
||||
case 404: eval_func = &Eval2D<2,4,4>; break;
|
||||
case 409: eval_func = &Eval2D<2,4,9>; break;
|
||||
// Q2
|
||||
case 909: eval_func = &Eval2D<2,9,9>; break;
|
||||
case 916: eval_func = &Eval2D<2,9,16>; break;
|
||||
// Q3
|
||||
case 1616: eval_func = &Eval2D<2,16,16>; break;
|
||||
case 1625: eval_func = &Eval2D<2,16,25>; break;
|
||||
case 1636: eval_func = &Eval2D<2,16,36>; break;
|
||||
// Q4
|
||||
case 2525: eval_func = &Eval2D<2,25,25>; break;
|
||||
case 2536: eval_func = &Eval2D<2,25,36>; break;
|
||||
case 2549: eval_func = &Eval2D<2,25,49>; break;
|
||||
case 2564: eval_func = &Eval2D<2,25,64>; break;
|
||||
}
|
||||
if (nq >= 100 || !eval_func)
|
||||
{
|
||||
eval_func = &Eval2D<2>;
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch (1000*nd + nq)
|
||||
{
|
||||
// Q1
|
||||
case 8008: eval_func = &Eval3D<3,8,8>; break;
|
||||
case 8027: eval_func = &Eval3D<3,8,27>; break;
|
||||
// Q2
|
||||
case 27027: eval_func = &Eval3D<3,27,27>; break;
|
||||
case 27064: eval_func = &Eval3D<3,27,64>; break;
|
||||
// Q3
|
||||
case 64064: eval_func = &Eval3D<3,64,64>; break;
|
||||
case 64125: eval_func = &Eval3D<3,64,125>; break;
|
||||
case 64216: eval_func = &Eval3D<3,64,216>; break;
|
||||
// Q4
|
||||
case 125125: eval_func = &Eval3D<3,125,125>; break;
|
||||
case 125216: eval_func = &Eval3D<3,125,216>; break;
|
||||
}
|
||||
if (nq >= 1000 || !eval_func)
|
||||
{
|
||||
eval_func = &Eval3D<3>;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (eval_func)
|
||||
{
|
||||
eval_func(ne, vdim, maps, e_vec, q_val, q_der, q_det, eval_flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("case not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
void QuadratureInterpolator::MultTranspose(
|
||||
unsigned eval_flags, const Vector &q_val, const Vector &q_der,
|
||||
Vector &e_vec) const
|
||||
{
|
||||
MFEM_ABORT("this method is not implemented yet");
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
-184
@@ -59,25 +59,9 @@ Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
|
||||
}
|
||||
|
||||
|
||||
/// Constants describing the possible orderings of the DOFs in one element.
|
||||
enum class ElementDofOrdering
|
||||
{
|
||||
/// Native ordering as defined by the FiniteElement.
|
||||
/** This ordering can be used by tensor-product elements when the
|
||||
interpolation from the DOFs to quadrature points does not use the
|
||||
tensor-product structure. */
|
||||
NATIVE,
|
||||
/// Lexicographic ordering for tensor-product FiniteElements.
|
||||
/** This ordering can be used only with tensor-product elements. */
|
||||
LEXICOGRAPHIC
|
||||
};
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class NURBSExtension;
|
||||
class BilinearFormIntegrator;
|
||||
class QuadratureSpace;
|
||||
class QuadratureInterpolator;
|
||||
|
||||
|
||||
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
|
||||
@@ -126,11 +110,6 @@ protected:
|
||||
/// Transformation to apply to GridFunctions after space Update().
|
||||
OperatorHandle Th;
|
||||
|
||||
/// The element restriction operators, see GetElementRestriction().
|
||||
mutable OperatorHandle L2E_nat, L2E_lex;
|
||||
|
||||
mutable Array<QuadratureInterpolator*> E2Q_array;
|
||||
|
||||
long sequence; // should match Mesh::GetSequence
|
||||
|
||||
void UpdateNURBS();
|
||||
@@ -278,60 +257,14 @@ public:
|
||||
bool Conforming() const { return mesh->Conforming(); }
|
||||
bool Nonconforming() const { return mesh->Nonconforming(); }
|
||||
|
||||
/// The returned SparseMatrix is owned by the FiniteElementSpace.
|
||||
const SparseMatrix *GetConformingProlongation() const;
|
||||
|
||||
/// The returned SparseMatrix is owned by the FiniteElementSpace.
|
||||
const SparseMatrix *GetConformingRestriction() const;
|
||||
|
||||
/// The returned Operator is owned by the FiniteElementSpace.
|
||||
virtual const Operator *GetProlongationMatrix() const
|
||||
{ return GetConformingProlongation(); }
|
||||
|
||||
/// The returned SparseMatrix is owned by the FiniteElementSpace.
|
||||
virtual const SparseMatrix *GetRestrictionMatrix() const
|
||||
{ return GetConformingRestriction(); }
|
||||
|
||||
/// Return an Operator that converts L-vectors to E-vectors.
|
||||
/** An L-vector is a vector of size GetVSize() which is the same size as a
|
||||
GridFunction. An E-vector represents the element-wise discontinuous
|
||||
version of the FE space.
|
||||
|
||||
The layout of the E-vector is: ND x VDIM x NE, where ND is the number of
|
||||
degrees of freedom, VDIM is the vector dimension of the FE space, and NE
|
||||
is the number of the mesh elements.
|
||||
|
||||
The parameter @a e_ordering describes how the local DOFs in each element
|
||||
should be ordered, see ElementDofOrdering.
|
||||
|
||||
For discontinuous spaces, where the element-restriction is the identity,
|
||||
this method will return NULL.
|
||||
|
||||
The returned Operator is owned by the FiniteElementSpace. */
|
||||
const Operator *GetElementRestriction(ElementDofOrdering e_ordering) const;
|
||||
|
||||
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
|
||||
quadrature point values and/or derivatives (Q-vectors). */
|
||||
/** An E-vector represents the element-wise discontinuous version of the FE
|
||||
space and can be obtained, for example, from a GridFunction using the
|
||||
Operator returned by GetElementRestriction().
|
||||
|
||||
All elements will use the same IntegrationRule, @a ir as the target
|
||||
quadrature points. */
|
||||
const QuadratureInterpolator *GetQuadratureInterpolator(
|
||||
const IntegrationRule &ir) const;
|
||||
|
||||
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
|
||||
quadrature point values and/or derivatives (Q-vectors). */
|
||||
/** An E-vector represents the element-wise discontinuous version of the FE
|
||||
space and can be obtained, for example, from a GridFunction using the
|
||||
Operator returned by GetElementRestriction().
|
||||
|
||||
The target quadrature points in the elements are described by the given
|
||||
QuadratureSpace, @a qs. */
|
||||
const QuadratureInterpolator *GetQuadratureInterpolator(
|
||||
const QuadratureSpace &qs) const;
|
||||
|
||||
/// Returns vector dimension.
|
||||
inline int GetVDim() const { return vdim; }
|
||||
|
||||
@@ -873,123 +806,6 @@ public:
|
||||
virtual const Operator &BackwardOperator();
|
||||
};
|
||||
|
||||
|
||||
/// Operator that converts FiniteElementSpace L-vectors to E-vectors.
|
||||
/** Objects of this type are typically created and owned by FiniteElementSpace
|
||||
objects, see FiniteElementSpace::GetElementRestriction(). */
|
||||
class ElementRestriction : public Operator
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace &fes;
|
||||
const int ne;
|
||||
const int vdim;
|
||||
const bool byvdim;
|
||||
const int ndofs;
|
||||
const int dof;
|
||||
const int nedofs;
|
||||
Array<int> offsets;
|
||||
Array<int> indices;
|
||||
|
||||
public:
|
||||
ElementRestriction(const FiniteElementSpace&, ElementDofOrdering);
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const;
|
||||
};
|
||||
|
||||
|
||||
/** @brief A class that performs interpolation from an E-vector to quadrature
|
||||
point values and/or derivatives (Q-vectors). */
|
||||
/** An E-vector represents the element-wise discontinuous version of the FE
|
||||
space and can be obtained, for example, from a GridFunction using the
|
||||
Operator returned by FiniteElementSpace::GetElementRestriction().
|
||||
|
||||
The target quadrature points in the elements can be described either by an
|
||||
IntegrationRule (all mesh elements must be of the same type in this case) or
|
||||
by a QuadratureSpace. */
|
||||
class QuadratureInterpolator
|
||||
{
|
||||
protected:
|
||||
friend class FiniteElementSpace; // Needs access to qspace and IntRule
|
||||
|
||||
const FiniteElementSpace *fespace; ///< Not owned
|
||||
const QuadratureSpace *qspace; ///< Not owned
|
||||
const IntegrationRule *IntRule; ///< Not owned
|
||||
|
||||
mutable bool use_tensor_products;
|
||||
|
||||
static const int MAX_NQ2D = 100;
|
||||
static const int MAX_ND2D = 100;
|
||||
static const int MAX_VDIM2D = 2;
|
||||
|
||||
static const int MAX_NQ3D = 1000;
|
||||
static const int MAX_ND3D = 1000;
|
||||
static const int MAX_VDIM3D = 3;
|
||||
|
||||
public:
|
||||
enum EvalFlags
|
||||
{
|
||||
VALUES = 1 << 0, ///< Evaluate the values at quadrature points
|
||||
DERIVATIVES = 1 << 1, ///< Evaluate the derivatives at quadrature points
|
||||
/** @brief Assuming the derivative at quadrature points form a matrix,
|
||||
this flag can be used to compute and store their determinants. This
|
||||
flag can only be used in Mult(). */
|
||||
DETERMINANTS = 1 << 2
|
||||
};
|
||||
|
||||
QuadratureInterpolator(const FiniteElementSpace &fes,
|
||||
const IntegrationRule &ir);
|
||||
|
||||
QuadratureInterpolator(const FiniteElementSpace &fes,
|
||||
const QuadratureSpace &qs);
|
||||
|
||||
/** @brief Disable the use of tensor product evaluations, for tensor-product
|
||||
elements, e.g. quads and hexes. */
|
||||
/** Currently, tensor product evaluations are not implemented and this method
|
||||
has no effect. */
|
||||
void DisableTensorProducts(bool disable = true) const
|
||||
{ use_tensor_products = !disable; }
|
||||
|
||||
/// Interpolate the E-vector @a e_vec to quadrature points.
|
||||
/** The @a eval_flags are a bitwise mask of constants from the EvalFlags
|
||||
enumeration. When the VALUES flag is set, the values at quadrature points
|
||||
are computed and stored in the Vector @a q_val. Similarly, when the flag
|
||||
DERIVATIVES is set, the derivatives are computed and stored in @a q_der.
|
||||
When the DETERMINANTS flags is set, it is assumed that the derivatives
|
||||
form a matrix at each quadrature point (i.e. the associated
|
||||
FiniteElementSpace is a vector space) and their determinants are computed
|
||||
and stored in @a q_det. */
|
||||
void Mult(const Vector &e_vec, unsigned eval_flags,
|
||||
Vector &q_val, Vector &q_der, Vector &q_det) const;
|
||||
|
||||
/// Perform the transpose operation of Mult(). (TODO)
|
||||
void MultTranspose(unsigned eval_flags, const Vector &q_val,
|
||||
const Vector &q_der, Vector &e_vec) const;
|
||||
|
||||
// Compute kernels follow (cannot be private or protected with nvcc)
|
||||
|
||||
/// Template compute kernel for 2D.
|
||||
template<const int T_VDIM = 0, const int T_ND = 0, const int T_NQ = 0>
|
||||
static void Eval2D(const int NE,
|
||||
const int vdim,
|
||||
const DofToQuad &maps,
|
||||
const Vector &e_vec,
|
||||
Vector &q_val,
|
||||
Vector &q_der,
|
||||
Vector &q_det,
|
||||
const int eval_flags);
|
||||
|
||||
/// Template compute kernel for 3D.
|
||||
template<const int T_VDIM = 0, const int T_ND = 0, const int T_NQ = 0>
|
||||
static void Eval3D(const int NE,
|
||||
const int vdim,
|
||||
const DofToQuad &maps,
|
||||
const Vector &e_vec,
|
||||
Vector &q_val,
|
||||
Vector &q_der,
|
||||
Vector &q_det,
|
||||
const int eval_flags);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+7
-19
@@ -30,9 +30,6 @@ using namespace std;
|
||||
GridFunction::GridFunction(Mesh *m, std::istream &input)
|
||||
: Vector()
|
||||
{
|
||||
// Grid functions are stored on the device
|
||||
UseDevice(true);
|
||||
|
||||
fes = new FiniteElementSpace;
|
||||
fec = fes->Load(m, input);
|
||||
|
||||
@@ -63,8 +60,6 @@ GridFunction::GridFunction(Mesh *m, std::istream &input)
|
||||
|
||||
GridFunction::GridFunction(Mesh *m, GridFunction *gf_array[], int num_pieces)
|
||||
{
|
||||
UseDevice(true);
|
||||
|
||||
// all GridFunctions must have the same FE collection, vdim, ordering
|
||||
int vdim, ordering;
|
||||
|
||||
@@ -168,7 +163,6 @@ void GridFunction::Update()
|
||||
Vector old_data;
|
||||
old_data.Swap(*this);
|
||||
SetSize(T->Height());
|
||||
UseDevice(true);
|
||||
T->Mult(old_data, *this);
|
||||
}
|
||||
else
|
||||
@@ -198,9 +192,7 @@ void GridFunction::MakeRef(FiniteElementSpace *f, Vector &v, int v_offset)
|
||||
MFEM_ASSERT(v.Size() >= v_offset + f->GetVSize(), "");
|
||||
if (f != fes) { Destroy(); }
|
||||
fes = f;
|
||||
v.UseDevice(true);
|
||||
NewMemoryAndSize(Memory<double>(v.GetMemory(), v_offset, fes->GetVSize()),
|
||||
fes->GetVSize(), true);
|
||||
NewDataAndSize((double *)v + v_offset, fes->GetVSize());
|
||||
sequence = fes->GetSequence();
|
||||
}
|
||||
|
||||
@@ -223,16 +215,13 @@ void GridFunction::MakeTRef(FiniteElementSpace *f, Vector &tv, int tv_offset)
|
||||
if (!f->GetProlongationMatrix())
|
||||
{
|
||||
MakeRef(f, tv, tv_offset);
|
||||
t_vec.NewMemoryAndSize(data, size, false);
|
||||
t_vec.NewDataAndSize(data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(tv.Size() >= tv_offset + f->GetTrueVSize(), "");
|
||||
SetSpace(f); // works in parallel
|
||||
tv.UseDevice(true);
|
||||
const int tv_size = f->GetTrueVSize();
|
||||
t_vec.NewMemoryAndSize(Memory<double>(tv.GetMemory(), tv_offset, tv_size),
|
||||
tv_size, true);
|
||||
t_vec.NewDataAndSize(&tv(tv_offset), f->GetTrueVSize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +302,7 @@ int GridFunction::VectorDim() const
|
||||
{
|
||||
fe = fes->GetFE(0);
|
||||
}
|
||||
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
return fes->GetVDim();
|
||||
}
|
||||
@@ -326,7 +315,7 @@ void GridFunction::GetTrueDofs(Vector &tv) const
|
||||
if (!R)
|
||||
{
|
||||
// R is identity -> make tv a reference to *this
|
||||
tv.NewDataAndSize(const_cast<double*>((const double*)data), size);
|
||||
tv.NewDataAndSize(data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1378,7 +1367,7 @@ void GridFunction::AccumulateAndCountBdrValues(
|
||||
if (vdofs.Size() == 0) { continue; }
|
||||
|
||||
transf = mesh->GetEdgeTransformation(edge);
|
||||
transf->Attribute = -1; // TODO: set the boundary attribute
|
||||
transf->Attribute = -1; // FIXME: set the boundary attribute
|
||||
fe = fes->GetEdgeElement(edge);
|
||||
if (!vcoeff)
|
||||
{
|
||||
@@ -1482,7 +1471,7 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
|
||||
if (dofs.Size() == 0) { continue; }
|
||||
|
||||
T = mesh->GetEdgeTransformation(edge);
|
||||
T->Attribute = -1; // TODO: set the boundary attribute
|
||||
T->Attribute = -1; // FIXME: set the boundary attribute
|
||||
fe = fes->GetEdgeElement(edge);
|
||||
lvec.SetSize(fe->GetDof());
|
||||
fe->Project(vcoeff, *T, lvec);
|
||||
@@ -1787,7 +1776,6 @@ void GridFunction::ProjectBdrCoefficient(VectorCoefficient &vcoeff,
|
||||
void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[], Array<int> &attr)
|
||||
{
|
||||
Array<int> values_counter;
|
||||
this->HostReadWrite();
|
||||
AccumulateAndCountBdrValues(coeff, NULL, attr, values_counter);
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
#ifdef MFEM_DEBUG
|
||||
|
||||
+7
-12
@@ -68,16 +68,15 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
GridFunction() { fes = NULL; fec = NULL; sequence = 0; UseDevice(true); }
|
||||
GridFunction() { fes = NULL; fec = NULL; sequence = 0; }
|
||||
|
||||
/// Copy constructor. The internal true-dof vector #t_vec is not copied.
|
||||
GridFunction(const GridFunction &orig)
|
||||
: Vector(orig), fes(orig.fes), fec(NULL), sequence(orig.sequence)
|
||||
{ UseDevice(true); }
|
||||
: Vector(orig), fes(orig.fes), fec(NULL), sequence(orig.sequence) { }
|
||||
|
||||
/// Construct a GridFunction associated with the FiniteElementSpace @a *f.
|
||||
GridFunction(FiniteElementSpace *f) : Vector(f->GetVSize())
|
||||
{ fes = f; fec = NULL; sequence = f->GetSequence(); UseDevice(true); }
|
||||
{ fes = f; fec = NULL; sequence = f->GetSequence(); }
|
||||
|
||||
/// Construct a GridFunction using previously allocated array @a data.
|
||||
/** The GridFunction does not assume ownership of @a data which is assumed to
|
||||
@@ -85,9 +84,8 @@ public:
|
||||
for externally allocated array, the pointer @a data can be NULL. The data
|
||||
array can be replaced later using the method SetData().
|
||||
*/
|
||||
GridFunction(FiniteElementSpace *f, double *data)
|
||||
: Vector(data, f->GetVSize())
|
||||
{ fes = f; fec = NULL; sequence = f->GetSequence(); UseDevice(true); }
|
||||
GridFunction(FiniteElementSpace *f, double *data) : Vector(data, f->GetVSize())
|
||||
{ fes = f; fec = NULL; sequence = f->GetSequence(); }
|
||||
|
||||
/// Construct a GridFunction on the given Mesh, using the data from @a input.
|
||||
/** The content of @a input should be in the format created by the method
|
||||
@@ -126,7 +124,6 @@ public:
|
||||
|
||||
/// @brief Extract the true-dofs from the GridFunction. If all dofs are true,
|
||||
/// then `tv` will be set to point to the data of `*this`.
|
||||
/** @warning This method breaks const-ness when all dofs are true. */
|
||||
void GetTrueDofs(Vector &tv) const;
|
||||
|
||||
/// Shortcut for calling GetTrueDofs() with GetTrueVector() as argument.
|
||||
@@ -705,7 +702,7 @@ inline void QuadratureFunction::GetElementValues(int idx, Vector &values) const
|
||||
const int s_offset = qspace->element_offsets[idx];
|
||||
const int sl_size = qspace->element_offsets[idx+1] - s_offset;
|
||||
values.SetSize(vdim*sl_size);
|
||||
const double *q = data + vdim*s_offset;
|
||||
double *q = data + vdim*s_offset;
|
||||
for (int i = 0; i<values.Size(); i++)
|
||||
{
|
||||
values(i) = *(q++);
|
||||
@@ -725,14 +722,12 @@ inline void QuadratureFunction::GetElementValues(int idx,
|
||||
const int s_offset = qspace->element_offsets[idx];
|
||||
const int sl_size = qspace->element_offsets[idx+1] - s_offset;
|
||||
values.SetSize(vdim, sl_size);
|
||||
const double *q = data + vdim*s_offset;
|
||||
double *q = data + vdim*s_offset;
|
||||
for (int j = 0; j<sl_size; j++)
|
||||
{
|
||||
for (int i = 0; i<vdim; i++)
|
||||
{
|
||||
values(i,j) = *(q++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -78,19 +78,6 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
|
||||
}
|
||||
}
|
||||
|
||||
const Array<double> &IntegrationRule::GetWeights() const
|
||||
{
|
||||
if (weights.Size() != GetNPoints())
|
||||
{
|
||||
weights.SetSize(GetNPoints());
|
||||
for (int i = 0; i < GetNPoints(); i++)
|
||||
{
|
||||
weights[i] = IntPoint(i).weight;
|
||||
}
|
||||
}
|
||||
return weights;
|
||||
}
|
||||
|
||||
void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
|
||||
{
|
||||
// for pow on older compilers
|
||||
|
||||
@@ -87,9 +87,6 @@ class IntegrationRule : public Array<IntegrationPoint>
|
||||
private:
|
||||
friend class IntegrationRules;
|
||||
int Order;
|
||||
/** @brief The quadrature weights gathered as a contiguous array. Created
|
||||
by request with the method GetWeights(). */
|
||||
mutable Array<double> weights;
|
||||
|
||||
/// Define n-simplex rule (triangle/tetrahedron for n=2/3) of order (2s+1)
|
||||
void GrundmannMollerSimplexRule(int s, int n = 3);
|
||||
@@ -242,11 +239,6 @@ public:
|
||||
/// Returns a const reference to the i-th integration point
|
||||
const IntegrationPoint &IntPoint(int i) const { return (*this)[i]; }
|
||||
|
||||
/// Return the quadrature weights in a contiguous array.
|
||||
/** If a contiguous array is not required, the weights can be accessed with
|
||||
a call like this: `IntPoint(i).weight`. */
|
||||
const Array<double> &GetWeights() const;
|
||||
|
||||
/// Destroys an IntegrationRule object
|
||||
~IntegrationRule() { }
|
||||
};
|
||||
|
||||
@@ -19,9 +19,6 @@ namespace mfem
|
||||
LinearForm::LinearForm(FiniteElementSpace *f, LinearForm *lf)
|
||||
: Vector(f->GetVSize())
|
||||
{
|
||||
// Linear forms are stored on the device
|
||||
UseDevice(true);
|
||||
|
||||
fes = f;
|
||||
extern_lfs = 1;
|
||||
|
||||
@@ -86,10 +83,6 @@ void LinearForm::Assemble()
|
||||
|
||||
Vector::operator=(0.0);
|
||||
|
||||
// The above operation is executed on device because of UseDevice().
|
||||
// The first use of AddElementVector() below will move it back to host
|
||||
// because both 'vdofs' and 'elemvect' are on host.
|
||||
|
||||
if (dlfi.Size())
|
||||
{
|
||||
for (i = 0; i < fes -> GetNE(); i++)
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ public:
|
||||
/// Creates linear form associated with FE space @a *f.
|
||||
/** The pointer @a f is not owned by the newly constructed object. */
|
||||
LinearForm(FiniteElementSpace *f) : Vector(f->GetVSize())
|
||||
{ fes = f; extern_lfs = 0; UseDevice(true); }
|
||||
{ fes = f; extern_lfs = 0; }
|
||||
|
||||
/** @brief Create a LinearForm on the FiniteElementSpace @a f, using the
|
||||
same integrators as the LinearForm @a lf.
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
/** The associated FiniteElementSpace can be set later using one of the
|
||||
methods: Update(FiniteElementSpace *) or
|
||||
Update(FiniteElementSpace *, Vector &, int). */
|
||||
LinearForm() { fes = NULL; extern_lfs = 0; UseDevice(true); }
|
||||
LinearForm() { fes = NULL; extern_lfs = 0; }
|
||||
|
||||
/// Copy assignment. Only the data of the base class Vector is copied.
|
||||
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
|
||||
|
||||
+3
-3
@@ -181,7 +181,7 @@ void VectorDomainLFIntegrator::AssembleRHSElementVect(
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = 2*el.GetOrder();
|
||||
int intorder = el.GetOrder() + 1;
|
||||
ir = &IntRules.Get(el.GetGeomType(), intorder);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = 2*el.GetOrder();
|
||||
int intorder = el.GetOrder() + 1;
|
||||
ir = &IntRules.Get(el.GetGeomType(), intorder);
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int intorder = 2*el.GetOrder();
|
||||
int intorder = el.GetOrder() + 1;
|
||||
ir = &IntRules.Get(Tr.FaceGeom, intorder);
|
||||
}
|
||||
|
||||
|
||||
+36
-36
@@ -35,11 +35,11 @@ typedef double* QLocal2D_t @dim(Q1D, Q1D, NE);
|
||||
typedef double* DLocal3D_t @dim(D1D, D1D, D1D, NE);
|
||||
typedef double* QLocal3D_t @dim(Q1D, Q1D, Q1D, NE);
|
||||
|
||||
typedef double* Jacobian2D_t @dim(Q2D, 2, 2, NE);
|
||||
typedef double* Jacobian3D_t @dim(Q3D, 3, 3, NE);
|
||||
typedef double* Jacobian2D_t @dim(2, 2, Q2D, NE);
|
||||
typedef double* Jacobian3D_t @dim(3, 3, Q3D, NE);
|
||||
|
||||
typedef double* SymmOperator2D_t @dim(Q2D, 3, NE);
|
||||
typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
typedef double* SymmOperator2D_t @dim(3, Q2D, NE);
|
||||
typedef double* SymmOperator3D_t @dim(6, Q3D, NE);
|
||||
|
||||
@kernel void DiffusionSetup2D(const int NE,
|
||||
@restrict const double *W,
|
||||
@@ -48,12 +48,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
@restrict SymmOperator2D_t op) {
|
||||
for (int e = 0; e < NE; ++e; @outer) {
|
||||
for (int q = 0; q < Q2D; ++q; @inner) {
|
||||
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e);
|
||||
const double J21 = J(q, 0, 1, e), J22 = J(q, 1, 1, e);
|
||||
const double 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(q, 0, e) = c_detJ * (J21*J21 + J22*J22); // (1,1)
|
||||
op(q, 1, e) = -c_detJ * (J21*J11 + J22*J12); // (1,2), (2,1)
|
||||
op(q, 2, e) = c_detJ * (J11*J11 + J12*J12); // (2,2)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,9 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
@restrict SymmOperator3D_t op) {
|
||||
for (int e = 0; e < NE; ++e; @outer) {
|
||||
for (int q = 0; q < Q3D; ++q; @inner) {
|
||||
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e), J13 = J(q, 2, 0, e);
|
||||
const double J21 = J(q, 0, 1, e), J22 = J(q, 1, 1, e), J23 = J(q, 2, 1, e);
|
||||
const double J31 = J(q, 0, 2, e), J32 = J(q, 1, 2, e), J33 = J(q, 2, 2, e);
|
||||
const double J11 = J(0, 0, q, e), J12 = J(1, 0, q, e), J13 = J(2, 0, q, e);
|
||||
const double J21 = J(0, 1, q, e), J22 = J(1, 1, q, e), J23 = J(2, 1, q, e);
|
||||
const double J31 = J(0, 2, q, e), J32 = J(1, 2, q, e), J33 = J(2, 2, q, e);
|
||||
|
||||
const double detJ = ((J11 * J22 * J33) + (J12 * J23 * J31) + (J13 * J21 * J32) -
|
||||
(J13 * J22 * J31) - (J12 * J21 * J33) - (J11 * J23 * J32));
|
||||
@@ -88,12 +88,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
const double A33 = (J11 * J22) - (J12 * J21);
|
||||
|
||||
// adj(J)^Tadj(J)
|
||||
op(q, 0, e) = c_detJ * (A11*A11 + A21*A21 + A31*A31); // (1,1)
|
||||
op(q, 1, e) = c_detJ * (A11*A12 + A21*A22 + A31*A32); // (1,2), (2,1)
|
||||
op(q, 2, e) = c_detJ * (A11*A13 + A21*A23 + A31*A33); // (1,3), (3,1)
|
||||
op(q, 3, e) = c_detJ * (A12*A12 + A22*A22 + A32*A32); // (2,2)
|
||||
op(q, 4, e) = c_detJ * (A12*A13 + A22*A23 + A32*A33); // (2,3), (3,2)
|
||||
op(q, 5, e) = c_detJ * (A13*A13 + A23*A23 + A33*A33); // (3,3)
|
||||
op(0, q, e) = c_detJ * (A11*A11 + A21*A21 + A31*A31); // (1,1)
|
||||
op(1, q, e) = c_detJ * (A11*A12 + A21*A22 + A31*A32); // (1,2), (2,1)
|
||||
op(2, q, e) = c_detJ * (A11*A13 + A21*A23 + A31*A33); // (1,3), (3,1)
|
||||
op(3, q, e) = c_detJ * (A12*A12 + A22*A22 + A32*A32); // (2,2)
|
||||
op(4, q, e) = c_detJ * (A12*A13 + A22*A23 + A32*A33); // (2,3), (3,2)
|
||||
op(5, q, e) = c_detJ * (A13*A13 + A23*A23 + A33*A33); // (3,3)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,9 +146,9 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
for (int qy = 0; qy < Q1D; ++qy) {
|
||||
for (int qx = 0; qx < Q1D; ++qx) {
|
||||
const int q = QUAD_2D_ID(qx, qy);
|
||||
const double O11 = op(q, 0, e);
|
||||
const double O12 = op(q, 1, e);
|
||||
const double O22 = op(q, 2, e);
|
||||
const double O11 = op(0, q, e);
|
||||
const double O12 = op(1, q, e);
|
||||
const double O22 = op(2, q, e);
|
||||
|
||||
const double gradX = grad[qy][qx][0];
|
||||
const double gradY = grad[qy][qx][1];
|
||||
@@ -255,9 +255,9 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
}
|
||||
|
||||
const int q = QUAD_2D_ID(qx, qy);
|
||||
const double O11 = op(q, 0, e);
|
||||
const double O12 = op(q, 1, e);
|
||||
const double O22 = op(q, 2, e);
|
||||
const double O11 = op(0, q, e);
|
||||
const double O12 = op(1, q, e);
|
||||
const double O22 = op(2, q, e);
|
||||
|
||||
s_grad(0, qx, qy) = (O11 * gradX) + (O12 * gradY);
|
||||
s_grad(1, qx, qy) = (O12 * gradX) + (O22 * gradY);
|
||||
@@ -382,12 +382,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
for (int qy = 0; qy < Q1D; ++qy) {
|
||||
for (int qx = 0; qx < Q1D; ++qx) {
|
||||
const int q = QUAD_3D_ID(qx, qy, qz);
|
||||
const double O11 = op(q, 0, e);
|
||||
const double O12 = op(q, 1, e);
|
||||
const double O13 = op(q, 2, e);
|
||||
const double O22 = op(q, 3, e);
|
||||
const double O23 = op(q, 4, e);
|
||||
const double O33 = op(q, 5, e);
|
||||
const double O11 = op(0, q, e);
|
||||
const double O12 = op(1, q, e);
|
||||
const double O13 = op(2, q, e);
|
||||
const double O22 = op(3, q, e);
|
||||
const double O23 = op(4, q, e);
|
||||
const double O33 = op(5, q, e);
|
||||
|
||||
const double gradX = grad[qz][qy][qx][0];
|
||||
const double gradY = grad[qz][qy][qx][1];
|
||||
@@ -557,12 +557,12 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
|
||||
}
|
||||
|
||||
const int q = QUAD_3D_ID(qx, qy, qz);
|
||||
const double O11 = op(q, 0, e);
|
||||
const double O12 = op(q, 1, e);
|
||||
const double O13 = op(q, 2, e);
|
||||
const double O22 = op(q, 3, e);
|
||||
const double O23 = op(q, 4, e);
|
||||
const double O33 = op(q, 5, e);
|
||||
const double O11 = op(0, q, e);
|
||||
const double O12 = op(1, q, e);
|
||||
const double O13 = op(2, q, e);
|
||||
const double O22 = op(3, q, e);
|
||||
const double O23 = op(4, q, e);
|
||||
const double O33 = op(5, q, e);
|
||||
|
||||
const double qDxyz = (O11 * Dxyz) + (O12 * xDyz) + (O13 * xyDz);
|
||||
const double qxDyz = (O12 * Dxyz) + (O22 * xDyz) + (O23 * xyDz);
|
||||
|
||||
@@ -203,14 +203,7 @@ void ParBilinearForm::AssembleSharedFaces(int skip_zeros)
|
||||
vdofs1.Copy(vdofs_all);
|
||||
for (int j = 0; j < vdofs2.Size(); j++)
|
||||
{
|
||||
if (vdofs2[j] >= 0)
|
||||
{
|
||||
vdofs2[j] += height;
|
||||
}
|
||||
else
|
||||
{
|
||||
vdofs2[j] -= height;
|
||||
}
|
||||
vdofs2[j] += height;
|
||||
}
|
||||
vdofs_all.Append(vdofs2);
|
||||
for (int k = 0; k < fbfi.Size(); k++)
|
||||
|
||||
+35
-55
@@ -613,15 +613,15 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
|
||||
int ldof = GetVSize();
|
||||
int ltdof = TrueVSize();
|
||||
|
||||
HYPRE_Int *i_diag = new HYPRE_Int[ldof+1];
|
||||
HYPRE_Int *j_diag = new HYPRE_Int[ltdof];
|
||||
HYPRE_Int *i_diag = mfem::New<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_diag = mfem::New<HYPRE_Int>(ltdof);
|
||||
int diag_counter;
|
||||
|
||||
HYPRE_Int *i_offd = new HYPRE_Int[ldof+1];
|
||||
HYPRE_Int *j_offd = new HYPRE_Int[ldof-ltdof];
|
||||
HYPRE_Int *i_offd = mfem::New<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_offd = mfem::New<HYPRE_Int>(ldof-ltdof);
|
||||
int offd_counter;
|
||||
|
||||
HYPRE_Int *cmap = new HYPRE_Int[ldof-ltdof];
|
||||
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(ldof-ltdof);
|
||||
|
||||
HYPRE_Int *col_starts = GetTrueDofOffsets();
|
||||
HYPRE_Int *row_starts = GetDofOffsets();
|
||||
@@ -747,14 +747,12 @@ void ParFiniteElementSpace::GetEssentialTrueDofs(const Array<int>
|
||||
// Verify that in boolean arithmetic: P^T ess_dofs = R ess_dofs.
|
||||
Array<int> true_ess_dofs2(true_ess_dofs.Size());
|
||||
HypreParMatrix *Pt = Dof_TrueDof_Matrix()->Transpose();
|
||||
const int *ess_dofs_data = ess_dofs.HostRead();
|
||||
Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
|
||||
Pt->BooleanMult(1, ess_dofs, 0, true_ess_dofs2);
|
||||
delete Pt;
|
||||
int counter = 0;
|
||||
const int *ted = true_ess_dofs.HostRead();
|
||||
for (int i = 0; i < true_ess_dofs.Size(); i++)
|
||||
{
|
||||
if (bool(ted[i]) != bool(true_ess_dofs2[i])) { counter++; }
|
||||
if (bool(true_ess_dofs[i]) != bool(true_ess_dofs2[i])) { counter++; }
|
||||
}
|
||||
MFEM_VERIFY(counter == 0, "internal MFEM error: counter = " << counter);
|
||||
#endif
|
||||
@@ -904,15 +902,11 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
{
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
if (ldof_marker[ldofs[j]] != fn)
|
||||
{
|
||||
ldof_marker[ldof] = fn;
|
||||
ldof_marker[ldofs[j]] = fn;
|
||||
send_face_nbr_ldof.AddAColumnInRow(fn);
|
||||
}
|
||||
}
|
||||
send_nbr_elem_dof.AddColumnsInRow(send_el_off[fn] + i, ldofs.Size());
|
||||
}
|
||||
|
||||
@@ -966,11 +960,9 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
GetElementVDofs(my_elems[i], ldofs);
|
||||
for (int j = 0; j < ldofs.Size(); j++)
|
||||
{
|
||||
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
|
||||
|
||||
if (ldof_marker[ldof] != fn)
|
||||
if (ldof_marker[ldofs[j]] != fn)
|
||||
{
|
||||
ldof_marker[ldof] = fn;
|
||||
ldof_marker[ldofs[j]] = fn;
|
||||
send_face_nbr_ldof.AddConnection(fn, ldofs[j]);
|
||||
}
|
||||
}
|
||||
@@ -991,14 +983,12 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
|
||||
for (int i = 0; i < num_ldofs; i++)
|
||||
{
|
||||
int ldof = (ldofs[i] >= 0 ? ldofs[i] : -1-ldofs[i]);
|
||||
ldof_marker[ldof] = i;
|
||||
ldof_marker[ldofs[i]] = i;
|
||||
}
|
||||
|
||||
for ( ; j < j_end; j++)
|
||||
{
|
||||
int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
|
||||
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
|
||||
send_J[j] = ldof_marker[send_J[j]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,14 +1023,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
|
||||
for ( ; j < j_end; j++)
|
||||
{
|
||||
if (recv_J[j] >= 0)
|
||||
{
|
||||
recv_J[j] += shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
recv_J[j] -= shift;
|
||||
}
|
||||
recv_J[j] += shift;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1089,15 +1072,8 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
for (int fn = 0, j = 0; fn < num_face_nbrs; fn++)
|
||||
{
|
||||
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
|
||||
{
|
||||
int ldof = face_nbr_ldof.GetJ()[j];
|
||||
if (ldof < 0)
|
||||
{
|
||||
ldof = -1-ldof;
|
||||
}
|
||||
|
||||
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
|
||||
}
|
||||
face_nbr_glob_dof_map[j] =
|
||||
dof_face_nbr_offsets[fn] + face_nbr_ldof.GetJ()[j];
|
||||
}
|
||||
|
||||
MPI_Waitall(num_face_nbrs, send_requests, statuses);
|
||||
@@ -2273,7 +2249,7 @@ HypreParMatrix* ParFiniteElementSpace
|
||||
}
|
||||
|
||||
// create offd column mapping
|
||||
HYPRE_Int *cmap = new HYPRE_Int[col_map.size()];
|
||||
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(col_map.size());
|
||||
int offd_col = 0;
|
||||
for (std::map<HYPRE_Int, int>::iterator
|
||||
it = col_map.begin(); it != col_map.end(); ++it)
|
||||
@@ -2282,14 +2258,14 @@ HypreParMatrix* ParFiniteElementSpace
|
||||
it->second = offd_col++;
|
||||
}
|
||||
|
||||
HYPRE_Int *I_diag = new HYPRE_Int[vdim*local_rows + 1];
|
||||
HYPRE_Int *I_offd = new HYPRE_Int[vdim*local_rows + 1];
|
||||
HYPRE_Int *I_diag = mfem::New<HYPRE_Int>(vdim*local_rows + 1);
|
||||
HYPRE_Int *I_offd = mfem::New<HYPRE_Int>(vdim*local_rows + 1);
|
||||
|
||||
HYPRE_Int *J_diag = new HYPRE_Int[nnz_diag];
|
||||
HYPRE_Int *J_offd = new HYPRE_Int[nnz_offd];
|
||||
HYPRE_Int *J_diag = mfem::New<HYPRE_Int>(nnz_diag);
|
||||
HYPRE_Int *J_offd = mfem::New<HYPRE_Int>(nnz_offd);
|
||||
|
||||
double *A_diag = new double[nnz_diag];
|
||||
double *A_offd = new double[nnz_offd];
|
||||
double *A_diag = mfem::New<double>(nnz_diag);
|
||||
double *A_offd = mfem::New<double>(nnz_offd);
|
||||
|
||||
int vdim1 = bynodes ? vdim : 1;
|
||||
int vdim2 = bynodes ? 1 : vdim;
|
||||
@@ -2340,7 +2316,7 @@ HypreParMatrix* ParFiniteElementSpace
|
||||
|
||||
static HYPRE_Int* make_i_array(int nrows)
|
||||
{
|
||||
HYPRE_Int *I = new HYPRE_Int[nrows+1];
|
||||
HYPRE_Int *I = mfem::New<HYPRE_Int>(nrows+1);
|
||||
for (int i = 0; i <= nrows; i++) { I[i] = -1; }
|
||||
return I;
|
||||
}
|
||||
@@ -2352,7 +2328,7 @@ static HYPRE_Int* make_j_array(HYPRE_Int* I, int nrows)
|
||||
{
|
||||
if (I[i] >= 0) { nnz++; }
|
||||
}
|
||||
HYPRE_Int *J = new HYPRE_Int[nnz];
|
||||
HYPRE_Int *J = mfem::New<HYPRE_Int>(nnz);
|
||||
|
||||
I[nrows] = -1;
|
||||
for (int i = 0, k = 0; i <= nrows; i++)
|
||||
@@ -2451,7 +2427,7 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
}
|
||||
SortPairs<HYPRE_Int, int>(cmap_offd, offd_cols);
|
||||
|
||||
HYPRE_Int* cmap = new HYPRE_Int[offd_cols];
|
||||
HYPRE_Int* cmap = mfem::New<HYPRE_Int>(offd_cols);
|
||||
for (int i = 0; i < offd_cols; i++)
|
||||
{
|
||||
cmap[i] = cmap_offd[i].one;
|
||||
@@ -2647,7 +2623,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
offd->SetWidth(col_map.size());
|
||||
|
||||
// create offd column mapping for use by hypre
|
||||
HYPRE_Int *cmap = new HYPRE_Int[offd->Width()];
|
||||
HYPRE_Int *cmap = mfem::New<HYPRE_Int>(offd->Width());
|
||||
for (std::map<HYPRE_Int, int>::iterator
|
||||
it = col_map.begin(); it != col_map.end(); ++it)
|
||||
{
|
||||
@@ -2887,8 +2863,9 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
|
||||
MFEM_ASSERT(x.Size() == Width(), "");
|
||||
MFEM_ASSERT(y.Size() == Height(), "");
|
||||
|
||||
const double *xdata = x.HostRead();
|
||||
double *ydata = y.HostWrite();
|
||||
const double *xdata = x.GetData();
|
||||
double *ydata = y.GetData();
|
||||
x.Pull();
|
||||
const int m = external_ldofs.Size();
|
||||
|
||||
const int in_layout = 2; // 2 - input is ltdofs array
|
||||
@@ -2905,6 +2882,7 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
|
||||
|
||||
const int out_layout = 0; // 0 - output is ldofs array
|
||||
gc.BcastEnd(ydata, out_layout);
|
||||
y.Push();
|
||||
}
|
||||
|
||||
void ConformingProlongationOperator::MultTranspose(
|
||||
@@ -2913,8 +2891,9 @@ void ConformingProlongationOperator::MultTranspose(
|
||||
MFEM_ASSERT(x.Size() == Height(), "");
|
||||
MFEM_ASSERT(y.Size() == Width(), "");
|
||||
|
||||
const double *xdata = x.HostRead();
|
||||
double *ydata = y.HostWrite();
|
||||
const double *xdata = x.GetData();
|
||||
double *ydata = y.GetData();
|
||||
x.Pull();
|
||||
const int m = external_ldofs.Size();
|
||||
|
||||
gc.ReduceBegin(xdata);
|
||||
@@ -2930,6 +2909,7 @@ void ConformingProlongationOperator::MultTranspose(
|
||||
|
||||
const int out_layout = 2; // 2 - output is an array on all ltdofs
|
||||
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
|
||||
y.Push();
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+14
-15
@@ -367,10 +367,10 @@ void ParGridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<int>(zones_per_vdof, GroupCommunicator::Sum);
|
||||
gcomm.Bcast(zones_per_vdof);
|
||||
|
||||
// Accumulate for all vdofs.
|
||||
gcomm.Reduce<double>(data, GroupCommunicator::Sum);
|
||||
gcomm.Bcast<double>(data);
|
||||
// Accumulate for all tdofs.
|
||||
HypreParVector *tv = this->ParallelAssemble();
|
||||
this->Distribute(tv);
|
||||
delete tv;
|
||||
|
||||
ComputeMeans(type, zones_per_vdof);
|
||||
}
|
||||
@@ -389,10 +389,10 @@ void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &vcoeff,
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<int>(zones_per_vdof, GroupCommunicator::Sum);
|
||||
gcomm.Bcast(zones_per_vdof);
|
||||
|
||||
// Accumulate for all vdofs.
|
||||
gcomm.Reduce<double>(data, GroupCommunicator::Sum);
|
||||
gcomm.Bcast<double>(data);
|
||||
// Accumulate for all tdofs.
|
||||
HypreParVector *tv = this->ParallelAssemble();
|
||||
this->Distribute(tv);
|
||||
delete tv;
|
||||
|
||||
ComputeMeans(type, zones_per_vdof);
|
||||
}
|
||||
@@ -425,8 +425,8 @@ void ParGridFunction::ProjectBdrCoefficient(
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: is this the same as the conforming case (after the merge of
|
||||
// cut-mesh-groups-dev)?
|
||||
// FIXME: same as the conforming case after 'cut-mesh-groups-dev-*' is
|
||||
// merged?
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
#ifdef MFEM_DEBUG
|
||||
@@ -469,8 +469,8 @@ void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: is this the same as the conforming case (after the merge of
|
||||
// cut-mesh-groups-dev)?
|
||||
// FIXME: same as the conforming case after 'cut-mesh-groups-dev-*' is
|
||||
// merged?
|
||||
ComputeMeans(ARITHMETIC, values_counter);
|
||||
}
|
||||
#ifdef MFEM_DEBUG
|
||||
@@ -487,17 +487,16 @@ void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
|
||||
void ParGridFunction::Save(std::ostream &out) const
|
||||
{
|
||||
double *data_ = const_cast<double*>(HostRead());
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
|
||||
if (pfes->GetDofSign(i) < 0) { data[i] = -data[i]; }
|
||||
}
|
||||
|
||||
GridFunction::Save(out);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
|
||||
if (pfes->GetDofSign(i) < 0) { data[i] = -data[i]; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -956,13 +956,13 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
Tpr->Attribute = T.Attribute;
|
||||
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
|
||||
}
|
||||
// TODO: computing the coefficients 'coeff1' and 'coeff0' in physical
|
||||
// coordinates means that, generally, the gradient and Hessian of the
|
||||
// TMOP_Integrator will depend on the derivatives of the coefficients.
|
||||
// FIXME: computing the coefficients 'coeff1' and 'coeff0' in physical
|
||||
// coordinates means that, generally, the gradient and Hessian of the
|
||||
// TMOP_Integrator will depend on the derivatives of the coefficients.
|
||||
//
|
||||
// In some cases the coefficients are independent of any movement of
|
||||
// the physical coordinates (i.e. changes in 'elfun'), e.g. when the
|
||||
// coefficient is a ConstantCoefficient or a GridFunctionCoefficient.
|
||||
// In some cases the coefficients are independent of any movement of
|
||||
// the physical coordinates (i.e. changes in 'elfun'), e.g. when the
|
||||
// coefficient is a ConstantCoefficient or a GridFunctionCoefficient.
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
|
||||
+43
-5
@@ -19,12 +19,54 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
BaseArray::BaseArray(int asize, int ainc, int elementsize)
|
||||
{
|
||||
if (asize > 0)
|
||||
{
|
||||
data = mfem::New<char>(asize * elementsize);
|
||||
size = allocsize = asize;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = 0;
|
||||
size = allocsize = 0;
|
||||
}
|
||||
inc = ainc;
|
||||
}
|
||||
|
||||
BaseArray::~BaseArray()
|
||||
{
|
||||
if (allocsize > 0)
|
||||
{
|
||||
mfem::Delete((char*)data);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseArray::GrowSize(int minsize, int elementsize)
|
||||
{
|
||||
void *p;
|
||||
int nsize = (inc > 0) ? abs(allocsize) + inc : 2 * abs(allocsize);
|
||||
if (nsize < minsize) { nsize = minsize; }
|
||||
|
||||
p = mfem::New<char>(nsize * elementsize);
|
||||
if (size > 0)
|
||||
{
|
||||
mfem::Memcpy(p, data, size * elementsize);
|
||||
}
|
||||
if (allocsize > 0)
|
||||
{
|
||||
mfem::Delete((char*)data);
|
||||
}
|
||||
data = p;
|
||||
allocsize = nsize;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Print(std::ostream &out, int width) const
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
out << data[i];
|
||||
out << ((T*)data)[i];
|
||||
if ( !((i+1) % width) || i+1 == size )
|
||||
{
|
||||
out << '\n';
|
||||
@@ -71,12 +113,10 @@ T Array<T>::Max() const
|
||||
|
||||
T max = operator[](0);
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (max < operator[](i))
|
||||
{
|
||||
max = operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
@@ -88,12 +128,10 @@ T Array<T>::Min() const
|
||||
|
||||
T min = operator[](0);
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (operator[](i) < min)
|
||||
{
|
||||
min = operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
+108
-181
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "mem_manager.hpp"
|
||||
#include "device.hpp"
|
||||
#include "error.hpp"
|
||||
#include "globals.hpp"
|
||||
|
||||
@@ -26,6 +25,31 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Base class for array container.
|
||||
class BaseArray
|
||||
{
|
||||
protected:
|
||||
/// Pointer to data
|
||||
void *data;
|
||||
/// Size of the array
|
||||
int size;
|
||||
/// Size of the allocated memory
|
||||
int allocsize;
|
||||
/** Increment of allocated memory on overflow,
|
||||
inc = 0 doubles the array */
|
||||
int inc;
|
||||
|
||||
BaseArray() { }
|
||||
/// Creates array of asize elements of size elementsize
|
||||
BaseArray(int asize, int ainc, int elmentsize);
|
||||
/// Free the allocated memory
|
||||
~BaseArray();
|
||||
/** Increases the allocsize of the array to be at least minsize.
|
||||
The current content of the array is copied to the newly allocated
|
||||
space. minsize must be > abs(allocsize). */
|
||||
void GrowSize(int minsize, int elementsize);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Array;
|
||||
|
||||
@@ -41,78 +65,70 @@ void Swap(Array<T> &, Array<T> &);
|
||||
The elements can be accessed by the [] operator, the range is 0 to size-1.
|
||||
*/
|
||||
template <class T>
|
||||
class Array
|
||||
class Array : public BaseArray
|
||||
{
|
||||
protected:
|
||||
/// Pointer to data
|
||||
Memory<T> data;
|
||||
/// Size of the array
|
||||
int size;
|
||||
|
||||
inline void GrowSize(int minsize);
|
||||
|
||||
public:
|
||||
friend void Swap<T>(Array<T> &, Array<T> &);
|
||||
|
||||
/// Creates array of asize elements
|
||||
explicit inline Array(int asize = 0)
|
||||
: size(asize) { asize > 0 ? data.New(asize) : data.Reset(); }
|
||||
explicit inline Array(int asize = 0, int ainc = 0)
|
||||
: BaseArray(asize, ainc, sizeof (T)) { }
|
||||
|
||||
/** Creates array using an existing c-array of asize elements;
|
||||
allocsize is set to -asize to indicate that the data will not
|
||||
be deleted. */
|
||||
inline Array(T *_data, int asize)
|
||||
{ data.Wrap(_data, asize, false); size = asize; }
|
||||
inline Array(T *_data, int asize, int ainc = 0)
|
||||
{ data = _data; size = asize; allocsize = -asize; inc = ainc; }
|
||||
|
||||
/// Copy constructor: deep copy
|
||||
/** This method supports source arrays using any MemoryType. */
|
||||
inline Array(const Array &src);
|
||||
Array(const Array<T> &src)
|
||||
: BaseArray(src.size, 0, sizeof(T))
|
||||
{ mfem::Memcpy(data, src.data, size*sizeof(T)); }
|
||||
|
||||
/// Copy constructor (deep copy) from an Array of convertable type
|
||||
template <typename CT>
|
||||
inline Array(const Array<CT> &src);
|
||||
Array(const Array<CT> &src)
|
||||
: BaseArray(src.Size(), 0, sizeof(T))
|
||||
{ for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); } }
|
||||
|
||||
/// Destructor
|
||||
inline ~Array() { data.Delete(); }
|
||||
inline ~Array() { }
|
||||
|
||||
/// Assignment operator: deep copy
|
||||
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
|
||||
|
||||
/// Assignment operator (deep copy) from an Array of convertable type
|
||||
template <typename CT>
|
||||
inline Array &operator=(const Array<CT> &src);
|
||||
Array<T> &operator=(const Array<CT> &src)
|
||||
{
|
||||
SetSize(src.Size());
|
||||
for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return the data as 'T *'
|
||||
inline operator T *() { return data; }
|
||||
inline operator T *() { return (T *)data; }
|
||||
|
||||
/// Return the data as 'const T *'
|
||||
inline operator const T *() const { return data; }
|
||||
inline operator const T *() const { return (const T *)data; }
|
||||
|
||||
/// Returns the data
|
||||
inline T *GetData() { return data; }
|
||||
inline T *GetData() { return (T *)data; }
|
||||
/// Returns the data
|
||||
inline const T *GetData() const { return data; }
|
||||
|
||||
/// Return a reference to the Memory object used by the Array.
|
||||
Memory<T> &GetMemory() { return data; }
|
||||
|
||||
/// Return a reference to the Memory object used by the Array, const version.
|
||||
const Memory<T> &GetMemory() const { return data; }
|
||||
|
||||
/// Return the device flag of the Memory object used by the Array
|
||||
bool UseDevice() const { return data.UseDevice(); }
|
||||
inline const T *GetData() const { return (T *)data; }
|
||||
|
||||
/// Return true if the data will be deleted by the array
|
||||
inline bool OwnsData() const { return data.OwnsHostPtr(); }
|
||||
inline bool OwnsData() const { return (allocsize > 0); }
|
||||
|
||||
/// Changes the ownership of the data
|
||||
inline void StealData(T **p) { *p = data; data.Reset(); size = 0; }
|
||||
inline void StealData(T **p)
|
||||
{ *p = (T*)data; data = 0; size = allocsize = 0; }
|
||||
|
||||
/// NULL-ifies the data
|
||||
inline void LoseData() { data.Reset(); size = 0; }
|
||||
inline void LoseData() { data = 0; size = allocsize = 0; }
|
||||
|
||||
/// Make the Array own the data
|
||||
void MakeDataOwner() const { data.SetHostPtrOwner(true); }
|
||||
void MakeDataOwner() { allocsize = abs(allocsize); }
|
||||
|
||||
/// Logical size of the array
|
||||
inline int Size() const { return size; }
|
||||
@@ -123,18 +139,13 @@ public:
|
||||
/// Same as SetSize(int) plus initialize new entries with 'initval'
|
||||
inline void SetSize(int nsize, const T &initval);
|
||||
|
||||
/** @brief Resize the array to size @a nsize using MemoryType @a mt. Note
|
||||
that unlike the other versions of SetSize(), the current content of the
|
||||
array is not preserved. */
|
||||
inline void SetSize(int nsize, MemoryType mt);
|
||||
|
||||
/** Maximum number of entries the array can store without allocating more
|
||||
memory. */
|
||||
inline int Capacity() const { return data.Capacity(); }
|
||||
inline int Capacity() const { return abs(allocsize); }
|
||||
|
||||
/// Ensures that the allocated size is at least the given size.
|
||||
inline void Reserve(int capacity)
|
||||
{ if (capacity > Capacity()) { GrowSize(capacity); } }
|
||||
{ if (capacity > abs(allocsize)) { GrowSize(capacity, sizeof(T)); } }
|
||||
|
||||
/// Access element
|
||||
inline T & operator[](int i);
|
||||
@@ -177,7 +188,11 @@ public:
|
||||
inline void DeleteAll();
|
||||
|
||||
/// Create a copy of the current array
|
||||
inline void Copy(Array ©) const;
|
||||
inline void Copy(Array ©) const
|
||||
{
|
||||
copy.SetSize(Size());
|
||||
mfem::Memcpy(copy.GetData(), data, Size()*sizeof(T));
|
||||
}
|
||||
|
||||
/// Make this Array a reference to a pointer
|
||||
inline void MakeRef(T *, int);
|
||||
@@ -185,7 +200,7 @@ public:
|
||||
/// Make this Array a reference to 'master'
|
||||
inline void MakeRef(const Array &master);
|
||||
|
||||
inline void GetSubArray(int offset, int sa_size, Array<T> &sa) const;
|
||||
inline void GetSubArray(int offset, int sa_size, Array<T> &sa);
|
||||
|
||||
/// Prints array to stream with width elements per row
|
||||
void Print(std::ostream &out = mfem::out, int width = 4) const;
|
||||
@@ -220,18 +235,18 @@ public:
|
||||
T Min() const;
|
||||
|
||||
/// Sorts the array. This requires operator< to be defined for T.
|
||||
void Sort() { std::sort((T*)data, data + size); }
|
||||
void Sort() { std::sort((T*) data, (T*) data + size); }
|
||||
|
||||
/// Sorts the array using the supplied comparison function object.
|
||||
template<class Compare>
|
||||
void Sort(Compare cmp) { std::sort((T*)data, data + size, cmp); }
|
||||
void Sort(Compare cmp) { std::sort((T*) data, (T*) data + size, cmp); }
|
||||
|
||||
/** Removes duplicities from a sorted array. This requires operator== to be
|
||||
defined for T. */
|
||||
void Unique()
|
||||
{
|
||||
T* end = std::unique((T*)data, data + size);
|
||||
SetSize(end - data);
|
||||
T* end = std::unique((T*) data, (T*) data + size);
|
||||
SetSize(end - (T*) data);
|
||||
}
|
||||
|
||||
/// return true if the array is sorted.
|
||||
@@ -252,36 +267,10 @@ public:
|
||||
inline void CopyTo(U *dest) { std::copy(begin(), end(), dest); }
|
||||
|
||||
// STL-like begin/end
|
||||
inline T* begin() { return data; }
|
||||
inline T* end() { return data + size; }
|
||||
inline const T* begin() const { return data; }
|
||||
inline const T* end() const { return data + size; }
|
||||
inline T* begin() const { return (T*) data; }
|
||||
inline T* end() const { return (T*) data + size; }
|
||||
|
||||
long MemoryUsage() const { return Capacity() * sizeof(T); }
|
||||
|
||||
/// Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
|
||||
const T *Read(bool on_dev = true) const
|
||||
{ return mfem::Read(data, size, on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
|
||||
const T *HostRead() const
|
||||
{ return mfem::Read(data, size, false); }
|
||||
|
||||
/// Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
|
||||
T *Write(bool on_dev = true)
|
||||
{ return mfem::Write(data, size, on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
|
||||
T *HostWrite()
|
||||
{ return mfem::Write(data, size, false); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), on_dev).
|
||||
T *ReadWrite(bool on_dev = true)
|
||||
{ return mfem::ReadWrite(data, size, on_dev); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
|
||||
T *HostReadWrite()
|
||||
{ return mfem::ReadWrite(data, size, false); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@@ -289,9 +278,7 @@ inline bool operator==(const Array<T> &LHS, const Array<T> &RHS)
|
||||
{
|
||||
if ( LHS.Size() != RHS.Size() ) { return false; }
|
||||
for (int i=0; i<LHS.Size(); i++)
|
||||
{
|
||||
if ( LHS[i] != RHS[i] ) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -578,51 +565,17 @@ inline void Swap(Array<T> &a, Array<T> &b)
|
||||
{
|
||||
Swap(a.data, b.data);
|
||||
Swap(a.size, b.size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline Array<T>::Array(const Array &src)
|
||||
: size(src.Size())
|
||||
{
|
||||
size > 0 ? data.New(size, src.data.GetMemoryType()) : data.Reset();
|
||||
data.CopyFrom(src.data, size);
|
||||
data.UseDevice(src.data.UseDevice());
|
||||
}
|
||||
|
||||
template <typename T> template <typename CT>
|
||||
inline Array<T>::Array(const Array<CT> &src)
|
||||
: size(src.Size())
|
||||
{
|
||||
size > 0 ? data.New(size) : data.Reset();
|
||||
for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); }
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::GrowSize(int minsize)
|
||||
{
|
||||
const int nsize = std::max(minsize, 2 * data.Capacity());
|
||||
Memory<T> p(nsize, data.GetMemoryType());
|
||||
p.CopyFrom(data, size);
|
||||
p.UseDevice(data.UseDevice());
|
||||
data.Delete();
|
||||
data = p;
|
||||
}
|
||||
|
||||
template <typename T> template <typename CT>
|
||||
inline Array<T> &Array<T>::operator=(const Array<CT> &src)
|
||||
{
|
||||
SetSize(src.Size());
|
||||
for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); }
|
||||
return *this;
|
||||
Swap(a.allocsize, b.allocsize);
|
||||
Swap(a.inc, b.inc);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::SetSize(int nsize)
|
||||
{
|
||||
MFEM_ASSERT( nsize>=0, "Size must be non-negative. It is " << nsize );
|
||||
if (nsize > Capacity())
|
||||
if (nsize > abs(allocsize))
|
||||
{
|
||||
GrowSize(nsize);
|
||||
GrowSize(nsize, sizeof(T));
|
||||
}
|
||||
size = nsize;
|
||||
}
|
||||
@@ -633,51 +586,24 @@ inline void Array<T>::SetSize(int nsize, const T &initval)
|
||||
MFEM_ASSERT( nsize>=0, "Size must be non-negative. It is " << nsize );
|
||||
if (nsize > size)
|
||||
{
|
||||
if (nsize > Capacity())
|
||||
if (nsize > abs(allocsize))
|
||||
{
|
||||
GrowSize(nsize);
|
||||
GrowSize(nsize, sizeof(T));
|
||||
}
|
||||
for (int i = size; i < nsize; i++)
|
||||
{
|
||||
data[i] = initval;
|
||||
((T*)data)[i] = initval;
|
||||
}
|
||||
}
|
||||
size = nsize;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::SetSize(int nsize, MemoryType mt)
|
||||
{
|
||||
MFEM_ASSERT(nsize >= 0, "invalid new size: " << nsize);
|
||||
if (mt == data.GetMemoryType())
|
||||
{
|
||||
if (nsize <= Capacity())
|
||||
{
|
||||
size = nsize;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const bool use_dev = data.UseDevice();
|
||||
data.Delete();
|
||||
if (nsize > 0)
|
||||
{
|
||||
data.New(nsize, mt);
|
||||
size = nsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
size = 0;
|
||||
}
|
||||
data.UseDevice(use_dev);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T &Array<T>::operator[](int i)
|
||||
{
|
||||
MFEM_ASSERT( i>=0 && i<size,
|
||||
"Access element " << i << " of array, size = " << size );
|
||||
return data[i];
|
||||
return ((T*)data)[i];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -685,14 +611,14 @@ inline const T &Array<T>::operator[](int i) const
|
||||
{
|
||||
MFEM_ASSERT( i>=0 && i<size,
|
||||
"Access element " << i << " of array, size = " << size );
|
||||
return data[i];
|
||||
return ((T*)data)[i];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline int Array<T>::Append(const T &el)
|
||||
{
|
||||
SetSize(size+1);
|
||||
data[size-1] = el;
|
||||
((T*)data)[size-1] = el;
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -704,7 +630,7 @@ inline int Array<T>::Append(const T *els, int nels)
|
||||
SetSize(size + nels);
|
||||
for (int i = 0; i < nels; i++)
|
||||
{
|
||||
data[old_size+i] = els[i];
|
||||
((T*)data)[old_size+i] = els[i];
|
||||
}
|
||||
return size;
|
||||
}
|
||||
@@ -715,9 +641,9 @@ inline int Array<T>::Prepend(const T &el)
|
||||
SetSize(size+1);
|
||||
for (int i = size-1; i > 0; i--)
|
||||
{
|
||||
data[i] = data[i-1];
|
||||
((T*)data)[i] = ((T*)data)[i-1];
|
||||
}
|
||||
data[0] = el;
|
||||
((T*)data)[0] = el;
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -725,21 +651,21 @@ template <class T>
|
||||
inline T &Array<T>::Last()
|
||||
{
|
||||
MFEM_ASSERT(size > 0, "Array size is zero: " << size);
|
||||
return data[size-1];
|
||||
return ((T*)data)[size-1];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline const T &Array<T>::Last() const
|
||||
{
|
||||
MFEM_ASSERT(size > 0, "Array size is zero: " << size);
|
||||
return data[size-1];
|
||||
return ((T*)data)[size-1];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline int Array<T>::Union(const T &el)
|
||||
{
|
||||
int i = 0;
|
||||
while ((i < size) && (data[i] != el)) { i++; }
|
||||
while ((i < size) && (((T*)data)[i] != el)) { i++; }
|
||||
if (i == size)
|
||||
{
|
||||
Append(el);
|
||||
@@ -752,7 +678,7 @@ inline int Array<T>::Find(const T &el) const
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (data[i] == el) { return i; }
|
||||
if (((T*)data)[i] == el) { return i; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -760,7 +686,7 @@ inline int Array<T>::Find(const T &el) const
|
||||
template <class T>
|
||||
inline int Array<T>::FindSorted(const T &el) const
|
||||
{
|
||||
const T *begin = data, *end = begin + size;
|
||||
const T *begin = (const T*) data, *end = begin + size;
|
||||
const T* first = std::lower_bound(begin, end, el);
|
||||
if (first == end || !(*first == el)) { return -1; }
|
||||
return first - begin;
|
||||
@@ -771,11 +697,11 @@ inline void Array<T>::DeleteFirst(const T &el)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (data[i] == el)
|
||||
if (((T*)data)[i] == el)
|
||||
{
|
||||
for (i++; i < size; i++)
|
||||
{
|
||||
data[i-1] = data[i];
|
||||
((T*)data)[i-1] = ((T*)data)[i];
|
||||
}
|
||||
size--;
|
||||
return;
|
||||
@@ -786,40 +712,41 @@ inline void Array<T>::DeleteFirst(const T &el)
|
||||
template <class T>
|
||||
inline void Array<T>::DeleteAll()
|
||||
{
|
||||
const bool use_dev = data.UseDevice();
|
||||
data.Delete();
|
||||
data.Reset();
|
||||
size = 0;
|
||||
data.UseDevice(use_dev);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Array<T>::Copy(Array ©) const
|
||||
{
|
||||
copy.SetSize(Size(), data.GetMemoryType());
|
||||
data.CopyTo(copy.data, Size());
|
||||
copy.data.UseDevice(data.UseDevice());
|
||||
if (allocsize > 0)
|
||||
{
|
||||
mfem::Delete((char*)data);
|
||||
}
|
||||
data = NULL;
|
||||
size = allocsize = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(T *p, int s)
|
||||
{
|
||||
data.Delete();
|
||||
data.Wrap(p, s, false);
|
||||
if (allocsize > 0)
|
||||
{
|
||||
mfem::Delete((char*)data);
|
||||
}
|
||||
data = p;
|
||||
size = s;
|
||||
allocsize = -s;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(const Array &master)
|
||||
{
|
||||
data.Delete();
|
||||
data = master.data; // note: copies the device flag
|
||||
if (allocsize > 0)
|
||||
{
|
||||
mfem::Delete((char*)data);
|
||||
}
|
||||
data = master.data;
|
||||
size = master.size;
|
||||
data.ClearOwnerFlags();
|
||||
allocsize = -abs(master.allocsize);
|
||||
inc = master.inc;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::GetSubArray(int offset, int sa_size, Array<T> &sa) const
|
||||
inline void Array<T>::GetSubArray(int offset, int sa_size, Array<T> &sa)
|
||||
{
|
||||
sa.SetSize(sa_size);
|
||||
for (int i = 0; i < sa_size; i++)
|
||||
@@ -833,14 +760,14 @@ inline void Array<T>::operator=(const T &a)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
data[i] = a;
|
||||
((T*)data)[i] = a;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::Assign(const T *p)
|
||||
{
|
||||
data.CopyFromHost(p, Size());
|
||||
memcpy(data, p, Size()*sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-44
@@ -15,15 +15,11 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// Internal debug option, useful for tracking CUDA allocations, deallocations
|
||||
// and transfers.
|
||||
// #define MFEM_TRACK_CUDA_MEM
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
void mfem_cuda_error(cudaError_t err, const char *expr, const char *func,
|
||||
const char *file, int line)
|
||||
{
|
||||
mfem::err << "\n\nCUDA error: (" << expr << ") failed with error:\n --> "
|
||||
mfem::err << "CUDA error: (" << expr << ") failed with error:\n --> "
|
||||
<< cudaGetErrorString(err)
|
||||
<< "\n ... in function: " << func
|
||||
<< "\n ... in file: " << file << ':' << line << '\n';
|
||||
@@ -34,14 +30,7 @@ void mfem_cuda_error(cudaError_t err, const char *expr, const char *func,
|
||||
void* CuMemAlloc(void** dptr, size_t bytes)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "CuMemAlloc(): allocating " << bytes << " bytes ... "
|
||||
<< std::flush;
|
||||
#endif
|
||||
MFEM_CUDA_CHECK(cudaMalloc(dptr, bytes));
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "done: " << *dptr << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return *dptr;
|
||||
}
|
||||
@@ -49,14 +38,7 @@ void* CuMemAlloc(void** dptr, size_t bytes)
|
||||
void* CuMemFree(void *dptr)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "CuMemFree(): deallocating memory @ " << dptr << " ... "
|
||||
<< std::flush;
|
||||
#endif
|
||||
MFEM_CUDA_CHECK(cudaFree(dptr));
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "done." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return dptr;
|
||||
}
|
||||
@@ -64,14 +46,7 @@ void* CuMemFree(void *dptr)
|
||||
void* CuMemcpyHtoD(void* dst, const void* src, size_t bytes)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "CuMemcpyHtoD(): copying " << bytes << " bytes from "
|
||||
<< src << " to " << dst << " ... " << std::flush;
|
||||
#endif
|
||||
MFEM_CUDA_CHECK(cudaMemcpy(dst, src, bytes, cudaMemcpyHostToDevice));
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "done." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return dst;
|
||||
}
|
||||
@@ -84,22 +59,15 @@ void* CuMemcpyHtoDAsync(void* dst, const void* src, size_t bytes)
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* CuMemcpyDtoD(void *dst, const void *src, size_t bytes)
|
||||
void* CuMemcpyDtoD(void* dst, void* src, size_t bytes)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "CuMemcpyDtoD(): copying " << bytes << " bytes from "
|
||||
<< src << " to " << dst << " ... " << std::flush;
|
||||
#endif
|
||||
MFEM_CUDA_CHECK(cudaMemcpy(dst, src, bytes, cudaMemcpyDeviceToDevice));
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "done." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* CuMemcpyDtoDAsync(void* dst, const void *src, size_t bytes)
|
||||
void* CuMemcpyDtoDAsync(void* dst, void* src, size_t bytes)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
MFEM_CUDA_CHECK(cudaMemcpyAsync(dst, src, bytes, cudaMemcpyDeviceToDevice));
|
||||
@@ -107,22 +75,15 @@ void* CuMemcpyDtoDAsync(void* dst, const void *src, size_t bytes)
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* CuMemcpyDtoH(void *dst, const void *src, size_t bytes)
|
||||
void* CuMemcpyDtoH(void *dst, void *src, size_t bytes)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "CuMemcpyDtoH(): copying " << bytes << " bytes from "
|
||||
<< src << " to " << dst << " ... " << std::flush;
|
||||
#endif
|
||||
MFEM_CUDA_CHECK(cudaMemcpy(dst, src, bytes, cudaMemcpyDeviceToHost));
|
||||
#ifdef MFEM_TRACK_CUDA_MEM
|
||||
mfem::out << "done." << std::endl;
|
||||
#endif
|
||||
#endif
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* CuMemcpyDtoHAsync(void *dst, const void *src, size_t bytes)
|
||||
void* CuMemcpyDtoHAsync(void* dst, void* src, size_t bytes, void *s)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
MFEM_CUDA_CHECK(cudaMemcpyAsync(dst, src, bytes, cudaMemcpyDeviceToHost));
|
||||
|
||||
+9
-24
@@ -24,8 +24,8 @@
|
||||
#define MFEM_CUDA_BLOCKS 256
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
#define MFEM_DEVICE __device__
|
||||
#define MFEM_HOST_DEVICE __host__ __device__
|
||||
#define MFEM_ATTR_DEVICE __device__
|
||||
#define MFEM_ATTR_HOST_DEVICE __host__ __device__
|
||||
// Define a CUDA error check macro, MFEM_CUDA_CHECK(x), where x returns/is of
|
||||
// type 'cudaError_t'. This macro evaluates 'x' and raises an error if the
|
||||
// result is not cudaSuccess.
|
||||
@@ -39,26 +39,11 @@
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
#else
|
||||
#define MFEM_DEVICE
|
||||
#define MFEM_HOST_DEVICE
|
||||
#else // MFEM_USE_CUDA
|
||||
#define MFEM_ATTR_DEVICE
|
||||
#define MFEM_ATTR_HOST_DEVICE
|
||||
#endif // MFEM_USE_CUDA
|
||||
|
||||
// Define the MFEM inner threading macros
|
||||
#if defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)
|
||||
#define MFEM_SHARED __shared__
|
||||
#define MFEM_SYNC_THREAD __syncthreads()
|
||||
#define MFEM_THREAD_ID(k) threadIdx.k
|
||||
#define MFEM_THREAD_SIZE(k) blockDim.k
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=threadIdx.k; i<N; i+=blockDim.k)
|
||||
#else
|
||||
#define MFEM_SHARED
|
||||
#define MFEM_SYNC_THREAD
|
||||
#define MFEM_THREAD_ID(k) 0
|
||||
#define MFEM_THREAD_SIZE(k) 1
|
||||
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=0; i<N; i++)
|
||||
#endif
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -82,16 +67,16 @@ void* CuMemcpyHtoD(void *d_dst, const void *h_src, size_t bytes);
|
||||
void* CuMemcpyHtoDAsync(void *d_dst, const void *h_src, size_t bytes);
|
||||
|
||||
/// Copies memory from Device to Device
|
||||
void* CuMemcpyDtoD(void *d_dst, const void *d_src, size_t bytes);
|
||||
void* CuMemcpyDtoD(void *d_dst, void *d_src, size_t bytes);
|
||||
|
||||
/// Copies memory from Device to Device
|
||||
void* CuMemcpyDtoDAsync(void *d_dst, const void *d_src, size_t bytes);
|
||||
void* CuMemcpyDtoDAsync(void *d_dst, void *d_src, size_t bytes);
|
||||
|
||||
/// Copies memory from Device to Host
|
||||
void* CuMemcpyDtoH(void *h_dst, const void *d_src, size_t bytes);
|
||||
void* CuMemcpyDtoH(void *h_dst, void *d_src, size_t bytes);
|
||||
|
||||
/// Copies memory from Device to Host
|
||||
void* CuMemcpyDtoHAsync(void *h_dst, const void *d_src, size_t bytes);
|
||||
void* CuMemcpyDtoHAsync(void *h_dst, void *d_src, size_t bytes);
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
+7
-47
@@ -24,10 +24,7 @@ namespace mfem
|
||||
namespace internal
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_OCCA
|
||||
// Default occa::device used by MFEM.
|
||||
occa::device occaDevice;
|
||||
#endif
|
||||
OccaDevice occaDevice;
|
||||
|
||||
// Backends listed by priority, high to low:
|
||||
static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
|
||||
@@ -46,16 +43,6 @@ static const char *backend_name[Backend::NUM_BACKENDS] =
|
||||
|
||||
} // namespace mfem::internal
|
||||
|
||||
|
||||
// Initialize the unique global Device variable.
|
||||
Device Device::device_singleton;
|
||||
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
if (destroy_mm) { mm.Destroy(); }
|
||||
}
|
||||
|
||||
void Device::Configure(const std::string &device, const int dev)
|
||||
{
|
||||
std::map<std::string, Backend::Id> bmap;
|
||||
@@ -77,22 +64,18 @@ void Device::Configure(const std::string &device, const int dev)
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Perform setup.
|
||||
// Activate all backends for Setup().
|
||||
Get().allowed_backends = Get().backends;
|
||||
Get().Setup(dev);
|
||||
|
||||
// Enable the device
|
||||
Enable();
|
||||
|
||||
// Copy all data members from the global 'singleton_device' into '*this'.
|
||||
std::memcpy(this, &Get(), sizeof(Device));
|
||||
|
||||
// Only '*this' will call the MemoryManager::Destroy() method.
|
||||
destroy_mm = true;
|
||||
// Enable only the default host CPU backend.
|
||||
Get().allowed_backends = Backend::CPU;
|
||||
}
|
||||
|
||||
void Device::Print(std::ostream &out)
|
||||
@@ -101,7 +84,7 @@ void Device::Print(std::ostream &out)
|
||||
bool add_comma = false;
|
||||
for (int i = 0; i < Backend::NUM_BACKENDS; i++)
|
||||
{
|
||||
if (backends & internal::backend_list[i])
|
||||
if (Get().backends & internal::backend_list[i])
|
||||
{
|
||||
if (add_comma) { out << ','; }
|
||||
add_comma = true;
|
||||
@@ -111,29 +94,6 @@ void Device::Print(std::ostream &out)
|
||||
out << '\n';
|
||||
}
|
||||
|
||||
void Device::UpdateMemoryTypeAndClass()
|
||||
{
|
||||
if (Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
mem_type = MemoryType::CUDA;
|
||||
mem_class = MemoryClass::CUDA;
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_type = MemoryType::HOST;
|
||||
mem_class = MemoryClass::HOST;
|
||||
}
|
||||
}
|
||||
|
||||
void Device::Enable()
|
||||
{
|
||||
if (Get().backends & ~Backend::CPU)
|
||||
{
|
||||
Get().mode = Device::ACCELERATED;
|
||||
Get().UpdateMemoryTypeAndClass();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
static void DeviceSetup(const int dev, int &ngpu)
|
||||
{
|
||||
|
||||
+53
-160
@@ -13,7 +13,6 @@
|
||||
#define MFEM_DEVICE_HPP
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "mem_manager.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -60,31 +59,25 @@ struct Backend
|
||||
{
|
||||
/// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
|
||||
NUM_BACKENDS = 9,
|
||||
|
||||
/// Biwise-OR of all CPU backends
|
||||
CPU_MASK = CPU | RAJA_CPU | OCCA_CPU,
|
||||
/// Biwise-OR of all CUDA backends
|
||||
CUDA_MASK = CUDA | RAJA_CUDA | 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,
|
||||
|
||||
/// 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
|
||||
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. */
|
||||
/** @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:
|
||||
- At most one object of this class can be constructed and that object is
|
||||
controlled by its static methods.
|
||||
- If no Device object is constructed, the static methods will use a default
|
||||
global object which is never configured and always uses Backend::CPU.
|
||||
- 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
|
||||
@@ -92,78 +85,37 @@ struct Backend
|
||||
- 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. */
|
||||
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};
|
||||
|
||||
static Device device_singleton;
|
||||
|
||||
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.
|
||||
/// Set to true during configuration, except in 'device_singleton'.
|
||||
bool destroy_mm;
|
||||
|
||||
MemoryType mem_type; ///< Current Device MemoryType
|
||||
MemoryClass mem_class; ///< Current Device MemoryClass
|
||||
/** 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() { return device_singleton; }
|
||||
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; }
|
||||
|
||||
void UpdateMemoryTypeAndClass();
|
||||
|
||||
/// 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.
|
||||
|
||||
If the device is actually enabled, this method will also update the
|
||||
current MemoryType and MemoryClass. */
|
||||
static void Enable();
|
||||
|
||||
public:
|
||||
/** @brief Default constructor. Unless Configure() is called later, the
|
||||
default Backend::CPU will be used. */
|
||||
/** @note At most one Device object can be constructed during the lifetime of
|
||||
a program.
|
||||
@note This object should be destroyed after all other MFEM objects that
|
||||
use the Device are destroyed. */
|
||||
Device()
|
||||
: mode(Device::SEQUENTIAL),
|
||||
backends(Backend::CPU),
|
||||
destroy_mm(false),
|
||||
mem_type(MemoryType::HOST),
|
||||
mem_class(MemoryClass::HOST)
|
||||
{ }
|
||||
|
||||
/** @brief Construct a Device and configure it based on the @a device string.
|
||||
See Configure() for more details. */
|
||||
/** @note At most one Device object can be constructed during the lifetime of
|
||||
a program.
|
||||
@note This object should be destroyed after all other MFEM objects that
|
||||
use the Device are destroyed. */
|
||||
Device(const std::string &device, const int dev = 0)
|
||||
: mode(Device::SEQUENTIAL),
|
||||
backends(Backend::CPU),
|
||||
destroy_mm(false),
|
||||
mem_type(MemoryType::HOST),
|
||||
mem_class(MemoryClass::HOST)
|
||||
{ Configure(device, dev); }
|
||||
|
||||
/// Destructor.
|
||||
~Device();
|
||||
|
||||
/// 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
|
||||
@@ -179,11 +131,12 @@ public:
|
||||
* 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. */
|
||||
void Configure(const std::string &device, const int dev = 0);
|
||||
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.
|
||||
void Print(std::ostream &out = mfem::out);
|
||||
static void Print(std::ostream &out = mfem::out);
|
||||
|
||||
/// Return true if Configure() has been called previously.
|
||||
static inline bool IsConfigured() { return Get().ngpu >= 0; }
|
||||
@@ -191,105 +144,45 @@ public:
|
||||
/// Return true if an actual device (e.g. GPU) has been configured.
|
||||
static inline bool IsAvailable() { return Get().ngpu > 0; }
|
||||
|
||||
/// Return true if any backend other than Backend::CPU is enabled.
|
||||
/// 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. */
|
||||
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().backends & b_mask; }
|
||||
|
||||
/** @brief Get the current Device MemoryType. This is the MemoryType used by
|
||||
most MFEM classes when allocating memory to be used with device kernels.
|
||||
*/
|
||||
static inline MemoryType GetMemoryType() { return Get().mem_type; }
|
||||
|
||||
/** @brief Get the current Device MemoryClass. This is the MemoryClass used
|
||||
by most MFEM device kernels to access Memory objects. */
|
||||
static inline MemoryClass GetMemoryClass() { return Get().mem_class; }
|
||||
{ return Get().allowed_backends & b_mask; }
|
||||
};
|
||||
|
||||
|
||||
// Inline Memory access functions using the mfem::Device MemoryClass or
|
||||
// MemoryClass::HOST.
|
||||
|
||||
/** @brief Get a pointer for read access to @a mem with the mfem::Device
|
||||
MemoryClass, if @a on_dev = true, or MemoryClass::HOST, otherwise. */
|
||||
/** Also, if @a on_dev = true, the device flag of @a mem will be set. */
|
||||
template <typename T>
|
||||
inline const T *Read(const Memory<T> &mem, int size, bool on_dev = true)
|
||||
{
|
||||
if (!on_dev)
|
||||
{
|
||||
return mem.Read(MemoryClass::HOST, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
mem.UseDevice(true);
|
||||
return mem.Read(Device::GetMemoryClass(), size);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Shortcut to Read(const Memory<T> &mem, int size, false) */
|
||||
template <typename T>
|
||||
inline const T *HostRead(const Memory<T> &mem, int size)
|
||||
{
|
||||
return mfem::Read(mem, size, false);
|
||||
}
|
||||
|
||||
/** @brief Get a pointer for write access to @a mem with the mfem::Device
|
||||
MemoryClass, if @a on_dev = true, or MemoryClass::HOST, otherwise. */
|
||||
/** Also, if @a on_dev = true, the device flag of @a mem will be set. */
|
||||
template <typename T>
|
||||
inline T *Write(Memory<T> &mem, int size, bool on_dev = true)
|
||||
{
|
||||
if (!on_dev)
|
||||
{
|
||||
return mem.Write(MemoryClass::HOST, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
mem.UseDevice(true);
|
||||
return mem.Write(Device::GetMemoryClass(), size);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Shortcut to Write(const Memory<T> &mem, int size, false) */
|
||||
template <typename T>
|
||||
inline const T *HostWrite(const Memory<T> &mem, int size)
|
||||
{
|
||||
return mfem::Write(mem, size, false);
|
||||
}
|
||||
|
||||
/** @brief Get a pointer for read+write access to @a mem with the mfem::Device
|
||||
MemoryClass, if @a on_dev = true, or MemoryClass::HOST, otherwise. */
|
||||
/** Also, if @a on_dev = true, the device flag of @a mem will be set. */
|
||||
template <typename T>
|
||||
inline T *ReadWrite(Memory<T> &mem, int size, bool on_dev = true)
|
||||
{
|
||||
if (!on_dev)
|
||||
{
|
||||
return mem.ReadWrite(MemoryClass::HOST, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
mem.UseDevice(true);
|
||||
return mem.ReadWrite(Device::GetMemoryClass(), size);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Shortcut to ReadWrite(const Memory<T> &mem, int size, false) */
|
||||
template <typename T>
|
||||
inline const T *HostReadWrite(const Memory<T> &mem, int size)
|
||||
{
|
||||
return mfem::ReadWrite(mem, size, false);
|
||||
}
|
||||
|
||||
} // mfem
|
||||
|
||||
#endif // MFEM_DEVICE_HPP
|
||||
|
||||
+21
-111
@@ -30,40 +30,14 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// Maximum size of dofs and quads in 1D.
|
||||
const int MAX_D1D = 16;
|
||||
const int MAX_Q1D = 16;
|
||||
|
||||
// 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<1>(true,N, \
|
||||
[=] MFEM_DEVICE (int i) {__VA_ARGS__}, \
|
||||
[&] (int i) {__VA_ARGS__})
|
||||
|
||||
// MFEM_FORALL with a 2D CUDA block
|
||||
#define MFEM_FORALL_2D(i,N,X,Y,BZ,...) \
|
||||
ForallWrap<2>(true,N, \
|
||||
[=] MFEM_DEVICE (int i) {__VA_ARGS__}, \
|
||||
[&] (int i) {__VA_ARGS__}, \
|
||||
X,Y,BZ)
|
||||
|
||||
// MFEM_FORALL with a 3D CUDA block
|
||||
#define MFEM_FORALL_3D(i,N,X,Y,Z,...) \
|
||||
ForallWrap<3>(true,N, \
|
||||
[=] MFEM_DEVICE (int i) {__VA_ARGS__}, \
|
||||
[&] (int i) {__VA_ARGS__}, \
|
||||
X,Y,Z)
|
||||
|
||||
// MFEM_FORALL that uses the basic CPU backend when use_dev is false. See for
|
||||
// example the functions in vector.cpp, where we don't want to use the mfem
|
||||
// device for operations on small vectors.
|
||||
#define MFEM_FORALL_SWITCH(use_dev,i,N,...) \
|
||||
ForallWrap<1>(use_dev,N, \
|
||||
[=] MFEM_DEVICE (int i) {__VA_ARGS__}, \
|
||||
[&] (int i) {__VA_ARGS__})
|
||||
#define MFEM_FORALL(i,N,...) \
|
||||
ForallWrap(N, \
|
||||
[=] MFEM_ATTR_DEVICE (int i) {__VA_ARGS__}, \
|
||||
[&] (int i) {__VA_ARGS__})
|
||||
|
||||
|
||||
/// OpenMP backend
|
||||
@@ -122,110 +96,46 @@ void RajaSeqWrap(const int N, HBODY &&h_body)
|
||||
#ifdef MFEM_USE_CUDA
|
||||
|
||||
template <typename BODY> __global__ static
|
||||
void CuKernel1D(const int N, BODY body)
|
||||
void CuKernel(const int N, BODY body)
|
||||
{
|
||||
const int k = blockDim.x*blockIdx.x + threadIdx.x;
|
||||
if (k >= N) { return; }
|
||||
body(k);
|
||||
}
|
||||
|
||||
template <typename BODY> __global__ static
|
||||
void CuKernel2D(const int N, BODY body, const int BZ)
|
||||
{
|
||||
const int k = blockIdx.x*BZ + threadIdx.z;
|
||||
if (k >= N) { return; }
|
||||
body(k);
|
||||
}
|
||||
|
||||
template <typename BODY> __global__ static
|
||||
void CuKernel3D(const int N, BODY body)
|
||||
{
|
||||
const int k = blockIdx.x;
|
||||
if (k >= N) { return; }
|
||||
body(k);
|
||||
}
|
||||
|
||||
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
void CuWrap1D(const int N, DBODY &&d_body)
|
||||
template <int BLOCKS, typename DBODY>
|
||||
void CuWrap(const int N, DBODY &&d_body)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
const int GRID = (N+BLCK-1)/BLCK;
|
||||
CuKernel1D<<<GRID,BLCK>>>(N, d_body);
|
||||
const int GRID = (N+BLOCKS-1)/BLOCKS;
|
||||
CuKernel<<<GRID,BLOCKS>>>(N,d_body);
|
||||
MFEM_CUDA_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void CuWrap2D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
const int GRID = (N+BZ-1)/BZ;
|
||||
const dim3 BLCK(X,Y,BZ);
|
||||
CuKernel2D<<<GRID,BLCK>>>(N,d_body,BZ);
|
||||
MFEM_CUDA_CHECK(cudaGetLastError());
|
||||
}
|
||||
#else // MFEM_USE_CUDA
|
||||
|
||||
template <typename DBODY>
|
||||
void CuWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z)
|
||||
{
|
||||
if (N==0) { return; }
|
||||
const int GRID = N;
|
||||
const dim3 BLCK(X,Y,Z);
|
||||
CuKernel3D<<<GRID,BLCK>>>(N,d_body);
|
||||
MFEM_CUDA_CHECK(cudaGetLastError());
|
||||
}
|
||||
template <int BLOCKS, typename DBODY>
|
||||
void CuWrap(const int N, DBODY &&d_body) {}
|
||||
|
||||
#endif // MFEM_USE_CUDA
|
||||
#endif
|
||||
|
||||
|
||||
/// The forall kernel body wrapper
|
||||
template <const int DIM, typename DBODY, typename HBODY>
|
||||
inline void ForallWrap(const bool use_dev, const int N,
|
||||
DBODY &&d_body, HBODY &&h_body,
|
||||
const int X=0, const int Y=0, const int Z=0)
|
||||
template <typename DBODY, typename HBODY>
|
||||
void ForallWrap(const int N, DBODY &&d_body, HBODY &&h_body)
|
||||
{
|
||||
if (!use_dev) { goto backend_cpu; }
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
|
||||
// Handle all allowed CUDA backends except Backend::CUDA
|
||||
if (Device::Allows(Backend::CUDA_MASK & ~Backend::CUDA))
|
||||
if (Device::Allows(Backend::RAJA_CUDA))
|
||||
{ return RajaCudaWrap<MFEM_CUDA_BLOCKS>(N, d_body); }
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_CUDA
|
||||
// Handle all allowed CUDA backends
|
||||
if (DIM == 1 && Device::Allows(Backend::CUDA_MASK))
|
||||
{ return CuWrap1D(N, d_body); }
|
||||
if (Device::Allows(Backend::CUDA))
|
||||
{ return CuWrap<MFEM_CUDA_BLOCKS>(N, d_body); }
|
||||
|
||||
if (DIM == 2 && Device::Allows(Backend::CUDA_MASK))
|
||||
{ return CuWrap2D(N, d_body, X, Y, Z); }
|
||||
if (Device::Allows(Backend::RAJA_OMP)) { return RajaOmpWrap(N, h_body); }
|
||||
|
||||
if (DIM == 3 && Device::Allows(Backend::CUDA_MASK))
|
||||
{ return CuWrap3D(N, d_body, X, Y, Z); }
|
||||
#endif
|
||||
if (Device::Allows(Backend::OMP)) { return OmpWrap(N, h_body); }
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
|
||||
// Handle all allowed OpenMP backends except Backend::OMP
|
||||
if (Device::Allows(Backend::OMP_MASK & ~Backend::OMP))
|
||||
{ return RajaOmpWrap(N, h_body); }
|
||||
#endif
|
||||
if (Device::Allows(Backend::RAJA_CPU)) { return RajaSeqWrap(N, h_body); }
|
||||
|
||||
#ifdef MFEM_USE_OPENMP
|
||||
// Handle all allowed OpenMP backends
|
||||
if (Device::Allows(Backend::OMP_MASK)) { return OmpWrap(N, h_body); }
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_RAJA
|
||||
// Handle all allowed CPU backends except Backend::CPU
|
||||
if (Device::Allows(Backend::CPU_MASK & ~Backend::CPU))
|
||||
{ return RajaSeqWrap(N, h_body); }
|
||||
#endif
|
||||
|
||||
backend_cpu:
|
||||
// Handle Backend::CPU. This is also a fallback for any allowed backends not
|
||||
// handled above, e.g. OCCA_CPU with configuration 'occa-cpu,cpu', or
|
||||
// OCCA_OMP with configuration 'occa-omp,cpu'.
|
||||
for (int k = 0; k < N; k++) { h_body(k); }
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,6 @@ std::string MakeParFilename(const std::string &prefix, const int myid,
|
||||
return fname.str();
|
||||
}
|
||||
|
||||
#ifdef MFEM_COUNT_FLOPS
|
||||
namespace internal
|
||||
{
|
||||
long long flop_count;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
|
||||
+207
-556
@@ -15,42 +15,10 @@
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <algorithm> // std::max
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
MemoryType GetMemoryType(MemoryClass mc)
|
||||
{
|
||||
switch (mc)
|
||||
{
|
||||
case MemoryClass::HOST: return MemoryType::HOST;
|
||||
case MemoryClass::HOST_32: return MemoryType::HOST_32;
|
||||
case MemoryClass::HOST_64: return MemoryType::HOST_64;
|
||||
case MemoryClass::CUDA: return MemoryType::CUDA;
|
||||
case MemoryClass::CUDA_UVM: return MemoryType::CUDA_UVM;
|
||||
}
|
||||
return MemoryType::HOST;
|
||||
}
|
||||
|
||||
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
|
||||
{
|
||||
// | HOST HOST_32 HOST_64 CUDA CUDA_UVM
|
||||
// ---------+--------------------------------------------------
|
||||
// HOST | HOST HOST_32 HOST_64 CUDA CUDA_UVM
|
||||
// HOST_32 | HOST_32 HOST_32 HOST_64 CUDA CUDA_UVM
|
||||
// HOST_64 | HOST_64 HOST_64 HOST_64 CUDA CUDA_UVM
|
||||
// CUDA | CUDA CUDA CUDA CUDA CUDA_UVM
|
||||
// CUDA_UVM | CUDA_UVM CUDA_UVM CUDA_UVM CUDA_UVM CUDA_UVM
|
||||
|
||||
// Using the enumeration ordering:
|
||||
// HOST < HOST_32 < HOST_64 < CUDA < CUDA_UVM,
|
||||
// the above table is simply: a*b = max(a,b).
|
||||
|
||||
return std::max(mc1, mc2);
|
||||
}
|
||||
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
@@ -60,15 +28,17 @@ 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.
|
||||
/// - 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) {}
|
||||
host(true), bytes(size), h_ptr(h), d_ptr(nullptr), aliases() {}
|
||||
};
|
||||
|
||||
/// Alias class that holds the base memory region and the offset
|
||||
@@ -76,13 +46,10 @@ struct Alias
|
||||
{
|
||||
Memory *const mem;
|
||||
const long offset;
|
||||
unsigned long counter;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<const void*, Memory> MemoryMap;
|
||||
// TODO: use 'Alias' or 'const Alias' as the mapped type in the AliasMap instead
|
||||
// of 'Alias*'
|
||||
typedef std::unordered_map<const void*, Alias*> AliasMap;
|
||||
typedef std::unordered_map<const void*, const Alias*> AliasMap;
|
||||
|
||||
struct Ledger
|
||||
{
|
||||
@@ -97,213 +64,269 @@ static internal::Ledger *maps;
|
||||
MemoryManager::MemoryManager()
|
||||
{
|
||||
exists = true;
|
||||
enabled = true;
|
||||
maps = new internal::Ledger();
|
||||
}
|
||||
|
||||
MemoryManager::~MemoryManager()
|
||||
{
|
||||
if (exists) { Destroy(); }
|
||||
}
|
||||
|
||||
void MemoryManager::Destroy()
|
||||
{
|
||||
MFEM_VERIFY(exists, "MemoryManager has been destroyed already!");
|
||||
for (auto& n : maps->memories)
|
||||
{
|
||||
internal::Memory &mem = n.second;
|
||||
if (mem.d_ptr) { CuMemFree(mem.d_ptr); }
|
||||
}
|
||||
for (auto& n : maps->aliases)
|
||||
{
|
||||
delete n.second;
|
||||
}
|
||||
delete maps;
|
||||
exists = false;
|
||||
}
|
||||
|
||||
void* MemoryManager::Insert(void *ptr, const std::size_t bytes)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
MFEM_VERIFY(bytes == 0, "Trying to add NULL with size " << bytes);
|
||||
return NULL;
|
||||
}
|
||||
auto res = maps->memories.emplace(ptr, internal::Memory(ptr, bytes));
|
||||
if (res.second == false)
|
||||
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::InsertDevice(void *ptr, void *h_ptr, size_t bytes)
|
||||
{
|
||||
MFEM_VERIFY(ptr != NULL, "cannot register NULL device pointer");
|
||||
MFEM_VERIFY(h_ptr != NULL, "internal error");
|
||||
auto res = maps->memories.emplace(h_ptr, internal::Memory(h_ptr, bytes));
|
||||
if (res.second == false)
|
||||
{
|
||||
mfem_error("Trying to add an already present address!");
|
||||
}
|
||||
res.first->second.d_ptr = ptr;
|
||||
}
|
||||
|
||||
void *MemoryManager::Erase(void *ptr, bool free_dev_ptr)
|
||||
void *MemoryManager::Erase(void *ptr)
|
||||
{
|
||||
if (!UsingMM()) { return ptr; }
|
||||
if (!ptr) { return ptr; }
|
||||
auto mem_map_iter = maps->memories.find(ptr);
|
||||
if (mem_map_iter == maps->memories.end())
|
||||
const bool known = IsKnown(ptr);
|
||||
if (!known)
|
||||
{
|
||||
mfem_error("Trying to erase an unknown pointer!");
|
||||
}
|
||||
internal::Memory &mem = mem_map_iter->second;
|
||||
if (mem.d_ptr && free_dev_ptr) { CuMemFree(mem.d_ptr); }
|
||||
maps->memories.erase(mem_map_iter);
|
||||
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();
|
||||
}
|
||||
|
||||
void *MemoryManager::GetDevicePtr(const void *ptr, size_t bytes, bool copy_data)
|
||||
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)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
|
||||
return NULL;
|
||||
}
|
||||
internal::Memory &base = maps->memories.at(ptr);
|
||||
const size_t bytes = base.bytes;
|
||||
if (!base.d_ptr)
|
||||
{
|
||||
CuMemAlloc(&base.d_ptr, base.bytes);
|
||||
}
|
||||
if (copy_data)
|
||||
{
|
||||
MFEM_ASSERT(bytes <= base.bytes, "invalid copy size");
|
||||
CuMemAlloc(&base.d_ptr, bytes);
|
||||
CuMemcpyHtoD(base.d_ptr, ptr, bytes);
|
||||
base.host = false;
|
||||
}
|
||||
return base.d_ptr;
|
||||
}
|
||||
|
||||
void MemoryManager::InsertAlias(const void *base_ptr, void *alias_ptr,
|
||||
bool base_is_alias)
|
||||
// Looks if ptr is an alias of one memory
|
||||
static const void* AliasBaseMemory(const internal::Ledger *maps,
|
||||
const void *ptr)
|
||||
{
|
||||
long offset = static_cast<const char*>(alias_ptr) -
|
||||
static_cast<const char*>(base_ptr);
|
||||
if (!base_ptr)
|
||||
for (internal::MemoryMap::const_iterator mem = maps->memories.begin();
|
||||
mem != maps->memories.end(); mem++)
|
||||
{
|
||||
MFEM_VERIFY(offset == 0,
|
||||
"Trying to add alias to NULL at offset " << offset);
|
||||
return;
|
||||
}
|
||||
if (base_is_alias)
|
||||
{
|
||||
const internal::Alias *alias = maps->aliases.at(base_ptr);
|
||||
base_ptr = alias->mem->h_ptr;
|
||||
offset += alias->offset;
|
||||
}
|
||||
internal::Memory &mem = maps->memories.at(base_ptr);
|
||||
auto res = maps->aliases.emplace(alias_ptr, nullptr);
|
||||
if (res.second == false) // alias_ptr was already in the map
|
||||
{
|
||||
if (res.first->second->mem != &mem || res.first->second->offset != offset)
|
||||
{
|
||||
mfem_error("alias already exists with different base/offset!");
|
||||
}
|
||||
else
|
||||
{
|
||||
res.first->second->counter++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res.first->second = new internal::Alias{&mem, offset, 1};
|
||||
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;
|
||||
}
|
||||
|
||||
void MemoryManager::EraseAlias(void *alias_ptr)
|
||||
bool MemoryManager::IsAlias(const void *ptr)
|
||||
{
|
||||
if (!alias_ptr) { return; }
|
||||
auto alias_map_iter = maps->aliases.find(alias_ptr);
|
||||
if (alias_map_iter == maps->aliases.end())
|
||||
{
|
||||
mfem_error("alias not found");
|
||||
}
|
||||
internal::Alias *alias = alias_map_iter->second;
|
||||
if (--alias->counter) { return; }
|
||||
// erase the alias from the alias map:
|
||||
maps->aliases.erase(alias_map_iter);
|
||||
delete alias;
|
||||
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;
|
||||
}
|
||||
|
||||
void *MemoryManager::GetAliasDevicePtr(const void *alias_ptr, size_t bytes,
|
||||
bool copy_data)
|
||||
static inline bool MmDeviceIniFilter(void)
|
||||
{
|
||||
if (!alias_ptr)
|
||||
{
|
||||
MFEM_VERIFY(bytes == 0, "Trying to access NULL with size " << bytes);
|
||||
return NULL;
|
||||
}
|
||||
auto &alias_map = maps->aliases;
|
||||
auto alias_map_iter = alias_map.find(alias_ptr);
|
||||
if (alias_map_iter == alias_map.end())
|
||||
{
|
||||
mfem_error("alias not found");
|
||||
}
|
||||
const internal::Alias *alias = alias_map_iter->second;
|
||||
internal::Memory &base = *alias->mem;
|
||||
MFEM_ASSERT((char*)base.h_ptr + alias->offset == alias_ptr,
|
||||
"internal error");
|
||||
if (!base.d_ptr)
|
||||
{
|
||||
CuMemAlloc(&base.d_ptr, base.bytes);
|
||||
}
|
||||
if (copy_data)
|
||||
{
|
||||
CuMemcpyHtoD((char*)base.d_ptr + alias->offset, alias_ptr, bytes);
|
||||
base.host = false;
|
||||
}
|
||||
return (char*)base.d_ptr + alias->offset;
|
||||
if (!mm.UsingMM()) { return true; }
|
||||
if (!mm.IsEnabled()) { return true; }
|
||||
if (!Device::IsAvailable()) { return true; }
|
||||
if (!Device::IsConfigured()) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
static void PullKnown(internal::Ledger *maps,
|
||||
const void *ptr, const std::size_t bytes, bool copy_data)
|
||||
// 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);
|
||||
MFEM_ASSERT(base.h_ptr == ptr, "internal error");
|
||||
// There are cases where it is OK if base.d_ptr is not allocated yet:
|
||||
// for example, when requesting read-write access on host to memory created
|
||||
// as device memory.
|
||||
if (copy_data && base.d_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(base.h_ptr, base.d_ptr, bytes);
|
||||
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, bool copy_data)
|
||||
const void *ptr, const std::size_t bytes)
|
||||
{
|
||||
const internal::Alias *alias = maps->aliases.at(ptr);
|
||||
MFEM_ASSERT((char*)alias->mem->h_ptr + alias->offset == ptr,
|
||||
"internal error");
|
||||
// There are cases where it is OK if alias->mem->d_ptr is not allocated yet:
|
||||
// for example, when requesting read-write access on host to memory created
|
||||
// as device memory.
|
||||
if (copy_data && alias->mem->d_ptr)
|
||||
{
|
||||
CuMemcpyDtoH(const_cast<void*>(ptr),
|
||||
static_cast<char*>(alias->mem->d_ptr) + alias->offset,
|
||||
bytes);
|
||||
}
|
||||
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!"); }
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void MemoryManager::RegisterCheck(void *ptr)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
if (ptr != NULL && UsingMM())
|
||||
{
|
||||
if (!IsKnown(ptr))
|
||||
{
|
||||
@@ -323,389 +346,17 @@ void MemoryManager::PrintPtrs(void)
|
||||
<< "h_ptr " << mem.h_ptr << ", "
|
||||
<< "d_ptr " << mem.d_ptr;
|
||||
}
|
||||
mfem::out << std::endl;
|
||||
}
|
||||
|
||||
// Static private MemoryManager methods used by class Memory
|
||||
|
||||
void *MemoryManager::New_(void *h_ptr, std::size_t size, MemoryType mt,
|
||||
unsigned &flags)
|
||||
void MemoryManager::GetAll(void)
|
||||
{
|
||||
// TODO: save the types of the pointers ...
|
||||
flags = Mem::REGISTERED | Mem::OWNS_INTERNAL;
|
||||
switch (mt)
|
||||
for (const auto& n : maps->memories)
|
||||
{
|
||||
case MemoryType::HOST: return nullptr; // case is handled outside
|
||||
|
||||
case MemoryType::HOST_32:
|
||||
case MemoryType::HOST_64:
|
||||
mfem_error("New_(): aligned host types are not implemented yet");
|
||||
return nullptr;
|
||||
|
||||
case MemoryType::CUDA:
|
||||
mm.Insert(h_ptr, size);
|
||||
flags = flags | Mem::OWNS_HOST | Mem::OWNS_DEVICE | Mem::VALID_DEVICE;
|
||||
return h_ptr;
|
||||
|
||||
case MemoryType::CUDA_UVM:
|
||||
mfem_error("New_(): CUDA UVM allocation is not implemented yet");
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *MemoryManager::Register_(void *ptr, void *h_ptr, std::size_t capacity,
|
||||
MemoryType mt, bool own, bool alias,
|
||||
unsigned &flags)
|
||||
{
|
||||
// TODO: save the type of the registered pointer ...
|
||||
MFEM_VERIFY(alias == false, "cannot register an alias!");
|
||||
flags = flags | (Mem::REGISTERED | Mem::OWNS_INTERNAL);
|
||||
if (IsHostMemory(mt))
|
||||
{
|
||||
mm.Insert(ptr, capacity);
|
||||
flags = (own ? flags | Mem::OWNS_HOST : flags & ~Mem::OWNS_HOST) |
|
||||
Mem::OWNS_DEVICE | Mem::VALID_HOST;
|
||||
return ptr;
|
||||
}
|
||||
MFEM_VERIFY(mt == MemoryType::CUDA, "Only CUDA pointers are supported");
|
||||
mm.InsertDevice(ptr, h_ptr, capacity);
|
||||
flags = (own ? flags | Mem::OWNS_DEVICE : flags & ~Mem::OWNS_DEVICE) |
|
||||
Mem::OWNS_HOST | Mem::VALID_DEVICE;
|
||||
return h_ptr;
|
||||
}
|
||||
|
||||
void MemoryManager::Alias_(void *base_h_ptr, std::size_t offset,
|
||||
std::size_t size, unsigned base_flags,
|
||||
unsigned &flags)
|
||||
{
|
||||
// TODO: store the 'size' in the MemoryManager?
|
||||
mm.InsertAlias(base_h_ptr, (char*)base_h_ptr + offset,
|
||||
base_flags & Mem::ALIAS);
|
||||
flags = (base_flags | Mem::ALIAS | Mem::OWNS_INTERNAL) &
|
||||
~(Mem::OWNS_HOST | Mem::OWNS_DEVICE);
|
||||
}
|
||||
|
||||
MemoryType MemoryManager::Delete_(void *h_ptr, unsigned flags)
|
||||
{
|
||||
// TODO: this logic needs to be updated when support for HOST_32 and HOST_64
|
||||
// memory types is added.
|
||||
|
||||
MFEM_ASSERT(!(flags & Mem::OWNS_DEVICE) || (flags & Mem::OWNS_INTERNAL),
|
||||
"invalid Memory state");
|
||||
if (mm.exists && (flags & Mem::OWNS_INTERNAL))
|
||||
{
|
||||
if (flags & Mem::ALIAS)
|
||||
{
|
||||
mm.EraseAlias(h_ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
mm.Erase(h_ptr, flags & Mem::OWNS_DEVICE);
|
||||
}
|
||||
}
|
||||
return MemoryType::HOST;
|
||||
}
|
||||
|
||||
void *MemoryManager::ReadWrite_(void *h_ptr, MemoryClass mc,
|
||||
std::size_t size, unsigned &flags)
|
||||
{
|
||||
switch (mc)
|
||||
{
|
||||
case MemoryClass::HOST:
|
||||
if (!(flags & Mem::VALID_HOST))
|
||||
{
|
||||
if (flags & Mem::ALIAS) { PullAlias(maps, h_ptr, size, true); }
|
||||
else { PullKnown(maps, h_ptr, size, true); }
|
||||
}
|
||||
flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::HOST_32:
|
||||
// TODO: check that the host pointer is MemoryType::HOST_32 or
|
||||
// MemoryType::HOST_64
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::HOST_64:
|
||||
// TODO: check that the host pointer is MemoryType::HOST_64
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::CUDA:
|
||||
{
|
||||
// TODO: check that the device pointer is MemoryType::CUDA or
|
||||
// MemoryType::CUDA_UVM
|
||||
|
||||
const bool need_copy = !(flags & Mem::VALID_DEVICE);
|
||||
flags = (flags | Mem::VALID_DEVICE) & ~Mem::VALID_HOST;
|
||||
|
||||
// TODO: add support for UVM
|
||||
if (flags & Mem::ALIAS)
|
||||
{
|
||||
return mm.GetAliasDevicePtr(h_ptr, size, need_copy);
|
||||
}
|
||||
return mm.GetDevicePtr(h_ptr, size, need_copy);
|
||||
}
|
||||
|
||||
case MemoryClass::CUDA_UVM:
|
||||
// TODO: check that the host+device pointers are MemoryType::CUDA_UVM
|
||||
|
||||
// Do we need to update the validity flags?
|
||||
|
||||
return h_ptr; // the host and device pointers are the same
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const void *MemoryManager::Read_(void *h_ptr, MemoryClass mc,
|
||||
std::size_t size, unsigned &flags)
|
||||
{
|
||||
switch (mc)
|
||||
{
|
||||
case MemoryClass::HOST:
|
||||
if (!(flags & Mem::VALID_HOST))
|
||||
{
|
||||
if (flags & Mem::ALIAS) { PullAlias(maps, h_ptr, size, true); }
|
||||
else { PullKnown(maps, h_ptr, size, true); }
|
||||
}
|
||||
flags = flags | Mem::VALID_HOST;
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::HOST_32:
|
||||
// TODO: check that the host pointer is MemoryType::HOST_32 or
|
||||
// MemoryType::HOST_64
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::HOST_64:
|
||||
// TODO: check that the host pointer is MemoryType::HOST_64
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::CUDA:
|
||||
{
|
||||
// TODO: check that the device pointer is MemoryType::CUDA or
|
||||
// MemoryType::CUDA_UVM
|
||||
|
||||
const bool need_copy = !(flags & Mem::VALID_DEVICE);
|
||||
flags = flags | Mem::VALID_DEVICE;
|
||||
|
||||
// TODO: add support for UVM
|
||||
if (flags & Mem::ALIAS)
|
||||
{
|
||||
return mm.GetAliasDevicePtr(h_ptr, size, need_copy);
|
||||
}
|
||||
return mm.GetDevicePtr(h_ptr, size, need_copy);
|
||||
}
|
||||
|
||||
case MemoryClass::CUDA_UVM:
|
||||
// TODO: check that the host+device pointers are MemoryType::CUDA_UVM
|
||||
|
||||
// Do we need to update the validity flags?
|
||||
|
||||
return h_ptr; // the host and device pointers are the same
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *MemoryManager::Write_(void *h_ptr, MemoryClass mc, std::size_t size,
|
||||
unsigned &flags)
|
||||
{
|
||||
switch (mc)
|
||||
{
|
||||
case MemoryClass::HOST:
|
||||
flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::HOST_32:
|
||||
// TODO: check that the host pointer is MemoryType::HOST_32 or
|
||||
// MemoryType::HOST_64
|
||||
|
||||
flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::HOST_64:
|
||||
// TODO: check that the host pointer is MemoryType::HOST_64
|
||||
|
||||
flags = (flags | Mem::VALID_HOST) & ~Mem::VALID_DEVICE;
|
||||
return h_ptr;
|
||||
|
||||
case MemoryClass::CUDA:
|
||||
// TODO: check that the device pointer is MemoryType::CUDA or
|
||||
// MemoryType::CUDA_UVM
|
||||
|
||||
flags = (flags | Mem::VALID_DEVICE) & ~Mem::VALID_HOST;
|
||||
|
||||
// TODO: add support for UVM
|
||||
if (flags & Mem::ALIAS)
|
||||
{
|
||||
return mm.GetAliasDevicePtr(h_ptr, size, false);
|
||||
}
|
||||
return mm.GetDevicePtr(h_ptr, size, false);
|
||||
|
||||
case MemoryClass::CUDA_UVM:
|
||||
// TODO: check that the host+device pointers are MemoryType::CUDA_UVM
|
||||
|
||||
// Do we need to update the validity flags?
|
||||
|
||||
return h_ptr; // the host and device pointers are the same
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MemoryManager::SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
|
||||
size_t alias_size, unsigned base_flags,
|
||||
unsigned &alias_flags)
|
||||
{
|
||||
// This is called only when (base_flags & Mem::REGISTERED) is true.
|
||||
// Note that (alias_flags & REGISTERED) may not be true.
|
||||
MFEM_ASSERT(alias_flags & Mem::ALIAS, "not an alias");
|
||||
if ((base_flags & Mem::VALID_HOST) && !(alias_flags & Mem::VALID_HOST))
|
||||
{
|
||||
PullAlias(maps, alias_h_ptr, alias_size, true);
|
||||
}
|
||||
if ((base_flags & Mem::VALID_DEVICE) && !(alias_flags & Mem::VALID_DEVICE))
|
||||
{
|
||||
if (!(alias_flags & Mem::REGISTERED))
|
||||
{
|
||||
mm.InsertAlias(base_h_ptr, alias_h_ptr, base_flags & Mem::ALIAS);
|
||||
alias_flags = (alias_flags | Mem::REGISTERED | Mem::OWNS_INTERNAL) &
|
||||
~(Mem::OWNS_HOST | Mem::OWNS_DEVICE);
|
||||
}
|
||||
mm.GetAliasDevicePtr(alias_h_ptr, alias_size, true);
|
||||
}
|
||||
alias_flags = (alias_flags & ~(Mem::VALID_HOST | Mem::VALID_DEVICE)) |
|
||||
(base_flags & (Mem::VALID_HOST | Mem::VALID_DEVICE));
|
||||
}
|
||||
|
||||
MemoryType MemoryManager::GetMemoryType_(void *h_ptr, unsigned flags)
|
||||
{
|
||||
// TODO: support other memory types
|
||||
if (flags & Mem::VALID_DEVICE) { return MemoryType::CUDA; }
|
||||
return MemoryType::HOST;
|
||||
}
|
||||
|
||||
void MemoryManager::Copy_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
std::size_t size, unsigned src_flags,
|
||||
unsigned &dest_flags)
|
||||
{
|
||||
// Type of copy to use based on the src and dest validity flags:
|
||||
// | src
|
||||
// | h | d | hd
|
||||
// -----------+-----+-----+------
|
||||
// h | h2h d2h h2h
|
||||
// dest d | h2d d2d d2d
|
||||
// hd | h2h d2d d2d
|
||||
|
||||
const bool src_on_host =
|
||||
(src_flags & Mem::VALID_HOST) &&
|
||||
(!(src_flags & Mem::VALID_DEVICE) ||
|
||||
((dest_flags & Mem::VALID_HOST) && !(dest_flags & Mem::VALID_DEVICE)));
|
||||
const bool dest_on_host =
|
||||
(dest_flags & Mem::VALID_HOST) &&
|
||||
(!(dest_flags & Mem::VALID_DEVICE) ||
|
||||
((src_flags & Mem::VALID_HOST) && !(src_flags & Mem::VALID_DEVICE)));
|
||||
const void *src_d_ptr = src_on_host ? NULL :
|
||||
((src_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(src_h_ptr, size, false) :
|
||||
mm.GetDevicePtr(src_h_ptr, size, false));
|
||||
if (dest_on_host)
|
||||
{
|
||||
if (src_on_host)
|
||||
{
|
||||
if (dest_h_ptr != src_h_ptr && size != 0)
|
||||
{
|
||||
MFEM_ASSERT((char*)dest_h_ptr + size <= src_h_ptr ||
|
||||
(char*)src_h_ptr + size <= dest_h_ptr,
|
||||
"data overlaps!");
|
||||
std::memcpy(dest_h_ptr, src_h_ptr, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CuMemcpyDtoH(dest_h_ptr, src_d_ptr, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void *dest_d_ptr = (dest_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(dest_h_ptr, size, false) :
|
||||
mm.GetDevicePtr(dest_h_ptr, size, false);
|
||||
if (src_on_host)
|
||||
{
|
||||
CuMemcpyHtoD(dest_d_ptr, src_h_ptr, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
CuMemcpyDtoD(dest_d_ptr, src_d_ptr, size);
|
||||
}
|
||||
}
|
||||
dest_flags = dest_flags &
|
||||
~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
|
||||
}
|
||||
|
||||
void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
std::size_t size, unsigned src_flags)
|
||||
{
|
||||
const bool src_on_host = src_flags & Mem::VALID_HOST;
|
||||
if (src_on_host)
|
||||
{
|
||||
if (dest_h_ptr != src_h_ptr && size != 0)
|
||||
{
|
||||
MFEM_ASSERT((char*)dest_h_ptr + size <= src_h_ptr ||
|
||||
(char*)src_h_ptr + size <= dest_h_ptr,
|
||||
"data overlaps!");
|
||||
std::memcpy(dest_h_ptr, src_h_ptr, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const void *src_d_ptr = (src_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(src_h_ptr, size, false) :
|
||||
mm.GetDevicePtr(src_h_ptr, size, false);
|
||||
CuMemcpyDtoH(dest_h_ptr, src_d_ptr, size);
|
||||
const void *ptr = n.first;
|
||||
Ptr(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
std::size_t size, unsigned &dest_flags)
|
||||
{
|
||||
const bool dest_on_host = dest_flags & Mem::VALID_HOST;
|
||||
if (dest_on_host)
|
||||
{
|
||||
if (dest_h_ptr != src_h_ptr && size != 0)
|
||||
{
|
||||
MFEM_ASSERT((char*)dest_h_ptr + size <= src_h_ptr ||
|
||||
(char*)src_h_ptr + size <= dest_h_ptr,
|
||||
"data overlaps!");
|
||||
std::memcpy(dest_h_ptr, src_h_ptr, size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void *dest_d_ptr = (dest_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(dest_h_ptr, size, false) :
|
||||
mm.GetDevicePtr(dest_h_ptr, size, false);
|
||||
CuMemcpyHtoD(dest_d_ptr, src_h_ptr, size);
|
||||
}
|
||||
dest_flags = dest_flags &
|
||||
~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
|
||||
}
|
||||
|
||||
|
||||
void MemoryPrintFlags(unsigned flags)
|
||||
{
|
||||
typedef Memory<int> Mem;
|
||||
mfem::out
|
||||
<< " registered = " << bool(flags & Mem::REGISTERED)
|
||||
<< "\n owns host = " << bool(flags & Mem::OWNS_HOST)
|
||||
<< "\n owns device = " << bool(flags & Mem::OWNS_DEVICE)
|
||||
<< "\n owns internal = " << bool(flags & Mem::OWNS_INTERNAL)
|
||||
<< "\n valid host = " << bool(flags & Mem::VALID_HOST)
|
||||
<< "\n valid device = " << bool(flags & Mem::VALID_DEVICE)
|
||||
<< "\n alias = " << bool(flags & Mem::ALIAS)
|
||||
<< "\n device flag = " << bool(flags & Mem::USE_DEVICE)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
MemoryManager mm;
|
||||
bool MemoryManager::exists = false;
|
||||
|
||||
|
||||
+135
-688
@@ -13,9 +13,6 @@
|
||||
#define MFEM_MEM_MANAGER_HPP
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "error.hpp"
|
||||
#include <cstring> // std::memcpy
|
||||
#include <type_traits> // std::is_const
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -23,717 +20,167 @@ 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.
|
||||
|
||||
/// Memory types supported by MFEM.
|
||||
enum class MemoryType
|
||||
{
|
||||
HOST, ///< Host memory; using new[] and delete[]
|
||||
HOST_32, ///< Host memory aligned at 32 bytes (not supported yet)
|
||||
HOST_64, ///< Host memory aligned at 64 bytes (not supported yet)
|
||||
CUDA, ///< cudaMalloc, cudaFree
|
||||
CUDA_UVM ///< cudaMallocManaged, cudaFree (not supported yet)
|
||||
};
|
||||
|
||||
/// Memory classes identify subsets of memory types.
|
||||
/** This type is used by kernels that can work with multiple MemoryType%s. For
|
||||
example, kernels that can use CUDA or CUDA_UVM memory types should use
|
||||
MemoryClass::CUDA for their inputs. */
|
||||
enum class MemoryClass
|
||||
{
|
||||
HOST, ///< Memory types: { HOST, HOST_32, HOST_64, CUDA_UVM }
|
||||
HOST_32, ///< Memory types: { HOST_32, HOST_64 }
|
||||
HOST_64, ///< Memory types: { HOST_64 }
|
||||
CUDA, ///< Memory types: { CUDA, CUDA_UVM }
|
||||
CUDA_UVM ///< Memory types: { CUDA_UVM }
|
||||
};
|
||||
|
||||
/// Return true if the given memory type is in MemoryClass::HOST.
|
||||
inline bool IsHostMemory(MemoryType mt) { return mt <= MemoryType::HOST_64; }
|
||||
|
||||
/// Return a suitable MemoryType for a given MemoryClass.
|
||||
MemoryType GetMemoryType(MemoryClass mc);
|
||||
|
||||
/// Return a suitable MemoryClass from a pair of MemoryClass%es.
|
||||
/** Note: this operation is commutative, i.e. a*b = b*a, associative, i.e.
|
||||
(a*b)*c = a*(b*c), and has an identity element: MemoryClass::HOST.
|
||||
|
||||
Currently, the operation is defined as a*b := max(a,b) where the max
|
||||
operation is based on the enumeration ordering:
|
||||
|
||||
HOST < HOST_32 < HOST_64 < CUDA < CUDA_UVM. */
|
||||
MemoryClass operator*(MemoryClass mc1, MemoryClass mc2);
|
||||
|
||||
/// Class used by MFEM to store pointers to host and/or device memory.
|
||||
/** The template class parameter, T, must be a plain-old-data (POD) type.
|
||||
|
||||
In many respects this class behaves like a pointer:
|
||||
* When destroyed, a Memory object does NOT automatically delete any
|
||||
allocated memory.
|
||||
* Only the method Delete() will deallocate a Memory object.
|
||||
* Other methods that modify the object (e.g. New(), Wrap(), etc) will simply
|
||||
overwrite the old contents.
|
||||
* One difference with a pointer is that a const Memory object does not allow
|
||||
modification of the content (unlike e.g. a const pointer).
|
||||
|
||||
A Memory object stores up to two different pointers: one host pointer (with
|
||||
MemoryType from MemoryClass::HOST) and one device pointer (currently one of
|
||||
MemoryType::CUDA or MemoryTyep::CUDA_UVM).
|
||||
|
||||
A Memory object can hold (wrap) an externally allocated pointer with any
|
||||
given MemoryType.
|
||||
|
||||
Access to the content of the Memory object can be requested with any given
|
||||
MemoryClass through the methods ReadWrite(), Read(), and Write().
|
||||
Requesting such access may result in additional (internally handled)
|
||||
memory allocation and/or memory copy.
|
||||
* When ReadWrite() is called, the returned pointer becomes the only
|
||||
valid pointer.
|
||||
* When Read() is called, the returned pointer becomes valid, however
|
||||
the other pointer (host or device) may remain valid as well.
|
||||
* When Write() is called, the returned pointer becomes the only valid
|
||||
pointer, however, unlike ReadWrite(), no memory copy will be performed.
|
||||
|
||||
The host memory (pointer from MemoryClass::HOST) can be accessed through the
|
||||
inline methods: `operator[]()`, `operator*()`, the implicit conversion
|
||||
functions `operator T*()`, `operator const T*()`, and the explicit
|
||||
conversion template functions `operator U*()`, `operator const U*()` (with
|
||||
any suitable type U). In certain cases, using these methods may have
|
||||
undefined behavior, e.g. if the host pointer is not currently valid. */
|
||||
template <typename T>
|
||||
class Memory
|
||||
{
|
||||
protected:
|
||||
friend class MemoryManager;
|
||||
friend void MemoryPrintFlags(unsigned flags);
|
||||
|
||||
enum FlagMask
|
||||
{
|
||||
REGISTERED = 1, ///< #h_ptr is registered with the MemoryManager
|
||||
OWNS_HOST = 2, ///< The host pointer will be deleted by Delete()
|
||||
OWNS_DEVICE = 4, ///< The device pointer will be deleted by Delete()
|
||||
OWNS_INTERNAL = 8, ///< Ownership flag for internal Memory data
|
||||
VALID_HOST = 16, ///< Host pointer is valid
|
||||
VALID_DEVICE = 32, ///< Device pointer is valid
|
||||
ALIAS = 64,
|
||||
/// Internal device flag, see e.g. Vector::UseDevice()
|
||||
USE_DEVICE = 128
|
||||
};
|
||||
|
||||
/// Pointer to host memory. Not owned.
|
||||
/** When the pointer is not registered with the MemoryManager, this pointer
|
||||
has type MemoryType::HOST. When the pointer is registered, it can be any
|
||||
type from MemoryClass::HOST. */
|
||||
T *h_ptr;
|
||||
int capacity;
|
||||
mutable unsigned flags;
|
||||
// 'flags' is mutable so that it can be modified in Set{Host,Device}PtrOwner,
|
||||
// Copy{From,To}, {ReadWrite,Read,Write}.
|
||||
|
||||
public:
|
||||
/// Default constructor: no initialization.
|
||||
Memory() { }
|
||||
|
||||
/// Copy constructor: default.
|
||||
Memory(const Memory &orig) = default;
|
||||
|
||||
/// Move constructor: default.
|
||||
Memory(Memory &&orig) = default;
|
||||
|
||||
/// Copy-assignment operator: default.
|
||||
Memory &operator=(const Memory &orig) = default;
|
||||
|
||||
/// Move-assignment operator: default.
|
||||
Memory &operator=(Memory &&orig) = default;
|
||||
|
||||
/// Allocate host memory for @a size entries.
|
||||
explicit Memory(int size) { New(size); }
|
||||
|
||||
/** @brief Allocate memory for @a size entries with the given MemoryType
|
||||
@a mt. */
|
||||
/** The newly allocated memory is not initialized, however the given
|
||||
MemoryType is still set as valid. */
|
||||
Memory(int size, MemoryType mt) { New(size, mt); }
|
||||
|
||||
/** @brief Wrap an externally allocated host pointer, @a ptr with type
|
||||
MemoryType::HOST. */
|
||||
/** The parameter @a own determines whether @a ptr will be deleted (using
|
||||
operator delete[]) when the method Delete() is called. */
|
||||
explicit Memory(T *ptr, int size, bool own) { Wrap(ptr, size, own); }
|
||||
|
||||
/// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
|
||||
/** The new memory object will have the given MemoryType set as valid.
|
||||
|
||||
The given @a ptr must be allocated appropriately for the given
|
||||
MemoryType.
|
||||
|
||||
The parameter @a own determines whether @a ptr will be deleted when the
|
||||
method Delete() is called. */
|
||||
Memory(T *ptr, int size, MemoryType mt, bool own)
|
||||
{ Wrap(ptr, size, mt, own); }
|
||||
|
||||
/** @brief Alias constructor. Create a Memory object that points inside the
|
||||
Memory object @a base. */
|
||||
/** The new Memory object uses the same MemoryType(s) as @a base. */
|
||||
Memory(const Memory &base, int offset, int size)
|
||||
{ MakeAlias(base, offset, size); }
|
||||
|
||||
/// Destructor: default.
|
||||
/** @note The destructor will NOT delete the current memory. */
|
||||
~Memory() = default;
|
||||
|
||||
/** @brief Return true if the host pointer is owned. Ownership indicates
|
||||
whether the pointer will be deleted by the method Delete(). */
|
||||
bool OwnsHostPtr() const { return flags & OWNS_HOST; }
|
||||
|
||||
/** @brief Set/clear the ownership flag for the host pointer. Ownership
|
||||
indicates whether the pointer will be deleted by the method Delete(). */
|
||||
void SetHostPtrOwner(bool own) const
|
||||
{ flags = own ? (flags | OWNS_HOST) : (flags & ~OWNS_HOST); }
|
||||
|
||||
/** @brief Return true if the device pointer is owned. Ownership indicates
|
||||
whether the pointer will be deleted by the method Delete(). */
|
||||
bool OwnsDevicePtr() const { return flags & OWNS_DEVICE; }
|
||||
|
||||
/** @brief Set/clear the ownership flag for the device pointer. Ownership
|
||||
indicates whether the pointer will be deleted by the method Delete(). */
|
||||
void SetDevicePtrOwner(bool own) const
|
||||
{ flags = own ? (flags | OWNS_DEVICE) : (flags & ~OWNS_DEVICE); }
|
||||
|
||||
/** @brief Clear the ownership flags for the host and device pointers, as
|
||||
well as any internal data allocated by the Memory object. */
|
||||
void ClearOwnerFlags() const
|
||||
{ flags = flags & ~(OWNS_HOST | OWNS_DEVICE | OWNS_INTERNAL); }
|
||||
|
||||
/// Read the internal device flag.
|
||||
bool UseDevice() const { return flags & USE_DEVICE; }
|
||||
|
||||
/// Set the internal device flag.
|
||||
void UseDevice(bool use_dev) const
|
||||
{ flags = use_dev ? (flags | USE_DEVICE) : (flags & ~USE_DEVICE); }
|
||||
|
||||
/// Return the size of the allocated memory.
|
||||
int Capacity() const { return capacity; }
|
||||
|
||||
/// Reset the memory to be empty, ensuring that Delete() will be a no-op.
|
||||
/** This is the Memory class equivalent to setting a pointer to NULL, see
|
||||
Empty().
|
||||
|
||||
@note The current memory is NOT deleted by this method. */
|
||||
void Reset() { h_ptr = NULL; capacity = 0; flags = 0; }
|
||||
|
||||
/// Return true if the Memory object is empty, see Reset().
|
||||
/** Default-constructed objects are uninitialized, so they are not guaranteed
|
||||
to be empty. */
|
||||
bool Empty() const { return h_ptr == NULL; }
|
||||
|
||||
/// Allocate host memory for @a size entries with type MemoryType::HOST.
|
||||
/** @note The current memory is NOT deleted by this method. */
|
||||
void New(int size)
|
||||
{ h_ptr = new T[size]; capacity = size; flags = OWNS_HOST | VALID_HOST; }
|
||||
|
||||
/// Allocate memory for @a size entries with the given MemoryType.
|
||||
/** The newly allocated memory is not initialized, however the given
|
||||
MemoryType is still set as valid.
|
||||
|
||||
@note The current memory is NOT deleted by this method. */
|
||||
inline void New(int size, MemoryType mt);
|
||||
|
||||
/** @brief Wrap an externally allocated host pointer, @a ptr with type
|
||||
MemoryType::HOST. */
|
||||
/** The parameter @a own determines whether @a ptr will be deleted (using
|
||||
operator delete[]) when the method Delete() is called.
|
||||
|
||||
@note The current memory is NOT deleted by this method. */
|
||||
inline void Wrap(T *ptr, int size, bool own)
|
||||
{ h_ptr = ptr; capacity = size; flags = (own ? OWNS_HOST : 0) | VALID_HOST; }
|
||||
|
||||
/// Wrap an externally allocated pointer, @a ptr, of the given MemoryType.
|
||||
/** The new memory object will have the given MemoryType set as valid.
|
||||
|
||||
The given @a ptr must be allocated appropriately for the given
|
||||
MemoryType.
|
||||
|
||||
The parameter @a own determines whether @a ptr will be deleted when the
|
||||
method Delete() is called.
|
||||
|
||||
@note The current memory is NOT deleted by this method. */
|
||||
inline void Wrap(T *ptr, int size, MemoryType mt, bool own);
|
||||
|
||||
/// Create a memory object that points inside the memory object @a base.
|
||||
/** The new Memory object uses the same MemoryType(s) as @a base.
|
||||
|
||||
@note The current memory is NOT deleted by this method. */
|
||||
inline void MakeAlias(const Memory &base, int offset, int size);
|
||||
|
||||
/// Delete the owned pointers. The Memory is not reset by this method.
|
||||
inline void Delete();
|
||||
|
||||
/// Array subscript operator for host memory.
|
||||
inline T &operator[](int idx);
|
||||
|
||||
/// Array subscript operator for host memory, const version.
|
||||
inline const T &operator[](int idx) const;
|
||||
|
||||
/// Direct access to the host memory as T* (implicit conversion).
|
||||
/** When the type T is const-qualified, this method can be used only if the
|
||||
host pointer is currently valid (the device pointer may be valid or
|
||||
invalid).
|
||||
|
||||
When the type T is not const-qualified, this method can be used only if
|
||||
the host pointer is the only valid pointer.
|
||||
|
||||
When the Memory is empty, this method can be used and it returns NULL. */
|
||||
inline operator T*();
|
||||
|
||||
/// Direct access to the host memory as const T* (implicit conversion).
|
||||
/** This method can be used only if the host pointer is currently valid (the
|
||||
device pointer may be valid or invalid).
|
||||
|
||||
When the Memory is empty, this method can be used and it returns NULL. */
|
||||
inline operator const T*() const;
|
||||
|
||||
/// Direct access to the host memory via explicit typecast.
|
||||
/** A pointer to type T must be reinterpret_cast-able to a pointer to type U.
|
||||
In particular, this method cannot be used to cast away const-ness from
|
||||
the base type T.
|
||||
|
||||
When the type U is const-qualified, this method can be used only if the
|
||||
host pointer is currently valid (the device pointer may be valid or
|
||||
invalid).
|
||||
|
||||
When the type U is not const-qualified, this method can be used only if
|
||||
the host pointer is the only valid pointer.
|
||||
|
||||
When the Memory is empty, this method can be used and it returns NULL. */
|
||||
template <typename U>
|
||||
inline explicit operator U*();
|
||||
|
||||
/// Direct access to the host memory via explicit typecast, const version.
|
||||
/** A pointer to type T must be reinterpret_cast-able to a pointer to type
|
||||
const U.
|
||||
|
||||
This method can be used only if the host pointer is currently valid (the
|
||||
device pointer may be valid or invalid).
|
||||
|
||||
When the Memory is empty, this method can be used and it returns NULL. */
|
||||
template <typename U>
|
||||
inline explicit operator const U*() const;
|
||||
|
||||
/// Get read-write access to the memory with the given MemoryClass.
|
||||
/** If only read or only write access is needed, then the methods
|
||||
Read() or Write() should be used instead of this method.
|
||||
|
||||
The parameter @a size must not exceed the Capacity(). */
|
||||
inline T *ReadWrite(MemoryClass mc, int size);
|
||||
|
||||
/// Get read-only access to the memory with the given MemoryClass.
|
||||
/** The parameter @a size must not exceed the Capacity(). */
|
||||
inline const T *Read(MemoryClass mc, int size) const;
|
||||
|
||||
/// Get write-only access to the memory with the given MemoryClass.
|
||||
/** The parameter @a size must not exceed the Capacity().
|
||||
|
||||
The contents of the returned pointer is undefined, unless it was
|
||||
validated by a previous call to Read() or ReadWrite() with
|
||||
the same MemoryClass. */
|
||||
inline T *Write(MemoryClass mc, int size);
|
||||
|
||||
/// Copy the host/device pointer validity flags from @a other to @a *this.
|
||||
/** This method synchronizes the pointer validity flags of two Memory objects
|
||||
that use the same host/device pointers, or when @a *this is an alias
|
||||
(sub-Memory) of @a other. Typically, this method should be called after
|
||||
@a other is manipulated in a way that changes its pointer validity flags
|
||||
(e.g. it was moved from device to host memory). */
|
||||
inline void Sync(const Memory &other) const;
|
||||
|
||||
/** @brief Update the alias Memory @a *this to match the memory location (all
|
||||
valid locations) of its base Memory, @a base. */
|
||||
/** This method is useful when alias Memory is moved and manipulated in a
|
||||
different memory space. Such operations render the pointer validity flags
|
||||
of the base incorrect. Calling this method will ensure that @a base is
|
||||
up-to-date. Note that this is achieved by moving/copying @a *this (if
|
||||
necessary), and not @a base. */
|
||||
inline void SyncAlias(const Memory &base, int alias_size) const;
|
||||
|
||||
/** @brief Return a MemoryType that is currently valid. If both the host and
|
||||
the device pointers are currently valid, then the device memory type is
|
||||
returned. */
|
||||
inline MemoryType GetMemoryType() const;
|
||||
|
||||
/// Copy @a size entries from @a src to @a *this.
|
||||
/** The given @a size should not exceed the Capacity() of the source @a src
|
||||
and the destination, @a *this. */
|
||||
inline void CopyFrom(const Memory &src, int size);
|
||||
|
||||
/// Copy @a size entries from the host pointer @a src to @a *this.
|
||||
/** The given @a size should not exceed the Capacity() of @a *this. */
|
||||
inline void CopyFromHost(const T *src, int size);
|
||||
|
||||
/// Copy @a size entries from @a *this to @a dest.
|
||||
/** The given @a size should not exceed the Capacity() of @a *this and the
|
||||
destination, @a dest. */
|
||||
inline void CopyTo(Memory &dest, int size) const
|
||||
{ dest.CopyFrom(*this, size); }
|
||||
|
||||
/// Copy @a size entries from @a *this to the host pointer @a dest.
|
||||
/** The given @a size should not exceed the Capacity() of @a *this. */
|
||||
inline void CopyToHost(T *dest, int size) const;
|
||||
};
|
||||
|
||||
|
||||
/// The memory manager class
|
||||
class MemoryManager
|
||||
{
|
||||
private:
|
||||
template <typename T> friend class Memory;
|
||||
// Used by the private static methods called by class Memory:
|
||||
typedef Memory<int> Mem;
|
||||
/// 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;
|
||||
|
||||
// Methods used by class Memory
|
||||
|
||||
// Allocate and register a new pointer. Return the host pointer.
|
||||
// h_ptr must be already allocated using new T[] if mt is a pure device
|
||||
// memory type, e.g. CUDA (mt will not be HOST).
|
||||
static void *New_(void *h_ptr, std::size_t size, MemoryType mt,
|
||||
unsigned &flags);
|
||||
|
||||
// Register an external pointer of the given MemoryType. Return the host
|
||||
// pointer.
|
||||
static void *Register_(void *ptr, void *h_ptr, std::size_t capacity,
|
||||
MemoryType mt, bool own, bool alias, unsigned &flags);
|
||||
|
||||
// Register an alias. Return the host pointer. Note: base_h_ptr may be an
|
||||
// alias.
|
||||
static void Alias_(void *base_h_ptr, std::size_t offset, std::size_t size,
|
||||
unsigned base_flags, unsigned &flags);
|
||||
|
||||
// Un-register and free memory identified by its host pointer. Returns the
|
||||
// memory type of the host pointer.
|
||||
static MemoryType Delete_(void *h_ptr, unsigned flags);
|
||||
|
||||
// Return a pointer to the memory identified by the host pointer h_ptr for
|
||||
// access with the given MemoryClass.
|
||||
static void *ReadWrite_(void *h_ptr, MemoryClass mc, std::size_t size,
|
||||
unsigned &flags);
|
||||
|
||||
static const void *Read_(void *h_ptr, MemoryClass mc, std::size_t size,
|
||||
unsigned &flags);
|
||||
|
||||
static void *Write_(void *h_ptr, MemoryClass mc, std::size_t size,
|
||||
unsigned &flags);
|
||||
|
||||
static void SyncAlias_(const void *base_h_ptr, void *alias_h_ptr,
|
||||
size_t alias_size, unsigned base_flags,
|
||||
unsigned &alias_flags);
|
||||
|
||||
// Return the type the of the currently valid memory. If more than one types
|
||||
// are valid, return a device type.
|
||||
static MemoryType GetMemoryType_(void *h_ptr, unsigned flags);
|
||||
|
||||
// Copy entries from valid memory type to valid memory type. Both dest_h_ptr
|
||||
// and src_h_ptr are registered host pointers.
|
||||
static void Copy_(void *dest_h_ptr, const void *src_h_ptr, std::size_t size,
|
||||
unsigned src_flags, unsigned &dest_flags);
|
||||
|
||||
// Copy entries from valid memory type to host memory, where dest_h_ptr is
|
||||
// not a registered host pointer and src_h_ptr is a registered host pointer.
|
||||
static void CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
std::size_t size, unsigned src_flags);
|
||||
|
||||
// Copy entries from host memory to valid memory type, where dest_h_ptr is a
|
||||
// registered host pointer and src_h_ptr is not a registered host pointer.
|
||||
static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
std::size_t size, unsigned &dest_flags);
|
||||
|
||||
/// Adds an address in the map
|
||||
void *Insert(void *ptr, const std::size_t bytes);
|
||||
|
||||
void InsertDevice(void *ptr, void *h_ptr, size_t bytes);
|
||||
|
||||
/// Remove the address from the map, as well as all its aliases
|
||||
void *Erase(void *ptr, bool free_dev_ptr = true);
|
||||
|
||||
/// Return the corresponding device pointer of ptr, allocating and moving the
|
||||
/// data if needed (used in OccaPtr)
|
||||
void *GetDevicePtr(const void *ptr, size_t bytes, bool copy_data);
|
||||
|
||||
void InsertAlias(const void *base_ptr, void *alias_ptr, bool base_is_alias);
|
||||
|
||||
void EraseAlias(void *alias_ptr);
|
||||
|
||||
void *GetAliasDevicePtr(const void *alias_ptr, size_t bytes, bool copy_data);
|
||||
|
||||
/// Return true if the pointer has been registered
|
||||
bool IsKnown(const void *ptr);
|
||||
|
||||
public:
|
||||
MemoryManager();
|
||||
~MemoryManager();
|
||||
|
||||
void Destroy();
|
||||
/// 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);
|
||||
};
|
||||
|
||||
|
||||
// Inline methods
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::New(int size, MemoryType mt)
|
||||
{
|
||||
if (mt == MemoryType::HOST)
|
||||
{
|
||||
New(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate the host pointer with new T[] if 'mt' is a pure device memory
|
||||
// type, e.g. CUDA.
|
||||
T *tmp = (mt == MemoryType::CUDA) ? new T[size] : NULL;
|
||||
h_ptr = (T*)MemoryManager::New_(tmp, size*sizeof(T), mt, flags);
|
||||
capacity = size;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::Wrap(T *ptr, int size, MemoryType mt, bool own)
|
||||
{
|
||||
if (mt == MemoryType::HOST)
|
||||
{
|
||||
Wrap(ptr, size, own);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allocate the host pointer with new T[] if 'mt' is a pure device memory
|
||||
// type, e.g. CUDA.
|
||||
T *tmp = (mt == MemoryType::CUDA) ? new T[size] : NULL;
|
||||
h_ptr = (T*)MemoryManager::Register_(ptr, tmp, size*sizeof(T), mt, own,
|
||||
false, flags);
|
||||
capacity = size;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
|
||||
{
|
||||
h_ptr = base.h_ptr + offset;
|
||||
capacity = size;
|
||||
if (!(base.flags & REGISTERED))
|
||||
{
|
||||
flags = (base.flags | ALIAS) & ~(OWNS_HOST | OWNS_DEVICE);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryManager::Alias_(base.h_ptr, offset*sizeof(T), size*sizeof(T),
|
||||
base.flags, flags);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::Delete()
|
||||
{
|
||||
if (!(flags & REGISTERED) ||
|
||||
MemoryManager::Delete_(h_ptr, flags) == MemoryType::HOST)
|
||||
{
|
||||
if (flags & OWNS_HOST) { delete [] h_ptr; }
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T &Memory<T>::operator[](int idx)
|
||||
{
|
||||
MFEM_ASSERT((flags & VALID_HOST) && !(flags & VALID_DEVICE),
|
||||
"invalid host pointer access");
|
||||
return h_ptr[idx];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const T &Memory<T>::operator[](int idx) const
|
||||
{
|
||||
MFEM_ASSERT((flags & VALID_HOST), "invalid host pointer access");
|
||||
return h_ptr[idx];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Memory<T>::operator T*()
|
||||
{
|
||||
MFEM_ASSERT(Empty() ||
|
||||
((flags & VALID_HOST) &&
|
||||
(std::is_const<T>::value || !(flags & VALID_DEVICE))),
|
||||
"invalid host pointer access");
|
||||
return h_ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Memory<T>::operator const T*() const
|
||||
{
|
||||
MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
|
||||
return h_ptr;
|
||||
}
|
||||
|
||||
template <typename T> template <typename U>
|
||||
inline Memory<T>::operator U*()
|
||||
{
|
||||
MFEM_ASSERT(Empty() ||
|
||||
((flags & VALID_HOST) &&
|
||||
(std::is_const<U>::value || !(flags & VALID_DEVICE))),
|
||||
"invalid host pointer access");
|
||||
return reinterpret_cast<U*>(h_ptr);
|
||||
}
|
||||
|
||||
template <typename T> template <typename U>
|
||||
inline Memory<T>::operator const U*() const
|
||||
{
|
||||
MFEM_ASSERT(Empty() || (flags & VALID_HOST), "invalid host pointer access");
|
||||
return reinterpret_cast<U*>(h_ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *Memory<T>::ReadWrite(MemoryClass mc, int size)
|
||||
{
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (mc == MemoryClass::HOST) { return h_ptr; }
|
||||
MemoryManager::Register_(h_ptr, NULL, capacity*sizeof(T),
|
||||
MemoryType::HOST, flags & OWNS_HOST,
|
||||
flags & ALIAS, flags);
|
||||
}
|
||||
return (T*)MemoryManager::ReadWrite_(h_ptr, mc, size*sizeof(T), flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const T *Memory<T>::Read(MemoryClass mc, int size) const
|
||||
{
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (mc == MemoryClass::HOST) { return h_ptr; }
|
||||
MemoryManager::Register_((void*)h_ptr, NULL, capacity*sizeof(T),
|
||||
MemoryType::HOST, flags & OWNS_HOST,
|
||||
flags & ALIAS, flags);
|
||||
}
|
||||
return (const T *)MemoryManager::Read_(
|
||||
(void*)h_ptr, mc, size*sizeof(T), flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *Memory<T>::Write(MemoryClass mc, int size)
|
||||
{
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (mc == MemoryClass::HOST) { return h_ptr; }
|
||||
MemoryManager::Register_(h_ptr, NULL, capacity*sizeof(T),
|
||||
MemoryType::HOST, flags & OWNS_HOST,
|
||||
flags & ALIAS, flags);
|
||||
}
|
||||
return (T*)MemoryManager::Write_(h_ptr, mc, size*sizeof(T), flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::Sync(const Memory &other) const
|
||||
{
|
||||
if (!(flags & REGISTERED) && (other.flags & REGISTERED))
|
||||
{
|
||||
MFEM_ASSERT(h_ptr == other.h_ptr &&
|
||||
(flags & ALIAS) == (other.flags & ALIAS),
|
||||
"invalid input");
|
||||
flags = (flags | REGISTERED) & ~(OWNS_DEVICE | OWNS_INTERNAL);
|
||||
}
|
||||
flags = (flags & ~(VALID_HOST | VALID_DEVICE)) |
|
||||
(other.flags & (VALID_HOST | VALID_DEVICE));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::SyncAlias(const Memory &base, int alias_size) const
|
||||
{
|
||||
// Assuming that if *this is registered then base is also registered.
|
||||
MFEM_ASSERT(!(flags & REGISTERED) || (base.flags & REGISTERED),
|
||||
"invalid base state");
|
||||
if (!(base.flags & REGISTERED)) { return; }
|
||||
MemoryManager::SyncAlias_(base.h_ptr, h_ptr, alias_size*sizeof(T),
|
||||
base.flags, flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline MemoryType Memory<T>::GetMemoryType() const
|
||||
{
|
||||
if (!(flags & REGISTERED)) { return MemoryType::HOST; }
|
||||
return MemoryManager::GetMemoryType_(h_ptr, flags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyFrom(const Memory &src, int size)
|
||||
{
|
||||
if (!(flags & REGISTERED) && !(src.flags & REGISTERED))
|
||||
{
|
||||
if (h_ptr != src.h_ptr && size != 0)
|
||||
{
|
||||
MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
|
||||
"data overlaps!");
|
||||
std::memcpy(h_ptr, src, size*sizeof(T));
|
||||
}
|
||||
// *this is not registered, so (flags & VALID_HOST) must be true
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryManager::Copy_(h_ptr, src.h_ptr, size*sizeof(T), src.flags, flags);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyFromHost(const T *src, int size)
|
||||
{
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (h_ptr != src && size != 0)
|
||||
{
|
||||
MFEM_ASSERT(h_ptr + size <= src || src + size <= h_ptr,
|
||||
"data overlaps!");
|
||||
std::memcpy(h_ptr, src, size*sizeof(T));
|
||||
}
|
||||
// *this is not registered, so (flags & VALID_HOST) must be true
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryManager::CopyFromHost_(h_ptr, src, size*sizeof(T), flags);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyToHost(T *dest, int size) const
|
||||
{
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (h_ptr != dest && size != 0)
|
||||
{
|
||||
MFEM_ASSERT(h_ptr + size <= dest || dest + size <= h_ptr,
|
||||
"data overlaps!");
|
||||
std::memcpy(dest, h_ptr, size*sizeof(T));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryManager::CopyToHost_(dest, h_ptr, size*sizeof(T), flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @brief Print the state of a Memory object based on its internal flags.
|
||||
Useful in a debugger. */
|
||||
extern void MemoryPrintFlags(unsigned flags);
|
||||
|
||||
|
||||
/// 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
|
||||
|
||||
+33
-16
@@ -9,36 +9,53 @@
|
||||
// terms of the GNU Lesser General Public License (as published by the Free
|
||||
// Software Foundation) version 2.1 dated February 1999.
|
||||
|
||||
#include "occa.hpp"
|
||||
|
||||
#ifdef MFEM_USE_OCCA
|
||||
#include "device.hpp"
|
||||
|
||||
#if defined(MFEM_USE_CUDA) && OCCA_CUDA_ENABLED
|
||||
#include <occa/modes/cuda/utils.hpp>
|
||||
#endif
|
||||
#include "forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// This variable is defined in device.cpp:
|
||||
namespace internal { extern occa::device occaDevice; }
|
||||
namespace internal { extern OccaDevice occaDevice; }
|
||||
|
||||
occa::device &OccaDev() { return internal::occaDevice; }
|
||||
|
||||
occa::memory OccaMemoryWrap(void *ptr, std::size_t bytes)
|
||||
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(internal::occaDevice, ptr, bytes);
|
||||
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(internal::occaDevice, ptr, bytes);
|
||||
return occa::cpu::wrapMemory(dev, adrs, bytes);
|
||||
#else // MFEM_USE_OCCA
|
||||
return (void*)NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
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);
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_OCCA
|
||||
OccaDevice OccaDev() { return internal::occaDevice; }
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+17
-59
@@ -15,71 +15,29 @@
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_OCCA
|
||||
#include "mem_manager.hpp"
|
||||
#include "device.hpp"
|
||||
#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
|
||||
{
|
||||
|
||||
/// Return the default occa::device used by MFEM.
|
||||
occa::device &OccaDev();
|
||||
|
||||
/// Wrap a pointer as occa::memory with the default occa::device used by MFEM.
|
||||
/** It is assumed that @a ptr is suitable for use with the current mfem::Device
|
||||
configuration. */
|
||||
occa::memory OccaMemoryWrap(void *ptr, std::size_t bytes);
|
||||
|
||||
/** @brief Wrap a Memory object as occa::memory for read only access with the
|
||||
mfem::Device MemoryClass. The returned occa::memory is associated with the
|
||||
default occa::device used by MFEM. */
|
||||
template <typename T>
|
||||
const occa::memory OccaMemoryRead(const Memory<T> &mem, size_t size)
|
||||
{
|
||||
mem.UseDevice(true);
|
||||
const void *ptr = mem.Read(Device::GetMemoryClass(), size);
|
||||
return OccaMemoryWrap(const_cast<void *>(ptr), size*sizeof(T));
|
||||
}
|
||||
|
||||
/** @brief Wrap a Memory object as occa::memory for write only access with the
|
||||
mfem::Device MemoryClass. The returned occa::memory is associated with the
|
||||
default occa::device used by MFEM. */
|
||||
template <typename T>
|
||||
occa::memory OccaMemoryWrite(Memory<T> &mem, size_t size)
|
||||
{
|
||||
mem.UseDevice(true);
|
||||
return OccaMemoryWrap(mem.Write(Device::GetMemoryClass(), size),
|
||||
size*sizeof(T));
|
||||
}
|
||||
|
||||
/** @brief Wrap a Memory object as occa::memory for read-write access with the
|
||||
mfem::Device MemoryClass. The returned occa::memory is associated with the
|
||||
default occa::device used by MFEM. */
|
||||
template <typename T>
|
||||
occa::memory OccaMemoryReadWrite(Memory<T> &mem, size_t size)
|
||||
{
|
||||
mem.UseDevice(true);
|
||||
return OccaMemoryWrap(mem.ReadWrite(Device::GetMemoryClass(), size),
|
||||
size*sizeof(T));
|
||||
}
|
||||
|
||||
|
||||
/** @brief Function that determines if an OCCA kernel should be used, based on
|
||||
the current mfem::Device configuration. */
|
||||
inline bool DeviceCanUseOcca()
|
||||
{
|
||||
return Device::Allows(Backend::OCCA_CUDA) ||
|
||||
(Device::Allows(Backend::OCCA_OMP) &&
|
||||
!Device::Allows(Backend::DEVICE_MASK)) ||
|
||||
(Device::Allows(Backend::OCCA_CPU) &&
|
||||
!Device::Allows(Backend::DEVICE_MASK|Backend::OMP_MASK));
|
||||
}
|
||||
|
||||
typedef std::pair<int,int> occa_id_t;
|
||||
typedef std::map<occa_id_t, occa::kernel> occa_kernel_t;
|
||||
// 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_USE_OCCA
|
||||
|
||||
#endif // MFEM_OCCA_HPP
|
||||
|
||||
@@ -43,8 +43,6 @@ private:
|
||||
const char *description;
|
||||
bool required;
|
||||
|
||||
Option() = default;
|
||||
|
||||
Option(OptionType _type, void *_var_ptr, const char *_short_name,
|
||||
const char *_long_name, const char *_description, bool req)
|
||||
: type(_type), var_ptr(_var_ptr), short_name(_short_name),
|
||||
|
||||
@@ -26,8 +26,6 @@ public:
|
||||
A one;
|
||||
B two;
|
||||
|
||||
Pair() = default;
|
||||
|
||||
Pair(const A &one, const B &two) : one(one), two(two) {}
|
||||
};
|
||||
|
||||
|
||||
+48
-44
@@ -15,7 +15,7 @@
|
||||
#include "table.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
#include "../general/mem_manager.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
@@ -30,14 +30,14 @@ Table::Table(const Table &table)
|
||||
if (size >= 0)
|
||||
{
|
||||
const int nnz = table.I[size];
|
||||
I.New(size+1, table.I.GetMemoryType());
|
||||
J.New(nnz, table.J.GetMemoryType());
|
||||
I.CopyFrom(table.I, size+1);
|
||||
J.CopyFrom(table.J, nnz);
|
||||
I = mfem::New<int>(size+1);
|
||||
J = mfem::New<int>(nnz);
|
||||
memcpy(I, table.I, sizeof(int)*(size+1));
|
||||
memcpy(J, table.J, sizeof(int)*nnz);
|
||||
}
|
||||
else
|
||||
{
|
||||
I.Reset(); J.Reset();
|
||||
I = J = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ Table::Table (int dim, int connections_per_row)
|
||||
int i, j, sum = dim * connections_per_row;
|
||||
|
||||
size = dim;
|
||||
I.New(size+1);
|
||||
J.New(sum);
|
||||
I = mfem::New<int>(size+1);
|
||||
J = mfem::New<int>(sum);
|
||||
|
||||
I[0] = 0;
|
||||
for (i = 1; i <= size; i++)
|
||||
@@ -71,8 +71,8 @@ Table::Table (int nrows, int *partitioning)
|
||||
{
|
||||
size = nrows;
|
||||
|
||||
I.New(size+1);
|
||||
J.New(size);
|
||||
I = mfem::New<int>(size+1);
|
||||
J = mfem::New<int>(size);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
@@ -101,8 +101,8 @@ void Table::MakeJ()
|
||||
j = I[i], I[i] = k, k += j;
|
||||
}
|
||||
|
||||
J.Delete();
|
||||
J.New(I[size]=k);
|
||||
if (J) { mfem::Delete(J); }
|
||||
J = mfem::New<int>(I[size]=k);
|
||||
}
|
||||
|
||||
void Table::AddConnections (int r, const int *c, int nc)
|
||||
@@ -149,14 +149,14 @@ void Table::SetDims(int rows, int nnz)
|
||||
if (size != rows)
|
||||
{
|
||||
size = rows;
|
||||
I.Delete();
|
||||
(rows >= 0) ? I.New(rows+1) : I.Reset();
|
||||
if (I) { mfem::Delete(I); }
|
||||
I = (rows >= 0) ? (mfem::New<int>(rows+1)) : (NULL);
|
||||
}
|
||||
|
||||
if (j != nnz)
|
||||
{
|
||||
J.Delete();
|
||||
(nnz > 0) ? J.New(nnz) : J.Reset();
|
||||
if (J) { mfem::Delete(J); }
|
||||
J = (nnz > 0) ? (mfem::New<int>(nnz)) : (NULL);
|
||||
}
|
||||
|
||||
if (size >= 0)
|
||||
@@ -207,14 +207,14 @@ void Table::SortRows()
|
||||
|
||||
void Table::SetIJ(int *newI, int *newJ, int newsize)
|
||||
{
|
||||
I.Delete();
|
||||
J.Delete();
|
||||
mfem::Delete(I);
|
||||
mfem::Delete(J);
|
||||
I = newI;
|
||||
J = newJ;
|
||||
if (newsize >= 0)
|
||||
{
|
||||
size = newsize;
|
||||
}
|
||||
I.Wrap(newI, size+1, true);
|
||||
J.Wrap(newJ, I[size], true);
|
||||
}
|
||||
|
||||
int Table::Push(int i, int j)
|
||||
@@ -222,7 +222,6 @@ int Table::Push(int i, int j)
|
||||
MFEM_ASSERT( i >=0 && i<size, "Index out of bounds. i = "<<i);
|
||||
|
||||
for (int k = I[i], end = I[i+1]; k < end; k++)
|
||||
{
|
||||
if (J[k] == j)
|
||||
{
|
||||
return k;
|
||||
@@ -232,7 +231,6 @@ int Table::Push(int i, int j)
|
||||
J[k] = j;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_ABORT("Reached end of loop unexpectedly: (i,j) = (" << i << ", " << j
|
||||
<< ")");
|
||||
@@ -245,16 +243,14 @@ void Table::Finalize()
|
||||
int i, j, end, sum = 0, n = 0, newI = 0;
|
||||
|
||||
for (i=0; i<I[size]; i++)
|
||||
{
|
||||
if (J[i] != -1)
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum != I[size])
|
||||
{
|
||||
int *NewJ = new int[sum];
|
||||
int *NewJ = mfem::New<int>(sum);
|
||||
|
||||
for (i=0; i<size; i++)
|
||||
{
|
||||
@@ -269,9 +265,9 @@ void Table::Finalize()
|
||||
}
|
||||
I[size] = sum;
|
||||
|
||||
J.Delete();
|
||||
mfem::Delete(J);
|
||||
|
||||
J.Wrap(NewJ, sum, true);
|
||||
J = NewJ;
|
||||
|
||||
MFEM_ASSERT(sum == n, "sum = " << sum << ", n = " << n);
|
||||
}
|
||||
@@ -284,8 +280,8 @@ void Table::MakeFromList(int nrows, const Array<Connection> &list)
|
||||
size = nrows;
|
||||
int nnz = list.Size();
|
||||
|
||||
I.New(size+1);
|
||||
J.New(nnz);
|
||||
I = mfem::New<int>(size+1);
|
||||
J = mfem::New<int>(nnz);
|
||||
|
||||
for (int i = 0, k = 0; i <= size; i++)
|
||||
{
|
||||
@@ -335,12 +331,10 @@ void Table::PrintMatlab(std::ostream & out) const
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
for (j = I[i]; j < I[i+1]; j++)
|
||||
{
|
||||
out << i << " " << J[j] << " 1. \n";
|
||||
}
|
||||
}
|
||||
|
||||
out << flush;
|
||||
}
|
||||
@@ -361,17 +355,17 @@ void Table::Save(std::ostream &out) const
|
||||
|
||||
void Table::Load(std::istream &in)
|
||||
{
|
||||
I.Delete();
|
||||
J.Delete();
|
||||
mfem::Delete(I);
|
||||
mfem::Delete(J);
|
||||
|
||||
in >> size;
|
||||
I.New(size+1);
|
||||
I = mfem::New<int>(size+1);
|
||||
for (int i = 0; i <= size; i++)
|
||||
{
|
||||
in >> I[i];
|
||||
}
|
||||
int nnz = I[size];
|
||||
J.New(nnz);
|
||||
J =mfem::New<int>(nnz);
|
||||
for (int j = 0; j < nnz; j++)
|
||||
{
|
||||
in >> J[j];
|
||||
@@ -380,16 +374,28 @@ void Table::Load(std::istream &in)
|
||||
|
||||
void Table::Clear()
|
||||
{
|
||||
I.Delete();
|
||||
J.Delete();
|
||||
mfem::Delete(I);
|
||||
mfem::Delete(J);
|
||||
size = -1;
|
||||
I.Reset();
|
||||
J.Reset();
|
||||
I = J = NULL;
|
||||
}
|
||||
|
||||
void Table::Copy(Table & copy) const
|
||||
{
|
||||
copy = *this;
|
||||
if (size >= 0)
|
||||
{
|
||||
int * i_copy = mfem::New<int>(size+1);
|
||||
int * j_copy = mfem::New<int>(I[size]);
|
||||
|
||||
memcpy(i_copy, I, sizeof(int)*(size+1));
|
||||
memcpy(j_copy, J, sizeof(int)*I[size]);
|
||||
|
||||
copy.SetIJ(i_copy, j_copy, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Table::Swap(Table & other)
|
||||
@@ -407,8 +413,8 @@ long Table::MemoryUsage() const
|
||||
|
||||
Table::~Table ()
|
||||
{
|
||||
I.Delete();
|
||||
J.Delete();
|
||||
if (I) { mfem::Delete(I); }
|
||||
if (J) { mfem::Delete(J); }
|
||||
}
|
||||
|
||||
void Transpose (const Table &A, Table &At, int _ncols_A)
|
||||
@@ -438,12 +444,10 @@ void Transpose (const Table &A, Table &At, int _ncols_A)
|
||||
}
|
||||
|
||||
for (int i = 0; i < nrows_A; i++)
|
||||
{
|
||||
for (int j = i_A[i]; j < i_A[i+1]; j++)
|
||||
{
|
||||
j_At[i_At[j_A[j]]++] = i;
|
||||
}
|
||||
}
|
||||
for (int i = ncols_A; i > 0; i--)
|
||||
{
|
||||
i_At[i] = i_At[i-1];
|
||||
|
||||
+6
-13
@@ -27,7 +27,6 @@ namespace mfem
|
||||
struct Connection
|
||||
{
|
||||
int from, to;
|
||||
Connection() = default;
|
||||
Connection(int from, int to) : from(from), to(to) {}
|
||||
|
||||
bool operator== (const Connection &rhs) const
|
||||
@@ -43,17 +42,18 @@ struct Connection
|
||||
class Table
|
||||
{
|
||||
protected:
|
||||
|
||||
/// size is the number of TYPE I elements.
|
||||
int size;
|
||||
|
||||
/** Arrays for the connectivity information in the CSR storage.
|
||||
I is of size "size+1", J is of size the number of connections
|
||||
between TYPE I to TYPE II elements (actually stored I[size]). */
|
||||
Memory<int> I, J;
|
||||
int *I, *J;
|
||||
|
||||
public:
|
||||
/// Creates an empty table
|
||||
Table() { size = -1; I.Reset(); J.Reset(); }
|
||||
Table() { size = -1; I = J = NULL; }
|
||||
|
||||
/// Copy constructor
|
||||
Table(const Table &);
|
||||
@@ -65,8 +65,8 @@ public:
|
||||
explicit Table (int dim, int connections_per_row = 3);
|
||||
|
||||
/** Create a table from a list of connections, see MakeFromList(). */
|
||||
Table(int nrows, Array<Connection> &list) : size(-1)
|
||||
{ I.Reset(); J.Reset(); MakeFromList(nrows, list); }
|
||||
Table(int nrows, Array<Connection> &list) : size(-1), I(NULL), J(NULL)
|
||||
{ MakeFromList(nrows, list); }
|
||||
|
||||
/** Create a table with one entry per row with column indices given
|
||||
by 'partitioning'. */
|
||||
@@ -115,16 +115,9 @@ public:
|
||||
const int *GetI() const { return I; }
|
||||
const int *GetJ() const { return J; }
|
||||
|
||||
Memory<int> &GetIMemory() { return I; }
|
||||
Memory<int> &GetJMemory() { return J; }
|
||||
const Memory<int> &GetIMemory() const { return I; }
|
||||
const Memory<int> &GetJMemory() const { return J; }
|
||||
|
||||
/// @brief Sort the column (TYPE II) indices in each row.
|
||||
void SortRows();
|
||||
|
||||
/// Replace the #I and #J arrays with the given @a newI and @a newJ arrays.
|
||||
/** If @a newsize < 0, then the size of the Table is not modified. */
|
||||
void SetIJ(int *newI, int *newJ, int newsize = -1);
|
||||
|
||||
/** Establish connection between element i and element j in the table.
|
||||
@@ -151,7 +144,7 @@ public:
|
||||
int Width() const;
|
||||
|
||||
/// Call this if data has been stolen.
|
||||
void LoseData() { size = -1; I.Reset(); J.Reset(); }
|
||||
void LoseData() { size = -1; I = J = NULL; }
|
||||
|
||||
/// Prints the table to stream out.
|
||||
void Print(std::ostream & out = mfem::out, int width = 4) const;
|
||||
|
||||
+3
-31
@@ -50,13 +50,11 @@ BlockMatrix::BlockMatrix(const Array<int> & row_offsets_,
|
||||
BlockMatrix::~BlockMatrix()
|
||||
{
|
||||
if (owns_blocks)
|
||||
{
|
||||
for (SparseMatrix ** it = Aij.GetRow(0);
|
||||
it != Aij.GetRow(0)+(Aij.NumRows()*Aij.NumCols()); ++it)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockMatrix::SetBlock(int i, int j, SparseMatrix * mat)
|
||||
@@ -118,7 +116,6 @@ int BlockMatrix::NumNonZeroElems() const
|
||||
{
|
||||
int nnz_elem = 0;
|
||||
for (int jcol = 0; jcol != nColBlocks; ++jcol)
|
||||
{
|
||||
for (int irow = 0; irow != nRowBlocks; ++irow)
|
||||
{
|
||||
if (Aij(irow,jcol))
|
||||
@@ -126,7 +123,6 @@ int BlockMatrix::NumNonZeroElems() const
|
||||
nnz_elem+= Aij(irow,jcol)->NumNonZeroElems();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nnz_elem;
|
||||
}
|
||||
|
||||
@@ -171,12 +167,10 @@ int BlockMatrix::RowSize(const int i) const
|
||||
findGlobalRow(i, iblock, iloc);
|
||||
|
||||
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
||||
{
|
||||
if (Aij(iblock,jblock) != NULL)
|
||||
{
|
||||
rowsize += Aij(iblock,jblock)->RowSize(iloc);
|
||||
}
|
||||
}
|
||||
|
||||
return rowsize;
|
||||
}
|
||||
@@ -196,7 +190,6 @@ int BlockMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
|
||||
double *it_srow = srow.GetData();
|
||||
|
||||
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
||||
{
|
||||
if (Aij(iblock,jblock) != NULL)
|
||||
{
|
||||
Aij(iblock,jblock)->GetRow(iloc, bcols, bsrow);
|
||||
@@ -206,7 +199,6 @@ int BlockMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
|
||||
*(it_srow++) = bsrow(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -257,14 +249,12 @@ void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
|
||||
}
|
||||
|
||||
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
||||
{
|
||||
if (row_offsets[iiblock] != col_offsets[iiblock])
|
||||
{
|
||||
mfem::out << "BlockMatrix::EliminateRowCol: row_offests["
|
||||
<< iiblock << "] != col_offsets["<<iiblock<<"]\n";
|
||||
mfem_error();
|
||||
}
|
||||
}
|
||||
|
||||
// We also have to do the same for each Aij
|
||||
Array<int> block_dofs;
|
||||
@@ -280,22 +270,18 @@ void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
|
||||
if (Aij(iiblock, iiblock))
|
||||
{
|
||||
for (int i = 0; i < block_dofs.Size(); ++i)
|
||||
{
|
||||
if (block_dofs[i])
|
||||
{
|
||||
Aij(iiblock, iiblock)->EliminateRowCol(i,block_sol(i), block_rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < block_dofs.Size(); ++i)
|
||||
{
|
||||
if (block_dofs[i])
|
||||
{
|
||||
mfem_error("BlockMatrix::EliminateRowCol: Null diagonal block \n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
||||
@@ -303,12 +289,10 @@ void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
|
||||
if (jjblock != iiblock && Aij(iiblock, jjblock))
|
||||
{
|
||||
for (int i = 0; i < block_dofs.Size(); ++i)
|
||||
{
|
||||
if (block_dofs[i])
|
||||
{
|
||||
Aij(iiblock, jjblock)->EliminateRow(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jjblock != iiblock && Aij(jjblock, iiblock))
|
||||
{
|
||||
@@ -341,13 +325,11 @@ void BlockMatrix::EliminateZeroRows(const double threshold)
|
||||
if (norm <= threshold)
|
||||
{
|
||||
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
||||
{
|
||||
if (Aij(iblock,jblock))
|
||||
{
|
||||
Aij(iblock,jblock)->EliminateRow(
|
||||
i, (iblock==jblock) ? DIAG_ONE : DIAG_ZERO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,12 +340,10 @@ void BlockMatrix::EliminateZeroRows(const double threshold)
|
||||
{
|
||||
norm = 0.;
|
||||
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
||||
{
|
||||
if (Aij(iblock,jblock))
|
||||
{
|
||||
norm += Aij(iblock,jblock)->GetRowNorml1(i);
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(!(norm <= threshold), "diagonal block is NULL:"
|
||||
" iblock = " << iblock << ", i = " << i << ", norm = "
|
||||
@@ -475,9 +455,9 @@ SparseMatrix * BlockMatrix::CreateMonolithic() const
|
||||
{
|
||||
int nnz = NumNonZeroElems();
|
||||
|
||||
int * i_amono = new int[row_offsets[nRowBlocks]+2];
|
||||
int * j_amono = new int[nnz];
|
||||
double * data = new double[nnz];
|
||||
int * i_amono = mfem::New<int>(row_offsets[nRowBlocks]+2);
|
||||
int * j_amono = mfem::New<int>(nnz);
|
||||
double * data = mfem::New<double>(nnz);
|
||||
|
||||
for (int i = 0; i < row_offsets[nRowBlocks]+2; i++)
|
||||
{
|
||||
@@ -582,15 +562,11 @@ BlockMatrix * Transpose(const BlockMatrix & A)
|
||||
At->owns_blocks = 1;
|
||||
|
||||
for (int irowAt = 0; irowAt < At->NumRowBlocks(); ++irowAt)
|
||||
{
|
||||
for (int jcolAt = 0; jcolAt < At->NumColBlocks(); ++jcolAt)
|
||||
{
|
||||
if (!A.IsZeroBlock(jcolAt, irowAt))
|
||||
{
|
||||
At->SetBlock(irowAt, jcolAt, Transpose(A.GetBlock(jcolAt, irowAt)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return At;
|
||||
}
|
||||
|
||||
@@ -601,17 +577,14 @@ BlockMatrix * Mult(const BlockMatrix & A, const BlockMatrix & B)
|
||||
Array<SparseMatrix *> CijPieces(A.NumColBlocks());
|
||||
|
||||
for (int irowC = 0; irowC < A.NumRowBlocks(); ++irowC)
|
||||
{
|
||||
for (int jcolC = 0; jcolC < B.NumColBlocks(); ++jcolC)
|
||||
{
|
||||
CijPieces.SetSize(0, static_cast<SparseMatrix *>(NULL));
|
||||
for (int k = 0; k < A.NumColBlocks(); ++k)
|
||||
{
|
||||
if (!A.IsZeroBlock(irowC, k) && !B.IsZeroBlock(k, jcolC))
|
||||
{
|
||||
CijPieces.Append(Mult(A.GetBlock(irowC, k), B.GetBlock(k, jcolC)));
|
||||
}
|
||||
}
|
||||
|
||||
if (CijPieces.Size() > 1)
|
||||
{
|
||||
@@ -627,7 +600,6 @@ BlockMatrix * Mult(const BlockMatrix & A, const BlockMatrix & B)
|
||||
C->SetBlock(irowC, jcolC, CijPieces[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
@@ -171,9 +171,10 @@ inline void BlockMatrix::findGlobalRow(int iglobal, int & iblock,
|
||||
}
|
||||
|
||||
for (iblock = 0; iblock < nRowBlocks; ++iblock)
|
||||
{
|
||||
if (row_offsets[iblock+1] > iglobal) { break; }
|
||||
}
|
||||
if (row_offsets[iblock+1] > iglobal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
iloc = iglobal - row_offsets[iblock];
|
||||
}
|
||||
@@ -187,9 +188,10 @@ inline void BlockMatrix::findGlobalCol(int jglobal, int & jblock,
|
||||
}
|
||||
|
||||
for (jblock = 0; jblock < nColBlocks; ++jblock)
|
||||
{
|
||||
if (col_offsets[jblock+1] > jglobal) { break; }
|
||||
}
|
||||
if (col_offsets[jblock+1] > jglobal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
jloc = jglobal - col_offsets[jblock];
|
||||
}
|
||||
|
||||
@@ -117,15 +117,11 @@ void BlockOperator::MultTranspose (const Vector & x, Vector & y) const
|
||||
BlockOperator::~BlockOperator()
|
||||
{
|
||||
if (owns_blocks)
|
||||
{
|
||||
for (int iRow=0; iRow < nRowBlocks; ++iRow)
|
||||
{
|
||||
for (int jCol=0; jCol < nColBlocks; ++jCol)
|
||||
{
|
||||
delete op(jCol,iRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@@ -161,7 +157,6 @@ void BlockDiagonalPreconditioner::Mult (const Vector & x, Vector & y) const
|
||||
xblock.Update(x.GetData(), offsets);
|
||||
|
||||
for (int i=0; i<nBlocks; ++i)
|
||||
{
|
||||
if (op[i])
|
||||
{
|
||||
op[i]->Mult(xblock.GetBlock(i), yblock.GetBlock(i));
|
||||
@@ -170,7 +165,6 @@ void BlockDiagonalPreconditioner::Mult (const Vector & x, Vector & y) const
|
||||
{
|
||||
yblock.GetBlock(i) = xblock.GetBlock(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action of the transpose operator
|
||||
@@ -184,7 +178,6 @@ void BlockDiagonalPreconditioner::MultTranspose (const Vector & x,
|
||||
xblock.Update(x.GetData(), offsets);
|
||||
|
||||
for (int i=0; i<nBlocks; ++i)
|
||||
{
|
||||
if (op[i])
|
||||
{
|
||||
(op[i])->MultTranspose(xblock.GetBlock(i), yblock.GetBlock(i));
|
||||
@@ -193,18 +186,15 @@ void BlockDiagonalPreconditioner::MultTranspose (const Vector & x,
|
||||
{
|
||||
yblock.GetBlock(i) = xblock.GetBlock(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockDiagonalPreconditioner::~BlockDiagonalPreconditioner()
|
||||
{
|
||||
if (owns_blocks)
|
||||
{
|
||||
for (int i=0; i<nBlocks; ++i)
|
||||
{
|
||||
delete op[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockLowerTriangularPreconditioner::BlockLowerTriangularPreconditioner(
|
||||
|
||||
+7
-25
@@ -20,9 +20,8 @@ void BlockVector::SetBlocks()
|
||||
{
|
||||
for (int i = 0; i < numBlocks; ++i)
|
||||
{
|
||||
blocks[i].NewMemoryAndSize(
|
||||
Memory<double>(data, blockOffsets[i], BlockSize(i)),
|
||||
BlockSize(i), true);
|
||||
blocks[i].NewDataAndSize(data+blockOffsets[i],
|
||||
blockOffsets[i+1]-blockOffsets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,15 +44,6 @@ BlockVector::BlockVector(const Array<int> & bOffsets):
|
||||
SetBlocks();
|
||||
}
|
||||
|
||||
BlockVector::BlockVector(const Array<int> & bOffsets, MemoryType mt)
|
||||
: Vector(bOffsets.Last(), mt),
|
||||
numBlocks(bOffsets.Size()-1),
|
||||
blockOffsets(bOffsets.GetData())
|
||||
{
|
||||
blocks = new Vector[numBlocks];
|
||||
SetBlocks();
|
||||
}
|
||||
|
||||
//! Copy constructor
|
||||
BlockVector::BlockVector(const BlockVector & v):
|
||||
Vector(v),
|
||||
@@ -88,14 +78,9 @@ void BlockVector::Update(double *data, const Array<int> & bOffsets)
|
||||
}
|
||||
|
||||
void BlockVector::Update(const Array<int> &bOffsets)
|
||||
{
|
||||
Update(bOffsets, data.GetMemoryType());
|
||||
}
|
||||
|
||||
void BlockVector::Update(const Array<int> &bOffsets, MemoryType mt)
|
||||
{
|
||||
blockOffsets = bOffsets.GetData();
|
||||
if (OwnsData() && data.GetMemoryType() == mt)
|
||||
if (OwnsData())
|
||||
{
|
||||
// check if 'bOffsets' agree with the 'blocks'
|
||||
if (bOffsets.Size() == numBlocks+1)
|
||||
@@ -117,7 +102,7 @@ void BlockVector::Update(const Array<int> &bOffsets, MemoryType mt)
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
SetSize(bOffsets.Last(), mt);
|
||||
SetSize(bOffsets.Last());
|
||||
if (numBlocks != bOffsets.Size()-1)
|
||||
{
|
||||
delete [] blocks;
|
||||
@@ -135,14 +120,12 @@ BlockVector & BlockVector::operator=(const BlockVector & original)
|
||||
}
|
||||
|
||||
for (int i(0); i <= numBlocks; ++i)
|
||||
{
|
||||
if (blockOffsets[i]!=original.blockOffsets[i])
|
||||
{
|
||||
mfem_error("Size of Blocks don't match in BlockVector::operator=");
|
||||
}
|
||||
}
|
||||
|
||||
Vector::operator=(original);
|
||||
Vector::operator=(original.GetData());
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -161,9 +144,8 @@ BlockVector::~BlockVector()
|
||||
|
||||
void BlockVector::GetBlockView(int i, Vector & blockView)
|
||||
{
|
||||
blockView.NewMemoryAndSize(
|
||||
Memory<double>(data, blockOffsets[i], BlockSize(i)),
|
||||
BlockSize(i), true);
|
||||
blockView.NewDataAndSize(data+blockOffsets[i],
|
||||
blockOffsets[i+1]-blockOffsets[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-12
@@ -57,14 +57,11 @@ public:
|
||||
*/
|
||||
BlockVector(const Array<int> & bOffsets);
|
||||
|
||||
/// Construct a BlockVector with the given MemoryType @a mt.
|
||||
BlockVector(const Array<int> & bOffsets, MemoryType mt);
|
||||
|
||||
//! Copy constructor
|
||||
BlockVector(const BlockVector & block);
|
||||
|
||||
//! View constructor
|
||||
/**
|
||||
/*
|
||||
* data is an array of double of length at least blockOffsets[numBlocks] that
|
||||
* contain all the values of the monolithic vector. bOffsets is an array of
|
||||
* integers (length nBlocks+1) that tells the offsets of each block start.
|
||||
@@ -104,14 +101,6 @@ public:
|
||||
- the offsets @a bOffsets are different from the current offsets, or
|
||||
- currently, the block-vector does not own its data. */
|
||||
void Update(const Array<int> &bOffsets);
|
||||
|
||||
/** @brief Update a BlockVector with new @a bOffsets and make sure it owns
|
||||
its data and uses the MemoryType @a mt. */
|
||||
/** The block-vector will be re-allocated if either:
|
||||
- the offsets @a bOffsets are different from the current offsets, or
|
||||
- currently, the block-vector does not own its data, or
|
||||
- currently, the block-vector does not use MemoryType @a mt. */
|
||||
void Update(const Array<int> &bOffsets, MemoryType mt);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+217
-205
@@ -16,6 +16,8 @@
|
||||
#include "vector.hpp"
|
||||
#include "matrix.hpp"
|
||||
#include "densemat.hpp"
|
||||
#include "dtensor.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include "../general/table.hpp"
|
||||
#include "../general/globals.hpp"
|
||||
|
||||
@@ -72,13 +74,13 @@ DenseMatrix::DenseMatrix() : Matrix(0)
|
||||
|
||||
DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
|
||||
{
|
||||
const int hw = height * width;
|
||||
int hw = height * width;
|
||||
if (hw > 0)
|
||||
{
|
||||
MFEM_ASSERT(m.data, "invalid source matrix");
|
||||
data = new double[hw];
|
||||
data = mfem::New<double>(hw);
|
||||
capacity = hw;
|
||||
std::memcpy(data, m.data, sizeof(double)*hw);
|
||||
mfem::Memcpy(data, m.data, sizeof(double)*hw);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -87,13 +89,20 @@ 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 = new double[capacity](); // init with zeroes
|
||||
data = mfem::New<double>(capacity);
|
||||
mfem::Set(0.0, capacity, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -108,7 +117,8 @@ DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
|
||||
capacity = m*n;
|
||||
if (capacity > 0)
|
||||
{
|
||||
data = new double[capacity](); // init with zeroes
|
||||
data = mfem::New<double>(capacity);
|
||||
mfem::Set(0.0, capacity, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -116,21 +126,28 @@ 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 = new double[capacity];
|
||||
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
(*this)(i,j) = mat(j,i);
|
||||
}
|
||||
}
|
||||
data = mfem::New<double>(capacity);
|
||||
mfem::Transpose(height, width, data, mat.Data());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -138,6 +155,13 @@ 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,
|
||||
@@ -153,10 +177,11 @@ void DenseMatrix::SetSize(int h, int w)
|
||||
{
|
||||
if (capacity > 0)
|
||||
{
|
||||
delete [] data;
|
||||
mfem::Delete(data);
|
||||
}
|
||||
capacity = hw;
|
||||
data = new double[hw](); // init with zeroes
|
||||
data = mfem::New<double>(capacity);
|
||||
mfem::Set(0.0, capacity, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,32 +195,31 @@ 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)
|
||||
{
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
y[row] = 0.0;
|
||||
}
|
||||
mfem::Set(0.0, height, y);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
mfem::Mult(height, width, data, x, y);
|
||||
}
|
||||
|
||||
void DenseMatrix::Mult(const Vector &x, Vector &y) const
|
||||
@@ -211,7 +235,7 @@ double DenseMatrix::operator *(const DenseMatrix &m) const
|
||||
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
|
||||
"incompatible dimensions");
|
||||
|
||||
const int hw = height * width;
|
||||
int hw = height * width;
|
||||
double a = 0.0;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
@@ -249,8 +273,8 @@ void DenseMatrix::AddMult(const Vector &x, Vector &y) const
|
||||
MFEM_ASSERT(height == y.Size() && width == x.Size(),
|
||||
"incompatible dimensions");
|
||||
|
||||
const double *xp = x, *d_col = data;
|
||||
double *yp = y;
|
||||
const double *xp = x;
|
||||
double *d_col = data, *yp = y;
|
||||
for (int col = 0; col < width; col++)
|
||||
{
|
||||
double x_col = xp[col];
|
||||
@@ -267,7 +291,7 @@ void DenseMatrix::AddMultTranspose(const Vector &x, Vector &y) const
|
||||
MFEM_ASSERT(height == x.Size() && width == y.Size(),
|
||||
"incompatible dimensions");
|
||||
|
||||
const double *d_col = data;
|
||||
double *d_col = data;
|
||||
for (int col = 0; col < width; col++)
|
||||
{
|
||||
double y_col = 0.0;
|
||||
@@ -285,11 +309,11 @@ void DenseMatrix::AddMult_a(double a, const Vector &x, Vector &y) const
|
||||
MFEM_ASSERT(height == y.Size() && width == x.Size(),
|
||||
"incompatible dimensions");
|
||||
|
||||
const double *xp = x, *d_col = data;
|
||||
double *yp = y;
|
||||
const double *xp = x;
|
||||
double *d_col = data, *yp = y;
|
||||
for (int col = 0; col < width; col++)
|
||||
{
|
||||
const double x_col = a*xp[col];
|
||||
double x_col = a*xp[col];
|
||||
for (int row = 0; row < height; row++)
|
||||
{
|
||||
yp[row] += x_col*d_col[row];
|
||||
@@ -304,7 +328,7 @@ void DenseMatrix::AddMultTranspose_a(double a, const Vector &x,
|
||||
MFEM_ASSERT(height == x.Size() && width == y.Size(),
|
||||
"incompatible dimensions");
|
||||
|
||||
const double *d_col = data;
|
||||
double *d_col = data;
|
||||
for (int col = 0; col < width; col++)
|
||||
{
|
||||
double y_col = 0.0;
|
||||
@@ -339,12 +363,10 @@ void DenseMatrix::LeftScaling(const Vector & s)
|
||||
{
|
||||
double * it_data = data;
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
*(it_data++) *= s(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InvLeftScaling this = diag(1./s) * this
|
||||
@@ -352,12 +374,10 @@ void DenseMatrix::InvLeftScaling(const Vector & s)
|
||||
{
|
||||
double * it_data = data;
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
*(it_data++) /= s(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RightScaling: this = this * diag(s);
|
||||
@@ -378,10 +398,11 @@ void DenseMatrix::RightScaling(const Vector & s)
|
||||
// InvRightScaling: this = this * diag(1./s);
|
||||
void DenseMatrix::InvRightScaling(const Vector & s)
|
||||
{
|
||||
double sj;
|
||||
double * it_data = data;
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
const double sj = 1./s(j);
|
||||
sj = 1./s(j);
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
*(it_data++) *= sj;
|
||||
@@ -407,12 +428,10 @@ void DenseMatrix::SymmetricScaling(const Vector & s)
|
||||
|
||||
double * it_data = data;
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
*(it_data++) *= ss[i]*ss[j];
|
||||
}
|
||||
}
|
||||
|
||||
delete[] ss;
|
||||
}
|
||||
@@ -428,19 +447,17 @@ void DenseMatrix::InvSymmetricScaling(const Vector & s)
|
||||
double * ss = new double[width];
|
||||
double * it_s = s.GetData();
|
||||
double * it_ss = ss;
|
||||
for (double * end_s = it_s + width; it_s != end_s; ++it_s)
|
||||
for ( double * end_s = it_s + width; it_s != end_s; ++it_s)
|
||||
{
|
||||
*(it_ss++) = 1./sqrt(*it_s);
|
||||
}
|
||||
|
||||
double * it_data = data;
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
*(it_data++) *= ss[i]*ss[j];
|
||||
}
|
||||
}
|
||||
|
||||
delete[] ss;
|
||||
}
|
||||
@@ -565,27 +582,22 @@ void DenseMatrix::Set(double alpha, const double *A)
|
||||
void DenseMatrix::Add(const double c, const DenseMatrix &A)
|
||||
{
|
||||
for (int j = 0; j < Width(); j++)
|
||||
{
|
||||
for (int i = 0; i < Height(); i++)
|
||||
{
|
||||
(*this)(i,j) += c * A(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DenseMatrix &DenseMatrix::operator=(double c)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = c;
|
||||
}
|
||||
mfem::Set(c, s, data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DenseMatrix &DenseMatrix::operator=(const double *d)
|
||||
{
|
||||
const int s = Height()*Width();
|
||||
int s = Height()*Width();
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = d[i];
|
||||
@@ -598,11 +610,9 @@ DenseMatrix &DenseMatrix::operator=(const DenseMatrix &m)
|
||||
SetSize(m.height, m.width);
|
||||
|
||||
const int hw = height * width;
|
||||
for (int i = 0; i < hw; i++)
|
||||
{
|
||||
data[i] = m.data[i];
|
||||
}
|
||||
|
||||
const DeviceVector d_m(m.GetData());
|
||||
DeviceVector d_data(data);
|
||||
MFEM_FORALL(i, hw, d_data[i] = d_m[i];);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -626,12 +636,10 @@ DenseMatrix &DenseMatrix::operator+=(const DenseMatrix &m)
|
||||
DenseMatrix &DenseMatrix::operator-=(const DenseMatrix &m)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
(*this)(i, j) -= m(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -2455,16 +2463,11 @@ void DenseMatrix::GetRowSums(Vector &l) const
|
||||
void DenseMatrix::Diag(double c, int n)
|
||||
{
|
||||
SetSize(n);
|
||||
|
||||
const int N = n*n;
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
data[i] = 0.0;
|
||||
}
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
data[i*(n+1)] = c;
|
||||
}
|
||||
DeviceVector d_data(data);
|
||||
MFEM_FORALL(i, N, d_data[i] = 0.0;);
|
||||
MFEM_FORALL(i, n, d_data[i*(n+1)] = c;);
|
||||
|
||||
}
|
||||
|
||||
void DenseMatrix::Diag(double *diag, int n)
|
||||
@@ -2607,17 +2610,20 @@ void DenseMatrix::GradToCurl(DenseMatrix &curl)
|
||||
|
||||
void DenseMatrix::GradToDiv(Vector &div)
|
||||
{
|
||||
MFEM_ASSERT(Width()*Height() == div.Size(), "incompatible Vector 'div'!");
|
||||
#ifdef MFEM_DEBUG
|
||||
if (Width()*Height() != div.Size())
|
||||
{
|
||||
mfem_error("DenseMatrix::GradToDiv(...)");
|
||||
}
|
||||
#endif
|
||||
|
||||
// div(dof*j+i) <-- (*this)(i,j)
|
||||
|
||||
const int n = height * width;
|
||||
double *ddata = div.GetData();
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
ddata[i] = data[i];
|
||||
}
|
||||
const DeviceVector d_data(data);
|
||||
DeviceVector d_ddata(ddata);
|
||||
MFEM_FORALL(i, n, d_ddata[i] = d_data[i];);
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyRows(const DenseMatrix &A, int row1, int row2)
|
||||
@@ -2625,12 +2631,10 @@ void DenseMatrix::CopyRows(const DenseMatrix &A, int row1, int row2)
|
||||
SetSize(row2 - row1 + 1, A.Width());
|
||||
|
||||
for (int j = 0; j < Width(); j++)
|
||||
{
|
||||
for (int i = row1; i <= row2; i++)
|
||||
{
|
||||
(*this)(i-row1,j) = A(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyCols(const DenseMatrix &A, int col1, int col2)
|
||||
@@ -2638,56 +2642,54 @@ void DenseMatrix::CopyCols(const DenseMatrix &A, int col1, int col2)
|
||||
SetSize(A.Height(), col2 - col1 + 1);
|
||||
|
||||
for (int j = col1; j <= col2; j++)
|
||||
{
|
||||
for (int i = 0; i < Height(); i++)
|
||||
{
|
||||
(*this)(i,j-col1) = A(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyMN(const DenseMatrix &A, int m, int n, int Aro, int Aco)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
SetSize(m,n);
|
||||
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
for (j = 0; j < n; j++)
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
(*this)(i,j) = A(Aro+i,Aco+j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyMN(const DenseMatrix &A, int row_offset, int col_offset)
|
||||
{
|
||||
int i, j;
|
||||
double *v = A.data;
|
||||
|
||||
for (int j = 0; j < A.Width(); j++)
|
||||
{
|
||||
for (int i = 0; i < A.Height(); i++)
|
||||
for (j = 0; j < A.Width(); j++)
|
||||
for (i = 0; i < A.Height(); i++)
|
||||
{
|
||||
(*this)(row_offset+i,col_offset+j) = *(v++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyMNt(const DenseMatrix &A, int row_offset, int col_offset)
|
||||
{
|
||||
int i, j;
|
||||
double *v = A.data;
|
||||
|
||||
for (int i = 0; i < A.Width(); i++)
|
||||
{
|
||||
for (int j = 0; j < A.Height(); j++)
|
||||
for (i = 0; i < A.Width(); i++)
|
||||
for (j = 0; j < A.Height(); j++)
|
||||
{
|
||||
(*this)(row_offset+i,col_offset+j) = *(v++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyMN(const DenseMatrix &A, int m, int n, int Aro, int Aco,
|
||||
int row_offset, int col_offset)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
MFEM_VERIFY(row_offset+m <= this->Height() && col_offset+n <= this->Width(),
|
||||
"this DenseMatrix is too small to accomodate the submatrix. "
|
||||
<< "row_offset = " << row_offset
|
||||
@@ -2707,27 +2709,23 @@ void DenseMatrix::CopyMN(const DenseMatrix &A, int m, int n, int Aro, int Aco,
|
||||
<< ", A.Width() = " << A.Width()
|
||||
);
|
||||
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
for (j = 0; j < n; j++)
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
(*this)(row_offset+i,col_offset+j) = A(Aro+i,Aco+j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::CopyMNDiag(double c, int n, int row_offset, int col_offset)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (int j = i+1; j < n; j++)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (j = i+1; j < n; j++)
|
||||
(*this)(row_offset+i,col_offset+j) =
|
||||
(*this)(row_offset+j,col_offset+i) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
(*this)(row_offset+i,col_offset+i) = c;
|
||||
}
|
||||
@@ -2736,16 +2734,14 @@ void DenseMatrix::CopyMNDiag(double c, int n, int row_offset, int col_offset)
|
||||
void DenseMatrix::CopyMNDiag(double *diag, int n, int row_offset,
|
||||
int col_offset)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (int j = i+1; j < n; j++)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (j = i+1; j < n; j++)
|
||||
(*this)(row_offset+i,col_offset+j) =
|
||||
(*this)(row_offset+j,col_offset+i) = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
(*this)(row_offset+i,col_offset+i) = diag[i];
|
||||
}
|
||||
@@ -2807,7 +2803,7 @@ void DenseMatrix::AddMatrix(DenseMatrix &A, int ro, int co)
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::AddMatrix(double a, const DenseMatrix &A, int ro, int co)
|
||||
void DenseMatrix::AddMatrix(double a, DenseMatrix &A, int ro, int co)
|
||||
{
|
||||
int h, ah, aw;
|
||||
double *p, *ap;
|
||||
@@ -2839,10 +2835,10 @@ void DenseMatrix::AddMatrix(double a, const DenseMatrix &A, int ro, int co)
|
||||
|
||||
void DenseMatrix::AddToVector(int offset, Vector &v) const
|
||||
{
|
||||
const int n = height * width;
|
||||
int i, n = height * width;
|
||||
double *vdata = v.GetData() + offset;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
vdata[i] += data[i];
|
||||
}
|
||||
@@ -2850,10 +2846,10 @@ void DenseMatrix::AddToVector(int offset, Vector &v) const
|
||||
|
||||
void DenseMatrix::GetFromVector(int offset, const Vector &v)
|
||||
{
|
||||
const int n = height * width;
|
||||
int i, n = height * width;
|
||||
const double *vdata = v.GetData() + offset;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
data[i] = vdata[i];
|
||||
}
|
||||
@@ -2861,7 +2857,7 @@ void DenseMatrix::GetFromVector(int offset, const Vector &v)
|
||||
|
||||
void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
|
||||
{
|
||||
const int n = Height();
|
||||
int n = Height();
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
if (dofs.Size() != n || Width() != n)
|
||||
@@ -2873,10 +2869,10 @@ void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
|
||||
int *dof = dofs;
|
||||
for (int i = 0; i < n-1; i++)
|
||||
{
|
||||
const int s = (dof[i] < 0) ? (-1) : (1);
|
||||
int s = (dof[i] < 0) ? (-1) : (1);
|
||||
for (int j = i+1; j < n; j++)
|
||||
{
|
||||
const int t = (dof[j] < 0) ? (-s) : (s);
|
||||
int t = (dof[j] < 0) ? (-s) : (s);
|
||||
if (t < 0)
|
||||
{
|
||||
(*this)(i,j) = -(*this)(i,j);
|
||||
@@ -3021,7 +3017,7 @@ DenseMatrix::~DenseMatrix()
|
||||
{
|
||||
if (capacity > 0)
|
||||
{
|
||||
delete [] data;
|
||||
mfem::Delete(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3031,12 +3027,10 @@ void Add(const DenseMatrix &A, const DenseMatrix &B,
|
||||
double alpha, DenseMatrix &C)
|
||||
{
|
||||
for (int j = 0; j < C.Width(); j++)
|
||||
{
|
||||
for (int i = 0; i < C.Height(); i++)
|
||||
{
|
||||
C(i,j) = A(i,j) + alpha * B(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Add(double alpha, const double *A,
|
||||
@@ -3080,20 +3074,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();
|
||||
for (int i = 0; i < ah*aw; i++)
|
||||
{
|
||||
ad[i] = 0.0;
|
||||
}
|
||||
for (int j = 0; j < aw; j++)
|
||||
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 k = 0; k < bw; k++)
|
||||
{
|
||||
for (int i = 0; i < ah; i++)
|
||||
{
|
||||
ad[i+j*ah] += bd[i+k*ah] * cd[k+j*bw];
|
||||
d_ad[i+j*ah] += d_bd[i+k*ah] * d_cd[k+j*bw];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3389,18 +3383,20 @@ void MultAAt(const DenseMatrix &a, DenseMatrix &aat)
|
||||
{
|
||||
const int height = a.Height();
|
||||
const int width = a.Width();
|
||||
for (int i = 0; i < height; i++)
|
||||
const DeviceVector d_a(a.GetData());
|
||||
DeviceVector d_aat(aat.GetData());
|
||||
MFEM_FORALL(i, height,
|
||||
{
|
||||
for (int j = 0; j <= i; j++)
|
||||
for (int j=0; j<=i; j++)
|
||||
{
|
||||
double temp = 0.;
|
||||
for (int k = 0; k < width; k++)
|
||||
double temp = 0.0;
|
||||
for (int k=0; k<width; k++)
|
||||
{
|
||||
temp += a(i,k) * a(j,k);
|
||||
temp += d_a[i+k*height] * d_a[j+k*height];
|
||||
}
|
||||
aat(j,i) = aat(i,j) = temp;
|
||||
d_aat[j+i*height] = d_aat[i+j*height] = temp;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AddMultADAt(const DenseMatrix &A, const Vector &D, DenseMatrix &ADAt)
|
||||
@@ -3807,7 +3803,6 @@ void AddMult_a_AAt(double a, const DenseMatrix &A, DenseMatrix &AAt)
|
||||
void Mult_a_AAt(double a, const DenseMatrix &A, DenseMatrix &AAt)
|
||||
{
|
||||
for (int i = 0; i < A.Height(); i++)
|
||||
{
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
double d = 0.;
|
||||
@@ -3817,22 +3812,22 @@ void Mult_a_AAt(double a, const DenseMatrix &A, DenseMatrix &AAt)
|
||||
}
|
||||
AAt(i, j) = AAt(j, i) = a * d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultVVt(const Vector &v, DenseMatrix &vvt)
|
||||
{
|
||||
for (int i = 0; i < v.Size(); i++)
|
||||
{
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
vvt(i,j) = vvt(j,i) = v(i) * v(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt)
|
||||
{
|
||||
int i, j;
|
||||
double vi;
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
if (v.Size() != VWt.Height() || w.Size() != VWt.Width())
|
||||
{
|
||||
@@ -3840,10 +3835,10 @@ void MultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt)
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < v.Size(); i++)
|
||||
for (i = 0; i < v.Size(); i++)
|
||||
{
|
||||
const double vi = v(i);
|
||||
for (int j = 0; j < w.Size(); j++)
|
||||
vi = v(i);
|
||||
for (j = 0; j < w.Size(); j++)
|
||||
{
|
||||
VWt(i, j) = vi * w(j);
|
||||
}
|
||||
@@ -3852,7 +3847,7 @@ void MultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt)
|
||||
|
||||
void AddMultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt)
|
||||
{
|
||||
const int m = v.Size(), n = w.Size();
|
||||
int m = v.Size(), n = w.Size();
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
if (VWt.Height() != m || VWt.Width() != n)
|
||||
@@ -3863,7 +3858,7 @@ void AddMultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt)
|
||||
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
const double vi = v(i);
|
||||
double vi = v(i);
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
VWt(i, j) += vi * w(j);
|
||||
@@ -3873,7 +3868,7 @@ void AddMultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt)
|
||||
|
||||
void AddMultVVt(const Vector &v, DenseMatrix &VVt)
|
||||
{
|
||||
const int n = v.Size();
|
||||
int n = v.Size();
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
if (VVt.Height() != n || VVt.Width() != n)
|
||||
@@ -3884,21 +3879,21 @@ void AddMultVVt(const Vector &v, DenseMatrix &VVt)
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
const double vi = v(i);
|
||||
double vi = v(i);
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
const double vivj = vi * v(j);
|
||||
double vivj = vi * v(j);
|
||||
VVt(i, j) += vivj;
|
||||
VVt(j, i) += vivj;
|
||||
}
|
||||
VVt(i, i) += vi * vi;
|
||||
VVt(i, i) += vi * v(i);
|
||||
}
|
||||
}
|
||||
|
||||
void AddMult_a_VWt(const double a, const Vector &v, const Vector &w,
|
||||
DenseMatrix &VWt)
|
||||
{
|
||||
const int m = v.Size(), n = w.Size();
|
||||
int m = v.Size(), n = w.Size();
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
if (VWt.Height() != m || VWt.Width() != n)
|
||||
@@ -3919,21 +3914,28 @@ void AddMult_a_VWt(const double a, const Vector &v, const Vector &w,
|
||||
|
||||
void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt)
|
||||
{
|
||||
MFEM_ASSERT(VVt.Height() == v.Size() && VVt.Width() == v.Size(),
|
||||
"incompatible dimensions!");
|
||||
int n = v.Size();
|
||||
|
||||
const int n = v.Size();
|
||||
for (int i = 0; i < n; i++)
|
||||
#ifdef MFEM_DEBUG
|
||||
if (VVt.Height() != n || VVt.Width() != n)
|
||||
{
|
||||
double avi = a * v(i);
|
||||
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,
|
||||
{
|
||||
double avi = a * d_v[i];
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
const double avivj = avi * v(j);
|
||||
VVt(i, j) += avivj;
|
||||
VVt(j, i) += avivj;
|
||||
double avivj = avi * d_v[j];
|
||||
d_VVt[i+j*height] += avivj;
|
||||
d_VVt[j+i*height] += avivj;
|
||||
}
|
||||
VVt(i, i) += avi * v(i);
|
||||
}
|
||||
d_VVt[i+i*height] += avi * d_v[i];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3945,47 +3947,49 @@ void LUFactors::Factor(int m)
|
||||
MFEM_VERIFY(!info, "LAPACK: error in DGETRF");
|
||||
#else
|
||||
// compiling without LAPACK
|
||||
double *data = this->data;
|
||||
for (int i = 0; i < m; i++)
|
||||
DeviceArray d_ipiv(ipiv);
|
||||
DeviceVector d_data(data);
|
||||
MFEM_FORALL(i, m,
|
||||
{
|
||||
// pivoting
|
||||
{
|
||||
int piv = i;
|
||||
double a = std::abs(data[piv+i*m]);
|
||||
double a = fabs(d_data[piv+i*m]);
|
||||
for (int j = i+1; j < m; j++)
|
||||
{
|
||||
const double b = std::abs(data[j+i*m]);
|
||||
const double b = fabs(d_data[j+i*m]);
|
||||
if (b > a)
|
||||
{
|
||||
a = b;
|
||||
piv = j;
|
||||
}
|
||||
}
|
||||
ipiv[i] = piv;
|
||||
d_ipiv[i] = piv;
|
||||
if (piv != i)
|
||||
{
|
||||
// swap rows i and piv in both L and U parts
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
Swap<double>(data[i+j*m], data[piv+j*m]);
|
||||
const double tmp = d_data[i+j*m];
|
||||
d_data[i+j*m] = d_data[piv+j*m];
|
||||
d_data[piv+j*m] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(data[i+i*m] != 0.0, "division by zero");
|
||||
const double a_ii_inv = 1.0/data[i+i*m];
|
||||
const double a_ii_inv = 1.0/d_data[i+i*m];
|
||||
for (int j = i+1; j < m; j++)
|
||||
{
|
||||
data[j+i*m] *= a_ii_inv;
|
||||
d_data[j+i*m] *= a_ii_inv;
|
||||
}
|
||||
for (int k = i+1; k < m; k++)
|
||||
{
|
||||
const double a_ik = data[i+k*m];
|
||||
const double a_ik = d_data[i+k*m];
|
||||
for (int j = i+1; j < m; j++)
|
||||
{
|
||||
data[j+k*m] -= a_ik * data[j+i*m];
|
||||
d_data[j+k*m] -= a_ik * d_data[j+i*m];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4047,24 +4051,29 @@ void LUFactors::LSolve(int m, int n, double *X) const
|
||||
const double *data = this->data;
|
||||
const int *ipiv = this->ipiv;
|
||||
double *x = X;
|
||||
for (int k = 0; k < n; k++)
|
||||
const DeviceVector d_data(data);
|
||||
const DeviceArray d_ipiv(ipiv);
|
||||
DeviceVector d_x(x);
|
||||
MFEM_FORALL(k, n,
|
||||
{
|
||||
double *d_mx = &d_x[k*m];
|
||||
// X <- P X
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
Swap<double>(x[i], x[ipiv[i]-ipiv_base]);
|
||||
const double tmp = d_mx[i];
|
||||
d_mx[i] = d_mx[d_ipiv[i]];
|
||||
d_mx[d_ipiv[i]] = tmp;
|
||||
}
|
||||
// X <- L^{-1} X
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
const double x_j = x[j];
|
||||
const double d_mx_j = d_mx[j];
|
||||
for (int i = j+1; i < m; i++)
|
||||
{
|
||||
x[i] -= data[i+j*m] * x_j;
|
||||
d_mx[i] -= d_data[i+j*m] * d_mx_j;
|
||||
}
|
||||
}
|
||||
x += m;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LUFactors::USolve(int m, int n, double *X) const
|
||||
@@ -4072,18 +4081,20 @@ void LUFactors::USolve(int m, int n, double *X) const
|
||||
const double *data = this->data;
|
||||
double *x = X;
|
||||
// X <- U^{-1} X
|
||||
for (int k = 0; k < n; k++)
|
||||
const DeviceVector d_data(data);
|
||||
DeviceVector d_x(x);
|
||||
MFEM_FORALL(k, n,
|
||||
{
|
||||
double *d_mx = &d_x[k*m];
|
||||
for (int j = m-1; j >= 0; j--)
|
||||
{
|
||||
const double x_j = ( x[j] /= data[j+j*m] );
|
||||
const double x_j = ( d_mx[j] /= d_data[j+j*m] );
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
x[i] -= data[i+j*m] * x_j;
|
||||
d_mx[i] -= d_data[i+j*m] * x_j;
|
||||
}
|
||||
}
|
||||
x += m;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LUFactors::Solve(int m, int n, double *X) const
|
||||
@@ -4233,8 +4244,8 @@ DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix &mat)
|
||||
{
|
||||
MFEM_ASSERT(height == width, "not a square matrix");
|
||||
a = &mat;
|
||||
lu.data = new double[width*width];
|
||||
lu.ipiv = new int[width];
|
||||
lu.data = mfem::New<double>(width*width);
|
||||
lu.ipiv = mfem::New<int>(width);
|
||||
Factor();
|
||||
}
|
||||
|
||||
@@ -4243,19 +4254,21 @@ DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix *mat)
|
||||
{
|
||||
MFEM_ASSERT(height == width, "not a square matrix");
|
||||
a = mat;
|
||||
lu.data = new double[width*width];
|
||||
lu.ipiv = new int[width];
|
||||
lu.data = mfem::New<double>(width*width);
|
||||
lu.ipiv = mfem::New<int>(width);
|
||||
}
|
||||
|
||||
void DenseMatrixInverse::Factor()
|
||||
{
|
||||
MFEM_ASSERT(a, "DenseMatrix is not given");
|
||||
const double *adata = a->data;
|
||||
const int s = width*width;
|
||||
for (int i = 0; i < s; i++)
|
||||
const int N = width*width;
|
||||
const DeviceVector d_data(adata);
|
||||
DeviceVector d_ludata(lu.data);
|
||||
MFEM_FORALL(i, N,
|
||||
{
|
||||
lu.data[i] = adata[i];
|
||||
}
|
||||
d_ludata[i] = d_data[i];
|
||||
});
|
||||
lu.Factor(width);
|
||||
}
|
||||
|
||||
@@ -4271,10 +4284,10 @@ void DenseMatrixInverse::Factor(const DenseMatrix &mat)
|
||||
if (width != mat.width)
|
||||
{
|
||||
height = width = mat.width;
|
||||
delete [] lu.data;
|
||||
lu.data = new double[width*width];
|
||||
delete [] lu.ipiv;
|
||||
lu.ipiv = new int[width];
|
||||
mfem::Delete(lu.data);
|
||||
lu.data = mfem::New<double>(width*width);
|
||||
mfem::Delete(lu.ipiv);
|
||||
lu.ipiv = mfem::New<int>(width);
|
||||
}
|
||||
a = &mat;
|
||||
Factor();
|
||||
@@ -4312,8 +4325,8 @@ void DenseMatrixInverse::TestInversion()
|
||||
|
||||
DenseMatrixInverse::~DenseMatrixInverse()
|
||||
{
|
||||
delete [] lu.data;
|
||||
delete [] lu.ipiv;
|
||||
mfem::Delete(lu.data);
|
||||
mfem::Delete(lu.ipiv);
|
||||
}
|
||||
|
||||
|
||||
@@ -4455,8 +4468,7 @@ const
|
||||
{
|
||||
int n = SizeI(), ne = SizeK();
|
||||
const int *I = elem_dof.GetI(), *J = elem_dof.GetJ(), *dofs;
|
||||
const double *d_col = tdata;
|
||||
double *yp = y, x_col;
|
||||
double *d_col = tdata, *yp = y, x_col;
|
||||
const double *xp = x;
|
||||
// the '4' here can be tuned for given platform and compiler
|
||||
if (n <= 4)
|
||||
|
||||
+28
-27
@@ -53,11 +53,10 @@ public:
|
||||
/// Creates rectangular matrix equal to the transpose of mat.
|
||||
DenseMatrix(const DenseMatrix &mat, char ch);
|
||||
|
||||
/// Construct a DenseMatrix using an 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)
|
||||
: Matrix(h, w) { UseExternalData(d, h, w); }
|
||||
/** 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);
|
||||
|
||||
/// Change the data array and the size of the DenseMatrix.
|
||||
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
|
||||
@@ -71,7 +70,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()) { delete [] data; } UseExternalData(d, h, w); }
|
||||
{ if (OwnsData()) { mfem::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. */
|
||||
@@ -79,7 +78,7 @@ public:
|
||||
|
||||
/// Delete the matrix data array (if owned) and reset the matrix state.
|
||||
void Clear()
|
||||
{ if (OwnsData()) { delete [] data; } ClearExternalData(); }
|
||||
{ if (OwnsData()) { mfem::Delete(data); } ClearExternalData(); }
|
||||
|
||||
/// For backward compatibility define Size to be synonym of Width()
|
||||
int Size() const { return Width(); }
|
||||
@@ -326,7 +325,7 @@ public:
|
||||
/// Perform (ro+i,co+j)+=A(i,j) for 0<=i<A.Height, 0<=j<A.Width
|
||||
void AddMatrix(DenseMatrix &A, int ro, int co);
|
||||
/// Perform (ro+i,co+j)+=a*A(i,j) for 0<=i<A.Height, 0<=j<A.Width
|
||||
void AddMatrix(double a, const DenseMatrix &A, int ro, int co);
|
||||
void AddMatrix(double a, DenseMatrix &A, int ro, int co);
|
||||
|
||||
/// Add the matrix 'data' to the Vector 'v' at the given 'offset'
|
||||
void AddToVector(int offset, Vector &v) const;
|
||||
@@ -657,36 +656,39 @@ class DenseTensor
|
||||
{
|
||||
private:
|
||||
DenseMatrix Mk;
|
||||
Memory<double> tdata;
|
||||
double *tdata;
|
||||
int nk;
|
||||
bool own_data;
|
||||
|
||||
public:
|
||||
DenseTensor()
|
||||
{
|
||||
nk = 0;
|
||||
tdata.Reset();
|
||||
tdata = NULL;
|
||||
own_data = true;
|
||||
}
|
||||
|
||||
DenseTensor(int i, int j, int k)
|
||||
: Mk(NULL, i, j)
|
||||
{
|
||||
nk = k;
|
||||
tdata.New(i*j*k);
|
||||
tdata = mfem::New<double>(i*j*k);
|
||||
own_data = true;
|
||||
}
|
||||
|
||||
/// Copy constructor: deep copy
|
||||
DenseTensor(const DenseTensor &other)
|
||||
: Mk(NULL, other.Mk.height, other.Mk.width), nk(other.nk)
|
||||
DenseTensor(const DenseTensor& other)
|
||||
: Mk(NULL, other.Mk.height, other.Mk.width), nk(other.nk), own_data(true)
|
||||
{
|
||||
const int size = Mk.Height()*Mk.Width()*nk;
|
||||
if (size > 0)
|
||||
{
|
||||
tdata.New(size, other.tdata.GetMemoryType());
|
||||
tdata.CopyFrom(other.tdata, size);
|
||||
tdata = mfem::New<double>(size);
|
||||
mfem::Memcpy(tdata, other.tdata, sizeof(double) * size);
|
||||
}
|
||||
else
|
||||
{
|
||||
tdata.Reset();
|
||||
tdata = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,23 +696,22 @@ public:
|
||||
int SizeJ() const { return Mk.Width(); }
|
||||
int SizeK() const { return nk; }
|
||||
|
||||
int TotalSize() const { return SizeI()*SizeJ()*SizeK(); }
|
||||
|
||||
void SetSize(int i, int j, int k)
|
||||
{
|
||||
const MemoryType mt = tdata.GetMemoryType();
|
||||
tdata.Delete();
|
||||
if (own_data) { mfem::Delete(tdata); }
|
||||
Mk.UseExternalData(NULL, i, j);
|
||||
nk = k;
|
||||
tdata.New(i*j*k, mt);
|
||||
tdata = mfem::New<double>(i*j*k);
|
||||
own_data = true;
|
||||
}
|
||||
|
||||
void UseExternalData(double *ext_data, int i, int j, int k)
|
||||
{
|
||||
tdata.Delete();
|
||||
if (own_data) { mfem::Delete(tdata); }
|
||||
Mk.UseExternalData(NULL, i, j);
|
||||
nk = k;
|
||||
tdata.Wrap(ext_data, i*j*k, false);
|
||||
tdata = ext_data;
|
||||
own_data = false;
|
||||
}
|
||||
|
||||
/// Sets the tensor elements equal to constant c
|
||||
@@ -731,9 +732,6 @@ public:
|
||||
|
||||
const double *Data() const { return tdata; }
|
||||
|
||||
Memory<double> &GetMemory() { return tdata; }
|
||||
const Memory<double> &GetMemory() 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 +741,10 @@ public:
|
||||
|
||||
long MemoryUsage() const { return nk*Mk.MemoryUsage(); }
|
||||
|
||||
~DenseTensor() { tdata.Delete(); }
|
||||
~DenseTensor()
|
||||
{
|
||||
if (own_data) { mfem::Delete(tdata); }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
+26
-18
@@ -23,7 +23,7 @@ template <int N, int Dim, typename T, typename... Args>
|
||||
class TensorInd
|
||||
{
|
||||
public:
|
||||
MFEM_HOST_DEVICE
|
||||
MFEM_ATTR_HOST_DEVICE
|
||||
static inline int result(const int* sizes, T first, Args... args)
|
||||
{
|
||||
#ifndef MFEM_USE_CUDA
|
||||
@@ -39,7 +39,7 @@ template <int Dim, typename T, typename... Args>
|
||||
class TensorInd<Dim, Dim, T, Args...>
|
||||
{
|
||||
public:
|
||||
MFEM_HOST_DEVICE
|
||||
MFEM_ATTR_HOST_DEVICE
|
||||
static inline int result(const int* sizes, T first, Args... args)
|
||||
{
|
||||
#ifndef MFEM_USE_CUDA
|
||||
@@ -96,13 +96,32 @@ public:
|
||||
// Initialize sizes, and compute the number of values
|
||||
const long int nb = Init<1, Dim, Args...>::result(sizes, args...);
|
||||
capacity = nb;
|
||||
data = (capacity > 0) ? _data : NULL;
|
||||
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_HOST_DEVICE DeviceTensor(const DeviceTensor& t)
|
||||
MFEM_ATTR_HOST_DEVICE DeviceTensor(const DeviceTensor& t)
|
||||
{
|
||||
capacity = t.capacity;
|
||||
for (int i = 0; i < Dim; ++i)
|
||||
{
|
||||
sizes[i] = t.sizes[i];
|
||||
@@ -114,30 +133,19 @@ public:
|
||||
inline operator Scalar *() const { return data; }
|
||||
|
||||
/// Const accessor for the data
|
||||
template <typename... Args> MFEM_HOST_DEVICE inline
|
||||
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...) ];
|
||||
}
|
||||
|
||||
/// Subscript operator where the tensor is viewed as a 1D array.
|
||||
MFEM_HOST_DEVICE inline Scalar& operator[](int i) const
|
||||
MFEM_ATTR_HOST_DEVICE inline Scalar& operator[](int i) const
|
||||
{
|
||||
return data[i];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @brief Wrap a pointer as a DeviceTensor with automatically deduced template
|
||||
parameters */
|
||||
template <typename T, typename... Dims>
|
||||
inline DeviceTensor<sizeof...(Dims),T> Reshape(T *ptr, Dims... dims)
|
||||
{
|
||||
return DeviceTensor<sizeof...(Dims),T>(ptr, dims...);
|
||||
}
|
||||
|
||||
|
||||
typedef DeviceTensor<1,int> DeviceArray;
|
||||
typedef DeviceTensor<1,double> DeviceVector;
|
||||
typedef DeviceTensor<2,double> DeviceMatrix;
|
||||
|
||||
+31
-76
@@ -79,7 +79,7 @@ template<typename TargetT, typename SourceT>
|
||||
static TargetT *DuplicateAs(const SourceT *array, int size,
|
||||
bool cplusplus = true)
|
||||
{
|
||||
TargetT *target_array = cplusplus ? new TargetT[size]
|
||||
TargetT *target_array = cplusplus ? mfem::New<TargetT>(size)
|
||||
/* */ : mfem_hypre_TAlloc(TargetT, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
@@ -207,8 +207,7 @@ HypreParVector& HypreParVector::operator=(const HypreParVector &y)
|
||||
|
||||
void HypreParVector::SetData(double *_data)
|
||||
{
|
||||
hypre_VectorData(hypre_ParVectorLocalVector(x)) = _data;
|
||||
Vector::SetData(_data);
|
||||
Vector::data = hypre_VectorData(hypre_ParVectorLocalVector(x)) = _data;
|
||||
}
|
||||
|
||||
HYPRE_Int HypreParVector::Randomize(HYPRE_Int seed)
|
||||
@@ -642,13 +641,13 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm, int id, int np,
|
||||
|
||||
HYPRE_Int i;
|
||||
|
||||
double *a_diag = new double[diag_nnz];
|
||||
double *a_diag = mfem::New<double>(diag_nnz);
|
||||
for (i = 0; i < diag_nnz; i++)
|
||||
{
|
||||
a_diag[i] = 1.0;
|
||||
}
|
||||
|
||||
double *a_offd = new double[offd_nnz];
|
||||
double *a_offd = mfem::New<double>(offd_nnz);
|
||||
for (i = 0; i < offd_nnz; i++)
|
||||
{
|
||||
a_offd[i] = 1.0;
|
||||
@@ -1011,36 +1010,37 @@ 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()
|
||||
<< ", expected size = " << Height());
|
||||
|
||||
auto x_data = x.HostRead();
|
||||
auto y_data = y.HostWrite();
|
||||
if (X == NULL)
|
||||
{
|
||||
X = new HypreParVector(A->comm,
|
||||
GetGlobalNumCols(),
|
||||
const_cast<double*>(x_data),
|
||||
x.GetData(),
|
||||
GetColStarts());
|
||||
Y = new HypreParVector(A->comm,
|
||||
GetGlobalNumRows(),
|
||||
y_data,
|
||||
y.GetData(),
|
||||
GetRowStarts());
|
||||
}
|
||||
else
|
||||
{
|
||||
X->SetData(const_cast<double*>(x_data));
|
||||
Y->SetData(y_data);
|
||||
X->SetData(x.GetData());
|
||||
Y->SetData(y.GetData());
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -1048,26 +1048,25 @@ void HypreParMatrix::MultTranspose(double a, const Vector &x,
|
||||
|
||||
// Note: x has the dimensions of Y (height), and
|
||||
// y has the dimensions of X (width)
|
||||
auto x_data = x.HostRead();
|
||||
auto y_data = y.HostWrite();
|
||||
if (X == NULL)
|
||||
{
|
||||
X = new HypreParVector(A->comm,
|
||||
GetGlobalNumCols(),
|
||||
y_data,
|
||||
y.GetData(),
|
||||
GetColStarts());
|
||||
Y = new HypreParVector(A->comm,
|
||||
GetGlobalNumRows(),
|
||||
const_cast<double*>(x_data),
|
||||
x.GetData(),
|
||||
GetRowStarts());
|
||||
}
|
||||
else
|
||||
{
|
||||
X->SetData(y_data);
|
||||
Y->SetData(const_cast<double*>(x_data));
|
||||
X->SetData(y.GetData());
|
||||
Y->SetData(x.GetData());
|
||||
}
|
||||
|
||||
hypre_ParCSRMatrixMatvecT(a, A, *Y, b, *X);
|
||||
y.Push();
|
||||
}
|
||||
|
||||
HYPRE_Int HypreParMatrix::Mult(HYPRE_ParVector x, HYPRE_ParVector y,
|
||||
@@ -1296,7 +1295,7 @@ static void get_sorted_rows_cols(const Array<int> &rows_cols,
|
||||
|
||||
void HypreParMatrix::Threshold(double threshold)
|
||||
{
|
||||
int ierr = 0;
|
||||
int ierr = 0;
|
||||
|
||||
MPI_Comm comm;
|
||||
hypre_CSRMatrix * csr_A;
|
||||
@@ -1347,7 +1346,7 @@ void HypreParMatrix::Threshold(double threshold)
|
||||
ierr += hypre_CSRMatrixDestroy(csr_A);
|
||||
}
|
||||
|
||||
/* TODO: GenerateDiagAndOffd() uses an int array of size equal to the number
|
||||
/* FIXME: GenerateDiagAndOffd() uses an int array of size equal to the number
|
||||
of columns in csr_A_wo_z which is the global number of columns in A. This
|
||||
does not scale well. */
|
||||
ierr += GenerateDiagAndOffd(csr_A_wo_z,parcsr_A_ptr,
|
||||
@@ -1482,14 +1481,14 @@ void HypreParMatrix::Destroy()
|
||||
{
|
||||
if (diagOwner & 1)
|
||||
{
|
||||
delete [] hypre_CSRMatrixI(A->diag);
|
||||
delete [] hypre_CSRMatrixJ(A->diag);
|
||||
mfem::Delete(hypre_CSRMatrixI(A->diag));
|
||||
mfem::Delete(hypre_CSRMatrixJ(A->diag));
|
||||
}
|
||||
hypre_CSRMatrixI(A->diag) = NULL;
|
||||
hypre_CSRMatrixJ(A->diag) = NULL;
|
||||
if (diagOwner & 2)
|
||||
{
|
||||
delete [] hypre_CSRMatrixData(A->diag);
|
||||
mfem::Delete(hypre_CSRMatrixData(A->diag));
|
||||
}
|
||||
hypre_CSRMatrixData(A->diag) = NULL;
|
||||
}
|
||||
@@ -1497,14 +1496,14 @@ void HypreParMatrix::Destroy()
|
||||
{
|
||||
if (offdOwner & 1)
|
||||
{
|
||||
delete [] hypre_CSRMatrixI(A->offd);
|
||||
delete [] hypre_CSRMatrixJ(A->offd);
|
||||
mfem::Delete(hypre_CSRMatrixI(A->offd));
|
||||
mfem::Delete(hypre_CSRMatrixJ(A->offd));
|
||||
}
|
||||
hypre_CSRMatrixI(A->offd) = NULL;
|
||||
hypre_CSRMatrixJ(A->offd) = NULL;
|
||||
if (offdOwner & 2)
|
||||
{
|
||||
delete [] hypre_CSRMatrixData(A->offd);
|
||||
mfem::Delete(hypre_CSRMatrixData(A->offd));
|
||||
}
|
||||
hypre_CSRMatrixData(A->offd) = NULL;
|
||||
}
|
||||
@@ -1512,7 +1511,7 @@ void HypreParMatrix::Destroy()
|
||||
{
|
||||
if (colMapOwner & 1)
|
||||
{
|
||||
delete [] hypre_ParCSRMatrixColMapOffd(A);
|
||||
mfem::Delete(hypre_ParCSRMatrixColMapOffd(A));
|
||||
}
|
||||
hypre_ParCSRMatrixColMapOffd(A) = NULL;
|
||||
}
|
||||
@@ -2097,7 +2096,6 @@ HypreSolver::HypreSolver()
|
||||
A = NULL;
|
||||
setup_called = 0;
|
||||
B = X = NULL;
|
||||
error_mode = ABORT_HYPRE_ERRORS;
|
||||
}
|
||||
|
||||
HypreSolver::HypreSolver(HypreParMatrix *_A)
|
||||
@@ -2106,12 +2104,10 @@ HypreSolver::HypreSolver(HypreParMatrix *_A)
|
||||
A = _A;
|
||||
setup_called = 0;
|
||||
B = X = NULL;
|
||||
error_mode = ABORT_HYPRE_ERRORS;
|
||||
}
|
||||
|
||||
void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
{
|
||||
HYPRE_Int err;
|
||||
if (A == NULL)
|
||||
{
|
||||
mfem_error("HypreSolver::Mult (...) : HypreParMatrix A is missing");
|
||||
@@ -2119,16 +2115,7 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
}
|
||||
if (!setup_called)
|
||||
{
|
||||
err = SetupFcn()(*this, *A, b, x);
|
||||
if (error_mode == WARN_HYPRE_ERRORS)
|
||||
{
|
||||
if (err) { MFEM_WARNING("Error during setup! Error code: " << err); }
|
||||
}
|
||||
else if (error_mode == ABORT_HYPRE_ERRORS)
|
||||
{
|
||||
MFEM_VERIFY(!err, "Error during setup! Error code: " << err);
|
||||
}
|
||||
hypre_error_flag = 0;
|
||||
SetupFcn()(*this, *A, b, x);
|
||||
setup_called = 1;
|
||||
}
|
||||
|
||||
@@ -2136,16 +2123,7 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
{
|
||||
x = 0.0;
|
||||
}
|
||||
err = SolveFcn()(*this, *A, b, x);
|
||||
if (error_mode == WARN_HYPRE_ERRORS)
|
||||
{
|
||||
if (err) { MFEM_WARNING("Error during solve! Error code: " << err); }
|
||||
}
|
||||
else if (error_mode == ABORT_HYPRE_ERRORS)
|
||||
{
|
||||
MFEM_VERIFY(!err, "Error during solve! Error code: " << err);
|
||||
}
|
||||
hypre_error_flag = 0;
|
||||
SolveFcn()(*this, *A, b, x);
|
||||
}
|
||||
|
||||
void HypreSolver::Mult(const Vector &b, Vector &x) const
|
||||
@@ -2155,23 +2133,21 @@ void HypreSolver::Mult(const Vector &b, Vector &x) const
|
||||
mfem_error("HypreSolver::Mult (...) : HypreParMatrix A is missing");
|
||||
return;
|
||||
}
|
||||
auto b_data = b.HostRead();
|
||||
auto x_data = x.HostWrite();
|
||||
if (B == NULL)
|
||||
{
|
||||
B = new HypreParVector(A->GetComm(),
|
||||
A -> GetGlobalNumRows(),
|
||||
const_cast<double*>(b_data),
|
||||
b.GetData(),
|
||||
A -> GetRowStarts());
|
||||
X = new HypreParVector(A->GetComm(),
|
||||
A -> GetGlobalNumCols(),
|
||||
x_data,
|
||||
x.GetData(),
|
||||
A -> GetColStarts());
|
||||
}
|
||||
else
|
||||
{
|
||||
B -> SetData(const_cast<double*>(b_data));
|
||||
X -> SetData(x_data);
|
||||
B -> SetData(b.GetData());
|
||||
X -> SetData(x.GetData());
|
||||
}
|
||||
|
||||
Mult(*B, *X);
|
||||
@@ -2281,9 +2257,6 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
x = 0.0;
|
||||
}
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
|
||||
HYPRE_ParCSRPCGSolve(pcg_solver, *A, b, x);
|
||||
|
||||
if (print_level > 0)
|
||||
@@ -2754,12 +2727,6 @@ void HypreBoomerAMG::SetElasticityOptions(ParFiniteElementSpace *fespace)
|
||||
|
||||
RecomputeRBMs();
|
||||
HYPRE_BoomerAMGSetInterpVectors(amg_precond, rbms.Size(), rbms.GetData());
|
||||
|
||||
// The above BoomerAMG options may result in singular matrices on the coarse
|
||||
// grids, which are handled correctly in hypre's Solve method, but can produce
|
||||
// hypre errors in the Setup (specifically in the l1 row norm computation).
|
||||
// See the documentation of SetErrorMode() for more details.
|
||||
error_mode = IGNORE_HYPRE_ERRORS;
|
||||
}
|
||||
|
||||
HypreBoomerAMG::~HypreBoomerAMG()
|
||||
@@ -2950,12 +2917,6 @@ HypreAMS::HypreAMS(HypreParMatrix &A, ParFiniteElementSpace *edge_fespace)
|
||||
theta, amg_interp_type, amg_Pmax);
|
||||
HYPRE_AMSSetBetaAMGOptions(ams, amg_coarsen_type, amg_agg_levels, amg_rlx_type,
|
||||
theta, amg_interp_type, amg_Pmax);
|
||||
|
||||
// The AMS preconditioner may sometimes require inverting singular matrices
|
||||
// with BoomerAMG, which are handled correctly in hypre's Solve method, but
|
||||
// can produce hypre errors in the Setup (specifically in the l1 row norm
|
||||
// computation). See the documentation of SetErrorMode() for more details.
|
||||
error_mode = IGNORE_HYPRE_ERRORS;
|
||||
}
|
||||
|
||||
HypreAMS::~HypreAMS()
|
||||
@@ -3194,12 +3155,6 @@ HypreADS::HypreADS(HypreParMatrix &A, ParFiniteElementSpace *face_fespace)
|
||||
theta, amg_interp_type, amg_Pmax);
|
||||
HYPRE_ADSSetAMSOptions(ads, ams_cycle_type, amg_coarsen_type, amg_agg_levels,
|
||||
amg_rlx_type, theta, amg_interp_type, amg_Pmax);
|
||||
|
||||
// The ADS preconditioner requires inverting singular matrices with BoomerAMG,
|
||||
// which are handled correctly in hypre's Solve method, but can produce hypre
|
||||
// errors in the Setup (specifically in the l1 row norm computation). See the
|
||||
// documentation of SetErrorMode() for more details.
|
||||
error_mode = IGNORE_HYPRE_ERRORS;
|
||||
}
|
||||
|
||||
HypreADS::~HypreADS()
|
||||
|
||||
+8
-36
@@ -451,18 +451,16 @@ public:
|
||||
|
||||
/** The "Boolean" analog of y = alpha * A * x + beta * y, where elements in
|
||||
the sparsity pattern of the matrix are treated as "true". */
|
||||
void BooleanMult(int alpha, const int *x, int beta, int *y)
|
||||
void BooleanMult(int alpha, int *x, int beta, int *y)
|
||||
{
|
||||
internal::hypre_ParCSRMatrixBooleanMatvec(A, alpha, const_cast<int*>(x),
|
||||
beta, y);
|
||||
internal::hypre_ParCSRMatrixBooleanMatvec(A, alpha, x, beta, y);
|
||||
}
|
||||
|
||||
/** The "Boolean" analog of y = alpha * A^T * x + beta * y, where elements in
|
||||
the sparsity pattern of the matrix are treated as "true". */
|
||||
void BooleanMultTranspose(int alpha, const int *x, int beta, int *y)
|
||||
void BooleanMultTranspose(int alpha, int *x, int beta, int *y)
|
||||
{
|
||||
internal::hypre_ParCSRMatrixBooleanMatvecT(A, alpha, const_cast<int*>(x),
|
||||
beta, y);
|
||||
internal::hypre_ParCSRMatrixBooleanMatvecT(A, alpha, x, beta, y);
|
||||
}
|
||||
|
||||
/// Initialize all entries with value.
|
||||
@@ -470,16 +468,13 @@ public:
|
||||
{ internal::hypre_ParCSRMatrixSetConstantValues(A, value); return *this; }
|
||||
|
||||
/** Perform the operation `*this += B`, assuming that both matrices use the
|
||||
same row and column partitions and the same col_map_offd arrays, or B has
|
||||
an empty off-diagonal block. We also assume that the sparsity pattern of
|
||||
`*this` contains that of `B`. */
|
||||
same row and column partitions and the same col_map_offd arrays. We also
|
||||
assume that the sparsity pattern of `*this` contains that of `B`. */
|
||||
HypreParMatrix &operator+=(const HypreParMatrix &B) { return Add(1.0, B); }
|
||||
|
||||
/** Perform the operation `*this += beta*B`, assuming that both matrices use
|
||||
the same row and column partitions and the same col_map_offd arrays, or
|
||||
B has an empty off-diagonal block. We also assume that the sparsity
|
||||
pattern of `*this` contains that of `B`. For a more general case consider
|
||||
the stand-alone function ParAdd described below. */
|
||||
the same row and column partitions and the same col_map_offd arrays. We
|
||||
also assume that the sparsity pattern of `*this` contains that of `B`. */
|
||||
HypreParMatrix &Add(const double beta, const HypreParMatrix &B)
|
||||
{
|
||||
MFEM_VERIFY(internal::hypre_ParCSRMatrixSum(A, beta, B.A) == 0,
|
||||
@@ -669,15 +664,6 @@ public:
|
||||
/// Abstract class for hypre's solvers and preconditioners
|
||||
class HypreSolver : public Solver
|
||||
{
|
||||
public:
|
||||
/// How to treat errors returned by hypre function calls.
|
||||
enum ErrorMode
|
||||
{
|
||||
IGNORE_HYPRE_ERRORS, ///< Ignore hypre errors (see e.g. HypreADS)
|
||||
WARN_HYPRE_ERRORS, ///< Issue warnings on hypre errors
|
||||
ABORT_HYPRE_ERRORS ///< Abort on hypre errors (default in base class)
|
||||
};
|
||||
|
||||
protected:
|
||||
/// The linear system matrix
|
||||
HypreParMatrix *A;
|
||||
@@ -688,9 +674,6 @@ protected:
|
||||
/// Was hypre's Setup function called already?
|
||||
mutable int setup_called;
|
||||
|
||||
/// How to treat hypre errors.
|
||||
mutable ErrorMode error_mode;
|
||||
|
||||
public:
|
||||
HypreSolver();
|
||||
|
||||
@@ -711,17 +694,6 @@ public:
|
||||
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
|
||||
virtual void Mult(const Vector &b, Vector &x) const;
|
||||
|
||||
/** @brief Set the behavior for treating hypre errors, see the ErrorMode
|
||||
enum. The default mode in the base class is ABORT_HYPRE_ERRORS. */
|
||||
/** Currently, there are three cases in derived classes where the error flag
|
||||
is set to IGNORE_HYPRE_ERRORS:
|
||||
* in the method HypreBoomerAMG::SetElasticityOptions(), and
|
||||
* in the constructor of classes HypreAMS and HypreADS.
|
||||
The reason for this is that a nonzero hypre error is returned) when
|
||||
hypre_ParCSRComputeL1Norms() encounters zero row in a matrix, which is
|
||||
expected in some cases with the above solvers. */
|
||||
void SetErrorMode(ErrorMode err_mode) const { error_mode = err_mode; }
|
||||
|
||||
virtual ~HypreSolver();
|
||||
};
|
||||
|
||||
|
||||
@@ -1484,7 +1484,7 @@ hypre_ParCSRMatrixAdd(hypre_ParCSRMatrix *A,
|
||||
0, 0, 0);
|
||||
|
||||
/* split C into diag and off-diag portions */
|
||||
/* TODO: GenerateDiagAndOffd() uses an int array of size equal to the
|
||||
/* FIXME: GenerateDiagAndOffd() uses an int array of size equal to the
|
||||
number of columns in csr_C_temp which is the global number of columns
|
||||
in A and B. This does not scale well. */
|
||||
ierr += GenerateDiagAndOffd(csr_C_temp, C,
|
||||
|
||||
@@ -115,9 +115,8 @@ hypre_ParCSRMatrixAdd(hypre_ParCSRMatrix *A,
|
||||
hypre_ParCSRMatrix *B);
|
||||
|
||||
/** Perform the operation A += beta*B, assuming that both matrices use the same
|
||||
row and column partitions and the same col_map_offd arrays, or B has an empty
|
||||
off-diagonal block. We also assume that the sparsity pattern of A contains
|
||||
that of B. */
|
||||
row and column partitions and the same col_map_offd arrays. We also assume
|
||||
that the sparsity pattern of A contains that of B. */
|
||||
HYPRE_Int
|
||||
hypre_ParCSRMatrixSum(hypre_ParCSRMatrix *A,
|
||||
HYPRE_Complex beta,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user