Compare commits

..
Author SHA1 Message Date
Yohann Dudouit b9b4a76cf9 Initial commit for Exaconstit using libCEED through MFEM. 2019-11-11 12:13:02 -08:00
Yohann Dudouit 7488a91216 Merge branch 'yohann/okina-gpu-libceed' of github.com:mfem/mfem into yohann/okina-gpu-libceed 2019-11-08 09:54:44 -08:00
Veselin Dobrev e18916ec92 Make sure CMake build works with libceed. In the GNU make build
system, install libceed q-function headers.

Add a runtime check for q-function header location: install/source
directories.
2019-11-07 16:48:55 -08:00
Yohann Dudouit 8decda8986 Add documentation to libCEED functions in MFEM. 2019-11-07 11:29:31 -08:00
Yohann Dudouit e03e973342 Add libCEED to CMake. 2019-11-06 15:38:17 -08:00
Yohann Dudouit 6b8414d820 First set of modifications based on technical meeting.
- guards CEED parts in bilininteg.hpp
- Capitalise some CEED related functions
- Replace Device::IsEnabled with Device::Allows(CUDA)
- Remove dead code in libceed/mass.cpp
2019-11-06 14:24:04 -08:00
Yohann Dudouit 69810197f6 Use enum class CeedCoeff instead of enum. 2019-10-30 12:29:35 -07:00
Yohann Dudouit 2a9cd29202 Remove unused files. 2019-10-28 12:23:47 -07:00
Yohann Dudouit b1784fc793 Replaces void* by CeedData* in Mass and Diffusion integrators. 2019-10-24 16:52:51 -07:00
Yohann Dudouit 4b9971d377 Merge branch 'yohann/okina-gpu-libceed' of github.com:mfem/mfem into yohann/okina-gpu-libceed 2019-10-18 16:47:17 -07:00
Yohann Dudouit 1a6f2226b3 Print the name of the backend used by libCEED and check that it is the requested one. 2019-10-18 16:46:37 -07:00
Yohann 675b507a7c Merge branch 'master' into yohann/okina-gpu-libceed 2019-10-17 15:46:30 -07:00
Yohann Dudouit 3a3bbeae4d Default cpu backend to /cpu/self since it picks automatically the best cpu backend available. 2019-10-17 15:26:37 -07:00
Yohann Dudouit dfc7f9f66d Rename a function to make it self documented. 2019-10-07 17:27:59 -07:00
camierjs d245dc8485 Merge branch 'master' into yohann/okina-gpu-libceed 2019-09-23 10:41:52 -07:00
Veselin Dobrev 5dbf3c8b9f Change the default CPU backend for libCEED. 2019-09-13 15:18:23 -07:00
Yohann Dudouit 407705c8de Define CEED_QFUNCTION. 2019-09-11 18:43:42 -07:00
Yohann Dudouit 0627783135 Add typedef when MFEM_USE_CEED=NO. 2019-09-11 18:22:49 -07:00
Yohann Dudouit c9fc6dfada Move libCEED files in a libceed subdirectory. 2019-09-11 18:00:31 -07:00
Yohann Dudouit c226405378 Remove ceed.hpp file. 2019-09-11 18:00:31 -07:00
Yohann Dudouit c6faec60f8 Cleaning due to Single Source QFunction. 2019-09-11 18:00:31 -07:00
Yohann Dudouit 3cb600cf8b Use correct sizes for In/Outputs of qFunctions. 2019-09-11 18:00:31 -07:00
Yohann Dudouit b610b36173 Update MFEM to support last version of libCEED. 2019-09-11 18:00:31 -07:00
Tzanio 930daccddb minor edits 2019-09-11 18:00:28 -07:00
Tzanio d84b46bc45 make style 2019-09-11 17:58:53 -07:00
Yohann Dudouit 51060626fe Fix a bug in the definition of the restriction for the diffusion op. 2019-09-11 17:57:52 -07:00
Yohann Dudouit ff69c272cc Update documentation of 'Configure' function. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 6bc7fa8216 Add #ifdef MFEM_USE_CEED guards. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 26f1114105 Separate diffusion and mass specific code from libceed.cpp. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 45bbc247a4 Make /gpu/cuda/gen the default ceed-cuda backend. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 0e812c6a01 split qFunctions for diff_apply in three functions. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 177fd7f80d Update .qf file 2019-09-11 17:57:52 -07:00
Yohann Dudouit b64de4db84 Use the Context to apply correctly the qFunction for cuda-gen backend. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 0e6ec8be7e Add qf file for cuda-gen backend. 2019-09-11 17:57:52 -07:00
Yohann Dudouit a09ce63307 libCEED takes into account the integration rule order of MFEM. 2019-09-11 17:57:52 -07:00
Yohann Dudouit 40f4200bb5 Solve double free bug. 2019-09-11 17:57:52 -07:00
camierjs 14c9a845a2 ceed_option fix 2019-09-11 17:57:52 -07:00
camierjs 09a93c96fb Rebase with ex1 working 2019-09-11 17:57:51 -07:00
Yohann Dudouit 9a3d7a34ad Rebase compiles. 2019-09-11 17:57:51 -07:00
Yohann Dudouit 05fcff7325 Move bilininteg_ext into bilininteg_diffusion/mass 2019-09-11 17:57:51 -07:00
Yohann Dudouit 87869636cc gpu debugging 2019-09-11 17:57:51 -07:00
Yohann Dudouit 9aa6823a0b CHANGELOG update 2019-09-11 17:57:51 -07:00
Tzanio 90c4f4fd78 make style 2019-09-11 17:57:51 -07:00
Yohann Dudouit bde675abff Add #ifdef MFEM_USE_CEED guards 2019-09-11 17:57:51 -07:00
Yohann Dudouit 065817c5e1 Remove GetCeed() function 2019-09-11 17:57:49 -07:00
Yohann Dudouit 689c259c80 Add MFEM_USE_CEED option 2019-09-11 17:56:08 -07:00
Yohann Dudouit 05cf4c40b0 Add option to libCEED backend ':' is the option separator.
- example: ./ex1 -pa -d ceed-cpu:/cpu/self/avx/blocked
  - bug: ./ex1 -pa -d ceed-cuda:/gpu/cuda/reg computes 'nan'
  - however, ./ex1 -pa -d ceed-cpu:/gpu/cuda/reg works
  - bug: ./ex1 -pa -d ceed-cpu:/gpu/cuda/ref crashes in libCEED
  - however, ./ex1 -pa -d ceed-cuda:/gpu/cuda/ref works...
2019-09-11 17:54:53 -07:00
Yohann Dudouit 9ddb97e100 Fix GridFunctionCoefficient for GPU backends. 2019-09-11 17:54:53 -07:00
Yohann Dudouit b69a90eb16 Initial commit for okina gpu libceed.
Ceed and Cuda backend working together.

Use MFEM_SOURCE_DIR to find okl and cu files

Separate most of ceed integration in libceed.?pp
2019-09-11 17:54:36 -07:00
108 changed files with 4541 additions and 6384 deletions
+19 -36
View File
@@ -10,48 +10,30 @@
Version 4.0.1 (development)
===========================
- Improved RAJA backend
- Improved multi-GPU MPI communication.
Improved GPU support
--------------------
GPU support
-----------
- Added initial support for AMD GPUs based on HIP: a C++ runtime API and kernel
language that can run on both AMD and NVIDIA hardware. With this change, the
list of backends is: "occa-cuda", "raja-cuda", "cuda", "hip", "occa-omp",
"raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
language that can run on both AMD and NVIDIA hardware. The list of current
backends is: "occa-cuda", "raja-cuda", "cuda", "hip", "occa-omp", "raja-omp",
"omp", "occa-cpu", "raja-cpu", and "cpu".
- Improved RAJA backend and multi-GPU MPI communications.
Discretization improvements
---------------------------
- Added support for non-conforming prism AMR, including coarsening and parallel
load balancing. Anisotropic prism refinement is only available in the serial
version at the moment.
Meshing improvements
--------------------
- The TMOP mesh optimization algorithms were extended to support r-adaptivity.
Target matrices can now be constructed either via a given analytical function
(e.g. spatial dependence of size, aspect ratio, etc., for each element) or via
a (Par)GridFunction specified on the original mesh.
- The TMOP mesh optimization algorithms have been improved to support AMR meshes.
- Added support for creating refined versions of periodic meshes, making use of
the new L2ElementRestriction class. This class also allows for computing
geometric factors on periodic meshes using partial assembly.
- Improved element numbering after uniform mesh refinement.
New and updated examples and miniapps
-------------------------------------
- The mesh-optimizer and pmesh-optimizer miniapps have been updated to
demonstrate the new r-adaptivity capabilities of TMOP.
- The (p)mesh-optimizer miniapp has been updated to demonstrate mesh
optimization for an AMR mesh.
libCEED support
---------------
- Added support for libCEED, the portable library for high-order operator
evaluation developed by the Center for Efficient Exascale Discretizations in
the Exascale Computing Project, https://github.com/CEED/libCEED. This initial
integration includes Mass and Diffusion integrators. libCEED GPU backends can
be used without specific MFEM configuration, however it is highly recommended
to use the "cuda" build option to minimize memory transfers. Both CPU and GPU
modes are available as MFEM device backends (ceed-cpu and ceed-cuda), using
some of the best performing CPU and GPU backends from libCEED.
Miscellaneous
-------------
- Upgraded the SUNDIALS interface to utilize SUNDIALS version 5.0. This
- Upgraded the SUNDIALS interface to utilize SUNDIALS version 5.0. This
necessitated a complete rework of the interface and requires changes at
the application level. Example usage of this new interface can be found
in the examples/sundials directory.
@@ -98,6 +80,7 @@ GPU support
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,
+13 -2
View File
@@ -248,13 +248,17 @@ if (MFEM_USE_MPFR)
find_package(MPFR REQUIRED)
endif()
if (MFEM_USE_CEED)
find_package(libCEED REQUIRED)
endif()
if (MFEM_USE_CONDUIT)
find_package(Conduit REQUIRED conduit relay blueprint )
endif()
# Axom/Sidre
if (MFEM_USE_SIDRE)
find_package(Axom REQUIRED Axom)
find_package(Axom REQUIRED Sidre SLIC axom_utils)
endif()
# PUMI
@@ -323,7 +327,7 @@ endif()
# be before SuiteSparse.
set(MFEM_TPLS MPI_CXX OPENMP BLAS LAPACK METIS HYPRE SuiteSparse SUNDIALS PETSC
MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT GECKO GNUTLS NETCDF MPFR PUMI
POSIXCLOCKS MFEMBacktrace ZLIB OCCA RAJA)
POSIXCLOCKS MFEMBacktrace ZLIB OCCA RAJA CEED)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
@@ -535,6 +539,13 @@ if (MFEM_USE_OCCA)
FILES_MATCHING PATTERN "*.okl")
endif()
# Install the libCEED files
if (MFEM_USE_CEED)
install(DIRECTORY ${MFEM_SOURCE_DIRS}
DESTINATION ${INSTALL_INCLUDE_DIR}/mfem
FILES_MATCHING PATTERN "fem/libceed/*.h")
endif()
# Install ${HEADERS}
# ---
# foreach (HDR ${HEADERS})
+17 -10
View File
@@ -383,11 +383,11 @@ MFEM_USE_MPFR = YES/NO
see below.
MFEM_USE_SIDRE = YES/NO
Sidre is a component of LLNL's axom project, https://github.com/LLNL/axom,
that provides an HDF5-based file format for visualization or restart
capability following the Conduit (https://github.com/LLNL/conduit) mesh
blueprint specification. When enabled, this option requires installation of
HDF5 (see also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
Sidre is a component of LLNL's axom project, http://goo.gl/cZyJdn, that
provides an HDF5-based file format for visualization or restart capability
following the Conduit (https://github.com/LLNL/conduit) mesh blueprint
specification. When enabled, this option requires installation of HDF5 (see
also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
MFEM_USE_CONDUIT = YES/NO
Enables support for converting MFEM Mesh and Grid Function objects to and
@@ -442,6 +442,11 @@ MFEM_USE_OCCA = YES/NO
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
MFEM_USE_CEED = YES/NO
Enables support for the libCEED library in MFEM. libCEED is a portable
library for performant high-order operator evaluation developed by the Center
for Efficient Exascale Discretizations in the Exascale Computing Project.
MFEM_BUILD_TAG = (any value)
An optional tag to characterize the build. Exported to config/config.mk.
Can be used to identify the MFEM build from other makefiles.
@@ -543,8 +548,7 @@ The specific libraries and their options are:
Options: PETSC_OPT, PETSC_LIB.
- Sidre (optional), part of LLNL's axom project, used when MFEM_USE_SIDRE = YES.
Starting with MFEM v4.1, Axom version 0.3.1 or later is required.
URL: https://github.com/LLNL/axom
URL: http://goo.gl/cZyJdn (axom, to be released)
https://github.com/LLNL/conduit (Conduit)
https://support.hdfgroup.org/HDF5 (HDF5)
Options: SIDRE_OPT, SIDRE_LIB.
@@ -571,8 +575,12 @@ The specific libraries and their options are:
URL: https://libocca.org
Options: OCCA_DIR, OCCA_OPT, OCCA_LIB.
- CEED, used when MFEM_USE_CEED = YES.
URL: https://github.com/CEED/libCEED
https://ceed.exascaleproject.org/libceed
Options: CEED_DIR, CEED_OPT, CEED_LIB
- RAJA, used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
@@ -714,8 +722,8 @@ MFEM_USE_GZSTREAM
MFEM_USE_PUMI
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_CEED
MFEM_USE_RAJA
MFEM_USE_SIDRE
The following options are CMake specific:
@@ -764,7 +772,6 @@ The CMake build system adds auto-detection for the following packages/libraries:
- PUMI
- OCCA
- RAJA
- AXOM - Used when MFEM_USE_SIDRE is enabled
The following built-in CMake packages are also used:
+1
View File
@@ -44,6 +44,7 @@ set(MFEM_USE_PUMI @MFEM_USE_PUMI@)
set(MFEM_USE_CUDA @MFEM_USE_CUDA@)
set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
set(MFEM_CXX_FLAGS "@CMAKE_CXX_FLAGS@")
+3
View File
@@ -120,6 +120,9 @@
// Enable MFEM functionality based on the OCCA library
#cmakedefine MFEM_USE_OCCA
// Enable MFEM functionality based on the libCEED library
#cmakedefine MFEM_USE_CEED
// Which library functions to use in class StopWatch for measuring time.
// For a list of the available options, see INSTALL.
// If not defined, an option is selected automatically.
+3 -1
View File
@@ -18,4 +18,6 @@ include(MfemCmakeUtilities)
# Note: components are enabled based on the find_package() parameters.
mfem_find_package(Axom AXOM AXOM_DIR "include" "" "lib" ""
"Paths to headers required by Axom." "Libraries required by Axom."
ADD_COMPONENT Axom "include" axom/config.hpp "lib" axom)
ADD_COMPONENT Sidre "include" sidre/sidre.hpp "lib" sidre
ADD_COMPONENT SLIC "include" slic/slic.hpp "lib" slic
ADD_COMPONENT axom_utils "include" axom_utils/Utilities.hpp "lib" axom_utils)
+19
View File
@@ -0,0 +1,19 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Defines the following variables:
# - CEED_FOUND
# - CEED_LIBRARIES
# - CEED_INCLUDE_DIRS
include(MfemCmakeUtilities)
mfem_find_package(libCEED CEED CEED_DIR "include" ceed.h "lib" ceed
"Paths to headers required by libCEED." "Libraries required by libCEED.")
+3
View File
@@ -135,6 +135,9 @@
// Enable functionality based on the OCCA library.
// #define MFEM_USE_OCCA
// Enable functionality based on the CEED library.
// #define MFEM_USE_CEED
// Version of HYPRE used for building MFEM.
// #define MFEM_HYPRE_VERSION @MFEM_HYPRE_VERSION@
+1
View File
@@ -45,6 +45,7 @@ MFEM_USE_CUDA = @MFEM_USE_CUDA@
MFEM_USE_HIP = @MFEM_USE_HIP@
MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
MFEM_USE_CEED = @MFEM_USE_CEED@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
+3 -1
View File
@@ -45,6 +45,7 @@ option(MFEM_USE_PUMI "Enable PUMI" OFF)
option(MFEM_USE_CUDA "Enable CUDA" OFF)
option(MFEM_USE_OCCA "Enable OCCA" OFF)
option(MFEM_USE_RAJA "Enable RAJA" OFF)
option(MFEM_USE_CEED "Enable CEED" OFF)
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
@@ -154,7 +155,7 @@ set(CONDUIT_DIR "${MFEM_DIR}/../conduit" CACHE PATH
set(AXOM_DIR "${MFEM_DIR}/../axom" CACHE PATH "Path to the Axom library.")
# May need to add "Boost" as requirement.
set(Axom_REQUIRED_PACKAGES "Conduit/relay/blueprint" CACHE STRING
set(Axom_REQUIRED_PACKAGES "Conduit/relay" CACHE STRING
"Additional packages required by Axom.")
set(PUMI_DIR "${MFEM_DIR}/../pumi-2.1.0" CACHE STRING
@@ -162,6 +163,7 @@ set(PUMI_DIR "${MFEM_DIR}/../pumi-2.1.0" CACHE STRING
set(OCCA_DIR "${MFEM_DIR}/../occa" CACHE PATH "Path to OCCA")
set(RAJA_DIR "${MFEM_DIR}/../raja" CACHE PATH "Path to RAJA")
set(CEED_DIR "${MFEM_DIR}/../libCEED" CACHE PATH "Path to libCEED")
set(BLAS_INCLUDE_DIRS "" CACHE STRING "Path to BLAS headers.")
set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
+7 -1
View File
@@ -133,6 +133,7 @@ MFEM_USE_CUDA = NO
MFEM_USE_HIP = NO
MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_CEED = NO
# Compile and link options for zlib.
ZLIB_DIR =
@@ -299,7 +300,7 @@ SIDRE_LIB = \
-Wl,-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-laxom -lconduit -lconduit_relay -lconduit_blueprint -lhdf5 $(ZLIB_LIB) -ldl
-lsidre -lslic -laxom_utils -lconduit -lconduit_relay -lhdf5 $(ZLIB_LIB) -ldl
# PUMI
# Note that PUMI_DIR is needed -- it is used to check for gmi_sim.h
@@ -321,6 +322,11 @@ OCCA_DIR = @MFEM_DIR@/../occa
OCCA_OPT = -I$(OCCA_DIR)/include
OCCA_LIB = $(XLINKER)-rpath,$(OCCA_DIR)/lib -L$(OCCA_DIR)/lib -locca
# CEED library configuration
CEED_DIR ?= @MFEM_DIR@/../libCEED
CEED_OPT = -I$(CEED_DIR)/include
CEED_LIB = $(XLINKER)-rpath,$(CEED_DIR)/lib -L$(CEED_DIR)/lib -lceed
# RAJA library configuration
RAJA_DIR = @MFEM_DIR@/../raja
RAJA_OPT = -I$(RAJA_DIR)/include
+4 -4
View File
@@ -5,11 +5,11 @@
// Sample runs:
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 79 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3876
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 79
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3876 -o 2 -sys
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4526 -n 6 -o 3 -elast
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
// mpirun -np 4 ex12p -m ../data/beam-hex-nurbs.mesh
//
+1 -1
View File
@@ -252,7 +252,7 @@ int main(int argc, char *argv[])
}
else
{
GMRES(A, M, B, X, 3, 5000, 100, rtol*rtol, 0.0);
GMRES(A, M, B, X, 3, 5000, 50, rtol*rtol, 0.0);
}
#else
// 11. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
+2 -1
View File
@@ -102,7 +102,8 @@ int main(int argc, char *argv[])
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels = (int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
int ref_levels =
(int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
+11 -2
View File
@@ -32,7 +32,6 @@ set(SRCS
nonlininteg.cpp
staticcond.cpp
tmop.cpp
tmop_tools.cpp
)
set(HDRS
@@ -65,7 +64,6 @@ set(HDRS
tfespace.hpp
tintrules.hpp
tmop.hpp
tmop_tools.hpp
)
if (MFEM_USE_SIDRE)
@@ -95,6 +93,17 @@ if (MFEM_USE_MPI)
pnonlinearform.hpp)
endif()
if (MFEM_USE_CEED)
list(APPEND SRCS
libceed/ceed.cpp
libceed/diffusion.cpp
libceed/mass.cpp)
list(APPEND HDRS
libceed/ceed.hpp
libceed/diffusion.hpp
libceed/mass.hpp)
endif()
convert_filenames_to_full_paths(SRCS)
convert_filenames_to_full_paths(HDRS)
+23 -13
View File
@@ -102,25 +102,35 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (elem_restrict_lex)
if (Device::Allows(Backend::CEED_MASK))
{
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);
}
}
else
{
if (elem_restrict_lex)
{
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);
}
}
}
}
void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
+87
View File
@@ -19,6 +19,10 @@
namespace mfem
{
#ifdef MFEM_USE_CEED
struct CeedData;
#endif
/// Abstract base class BilinearFormIntegrator
class BilinearFormIntegrator : public NonlinearFormIntegrator
{
@@ -1679,6 +1683,11 @@ private:
int dim, ne, dofs1D, quad1D;
Vector pa_data;
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
#endif
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator() { Q = NULL; MQ = NULL; maps = NULL; geom = NULL; }
@@ -1725,6 +1734,79 @@ public:
const FiniteElement &test_fe);
};
/** Class for integrating the bilinear form a(u,v) := (Q grad u, grad v) where Q
can be a scalar or a matrix coefficient. */
class MechanicsIntegrator: 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
// PA extension
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, dofs1D, quad1D;
Vector pa_data;
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
double* ktan_ptr;
#endif
public:
/// Construct a diffusion integrator with coefficient Q = 1
MechanicsIntegrator() { Q = NULL; MQ = NULL; maps = NULL; geom = NULL; }
/// Construct a diffusion integrator with a scalar coefficient q
MechanicsIntegrator(Coefficient &q)
: Q(&q) { MQ = NULL; maps = NULL; geom = NULL; }
/// Construct a diffusion integrator with a matrix coefficient q
MechanicsIntegrator(MatrixCoefficient &q)
: MQ(&q) { Q = NULL; maps = NULL; geom = NULL; }
/** Given a particular Finite Element
computes the element stiffness matrix elmat. */
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
/** Given a trial and test Finite Element computes the element stiffness
matrix elmat. */
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
/// Perform the local action of the BilinearFormIntegrator
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect);
virtual void ComputeElementFlux(const FiniteElement &el,
ElementTransformation &Trans,
Vector &u, const FiniteElement &fluxelem,
Vector &flux, int with_coef = 1);
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
virtual void AssemblePA(const FiniteElementSpace&);
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
};
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
class MassIntegrator: public BilinearFormIntegrator
{
@@ -1738,6 +1820,11 @@ protected:
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
#endif
public:
MassIntegrator(const IntegrationRule *ir = NULL)
+86 -73
View File
@@ -12,6 +12,7 @@
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/diffusion.hpp"
using namespace std;
@@ -27,7 +28,7 @@ static void OccaPADiffusionSetup2D(const int D1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
const double COEFF,
Vector &op)
{
occa::properties props;
@@ -35,9 +36,7 @@ static void OccaPADiffusionSetup2D(const int D1D,
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup2D_ker;
if (OccaDiffSetup2D_ker.find(id) == OccaDiffSetup2D_ker.end())
@@ -47,7 +46,7 @@ static void OccaPADiffusionSetup2D(const int D1D,
"DiffusionSetup2D", props);
OccaDiffSetup2D_ker.emplace(id, DiffusionSetup2D);
}
OccaDiffSetup2D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
OccaDiffSetup2D_ker.at(id)(NE, o_W, o_J, COEFF, o_op);
}
static void OccaPADiffusionSetup3D(const int D1D,
@@ -55,7 +54,7 @@ static void OccaPADiffusionSetup3D(const int D1D,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
const double COEFF,
Vector &op)
{
occa::properties props;
@@ -63,9 +62,7 @@ static void OccaPADiffusionSetup3D(const int D1D,
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup3D_ker;
if (OccaDiffSetup3D_ker.find(id) == OccaDiffSetup3D_ker.end())
@@ -75,7 +72,7 @@ static void OccaPADiffusionSetup3D(const int D1D,
"DiffusionSetup3D", props);
OccaDiffSetup3D_ker.emplace(id, DiffusionSetup3D);
}
OccaDiffSetup3D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
OccaDiffSetup3D_ker.at(id)(NE, o_W, o_J, COEFF, o_op);
}
#endif // MFEM_USE_OCCA
@@ -84,16 +81,14 @@ static void PADiffusionSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
const double COEFF,
Vector &op)
{
const int NQ = Q1D*Q1D;
const bool const_c = c.Size() == 1;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto C = const_c ? Reshape(c.Read(), 1, 1) : Reshape(c.Read(), NQ, NE);
auto D = Reshape(d.Write(), NQ, 3, NE);
auto y = Reshape(op.Write(), NQ, 3, NE);
MFEM_FORALL(e, NE,
{
@@ -103,11 +98,10 @@ static void PADiffusionSetup2D(const int Q1D,
const double J21 = J(q,1,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / ((J11*J22)-(J21*J12));
D(q,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
D(q,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
D(q,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
const double c_detJ = W[q] * COEFF / ((J11*J22)-(J21*J12));
y(q,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
y(q,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
y(q,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
}
});
}
@@ -117,15 +111,13 @@ static void PADiffusionSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
const double COEFF,
Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
const bool const_c = c.Size() == 1;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto C = const_c ? Reshape(c.Read(), 1, 1) : Reshape(c.Read(), NQ, NE);
auto D = Reshape(d.Write(), NQ, 6, NE);
auto y = Reshape(op.Write(), NQ, 6, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
@@ -142,8 +134,7 @@ static void PADiffusionSetup3D(const int Q1D,
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / detJ;
const double c_detJ = W[q] * COEFF / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
@@ -155,12 +146,12 @@ static void PADiffusionSetup3D(const int Q1D,
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
D(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
D(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
D(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
D(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
D(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
D(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
y(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
y(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
y(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
y(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
y(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
y(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
}
});
}
@@ -171,8 +162,8 @@ static void PADiffusionSetup(const int dim,
const int NE,
const Array<double> &W,
const Vector &J,
const Vector &C,
Vector &D)
const double COEFF,
Vector &op)
{
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PADiffusionSetup"); }
if (dim == 2)
@@ -180,67 +171,58 @@ static void PADiffusionSetup(const int dim,
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, C, D);
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, COEFF, op);
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup2D(Q1D, NE, W, J, C, D);
PADiffusionSetup2D(Q1D, NE, W, J, COEFF, op);
}
if (dim == 3)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup3D(D1D, Q1D, NE, W, J, C, D);
OccaPADiffusionSetup3D(D1D, Q1D, NE, W, J, COEFF, op);
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup3D(Q1D, NE, W, J, C, D);
PADiffusionSetup3D(Q1D, NE, W, J, COEFF, op);
}
}
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
Vector coeff;
if (Q == nullptr)
#ifdef MFEM_USE_CEED
if (Device::Allows(Backend::CEED_MASK))
{
coeff.SetSize(1);
coeff(0) = 1.0;
}
else if (ConstantCoefficient* cQ = dynamic_cast<ConstantCoefficient*>(Q))
{
coeff.SetSize(1);
coeff(0) = cQ->constant;
CeedData* ptr = new CeedData();
ceedDataPtr = ptr;
InitCeedCoeff(Q, ptr);
CeedPADiffusionAssemble(fes, *ir, *ptr);
}
else
#endif
{
coeff.SetSize(nq * ne);
auto C = Reshape(coeff.Write(), nq, ne);
for (int e = 0; e < ne; ++e)
{
ElementTransformation& T = *fes.GetElementTransformation(e);
for (int q = 0; q < nq; ++q)
{
C(q,e) = Q->Eval(T, ir->IntPoint(q));
}
}
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient*>(Q);
MFEM_VERIFY(cQ != NULL, "only ConstantCoefficient is supported!");
const double coeff = cQ->constant;
PADiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
PADiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J, coeff,
pa_data);
}
#ifdef MFEM_USE_OCCA
@@ -1119,9 +1101,40 @@ static void PADiffusionApply(const int dim,
// PA Diffusion Apply kernel
void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PADiffusionApply(dim, dofs1D, quad1D, ne,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
#ifdef MFEM_USE_CEED
if (Device::Allows(Backend::CEED_MASK))
{
const CeedScalar *x_ptr;
CeedScalar *y_ptr;
CeedMemType mem;
CeedGetPreferredMemType(internal::ceed, &mem);
if ( Device::Allows(Backend::CUDA) && mem==CEED_MEM_DEVICE )
{
x_ptr = x.Read();
y_ptr = y.ReadWrite();
}
else
{
x_ptr = x.HostRead();
y_ptr = y.HostReadWrite();
mem = CEED_MEM_HOST;
}
CeedVectorSetArray(ceedDataPtr->u, mem, CEED_USE_POINTER,
const_cast<CeedScalar*>(x_ptr));
CeedVectorSetArray(ceedDataPtr->v, mem, CEED_USE_POINTER, y_ptr);
CeedOperatorApply(ceedDataPtr->oper, ceedDataPtr->u, ceedDataPtr->v,
CEED_REQUEST_IMMEDIATE);
CeedVectorSyncArray(ceedDataPtr->v, mem);
}
else
#endif
{
PADiffusionApply(dim, dofs1D, quad1D, ne,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
}
}
} // namespace mfem
+104 -72
View File
@@ -12,6 +12,7 @@
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/mass.hpp"
using namespace std;
@@ -29,88 +30,88 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation *T = mesh->GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T);
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::COORDINATES |
GeometricFactors::JACOBIANS);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, Device::GetMemoryType());
Vector coeff;
if (Q == nullptr)
#ifdef MFEM_USE_CEED
if (Device::Allows(Backend::CEED_MASK))
{
coeff.SetSize(1);
coeff(0) = 1.0;
}
else if (ConstantCoefficient* cQ = dynamic_cast<ConstantCoefficient*>(Q))
{
coeff.SetSize(1);
coeff(0) = cQ->constant;
CeedData* ptr = new CeedData();
ceedDataPtr = ptr;
InitCeedCoeff(Q, ptr);
CeedPAMassAssemble(fes, *ir, *ptr);
}
else
#endif
{
coeff.SetSize(nq * ne);
auto C = Reshape(coeff.Write(), nq, ne);
for (int e = 0; e < ne; ++e)
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)
{
ElementTransformation& T = *fes.GetElementTransformation(e);
for (int q = 0; q < nq; ++q)
double constant = 0.0;
if (const_coeff)
{
C(q,e) = Q->Eval(T, ir->IntPoint(q));
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==1) { MFEM_ABORT("Not supported yet... stay tuned!"); }
if (dim==2)
{
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
auto w = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
auto C =
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
MFEM_FORALL(e, NE,
if (dim==3)
{
for (int q = 0; q < NQ; ++q)
double constant = 0.0;
if (const_coeff)
{
const double J11 = J(q,0,0,e);
const double J12 = J(q,1,0,e);
const double J21 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double detJ = (J11*J22)-(J21*J12);
const double coeff = const_c ? C(0,0) : C(q,e);
v(q,e) = w[q] * coeff * detJ;
constant = const_coeff->constant;
}
});
}
if (dim==3)
{
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
auto W = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
auto C =
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,NE);
auto v = Reshape(pa_data.Write(), NQ,NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
else
{
const double J11 = J(q,0,0,e), J12 = J(q,0,1,e), J13 = J(q,0,2,e);
const double J21 = J(q,1,0,e), J22 = J(q,1,1,e), J23 = J(q,1,2,e);
const double J31 = J(q,2,0,e), J32 = J(q,2,1,e), J33 = J(q,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double coeff = const_c ? C(0,0) : C(q,e);
v(q,e) = W[q] * coeff * detJ;
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;
}
});
}
}
}
@@ -763,7 +764,7 @@ static void PAMassApply(const int dim,
#endif // MFEM_USE_OCCA
if (dim == 2)
{
switch ((D1D << 4) | Q1D)
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);
@@ -778,7 +779,7 @@ static void PAMassApply(const int dim,
}
else if (dim == 3)
{
switch ((D1D << 4) | Q1D)
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);
@@ -795,7 +796,38 @@ static void PAMassApply(const int dim,
void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PAMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
#ifdef MFEM_USE_CEED
if (Device::Allows(Backend::CEED_MASK))
{
const CeedScalar *x_ptr;
CeedScalar *y_ptr;
CeedMemType mem;
CeedGetPreferredMemType(internal::ceed, &mem);
if ( Device::Allows(Backend::CUDA) && mem==CEED_MEM_DEVICE )
{
x_ptr = x.Read();
y_ptr = y.ReadWrite();
}
else
{
x_ptr = x.HostRead();
y_ptr = y.HostReadWrite();
mem = CEED_MEM_HOST;
}
CeedVectorSetArray(ceedDataPtr->u, mem, CEED_USE_POINTER,
const_cast<CeedScalar*>(x_ptr));
CeedVectorSetArray(ceedDataPtr->v, mem, CEED_USE_POINTER, y_ptr);
CeedOperatorApply(ceedDataPtr->oper, ceedDataPtr->u, ceedDataPtr->v,
CEED_REQUEST_IMMEDIATE);
CeedVectorSyncArray(ceedDataPtr->v, mem);
}
else
#endif
{
PAMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
}
}
} // namespace mfem
File diff suppressed because it is too large Load Diff
-5
View File
@@ -9635,7 +9635,6 @@ void L2_TetrahedronElement::ProjectDelta(int vertex, Vector &dofs) const
const IntegrationPoint &ip = Nodes.IntPoint(i);
dofs[i] = pow(ip.y, Order);
}
break;
case 3:
for (int i = 0; i < Dof; i++)
{
@@ -11966,10 +11965,6 @@ Linear3DFiniteElement TetrahedronFE;
// Object declared in mesh/wedge.hpp.
// Defined here to ensure it is constructed after 'poly1d' and before
// 'Geometries'.
// TODO: define as thread_local to prevent race conditions in GLVis, because
// there is no "LinearWedgeFiniteElement" and WedgeFE is in turn used from two
// different threads for different things in GLVis. We also don't want to turn
// MFEM_THREAD_SAFE on globally. (See PR #731)
H1_WedgeElement WedgeFE(1);
// Object declared in geom.hpp.
-1
View File
@@ -31,7 +31,6 @@
#include "estimators.hpp"
#include "staticcond.hpp"
#include "tmop.hpp"
#include "tmop_tools.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
+39 -145
View File
@@ -567,40 +567,6 @@ bool FiniteElementSpace::DofFinalizable(int dof, const Array<bool>& finalized,
return true;
}
void FiniteElementSpace::GetDegenerateFaceDofs(int index,
Array<int> &dofs) const
{
// In NC meshes with prisms, a special constraint occurs where a prism edge
// is slave to a quadrilateral face. Rather than introduce a new edge-face
// constraint type, we handle such cases as degenerate face-face constraints,
// where the point-matrix rectangle has zero height. This method returns
// DOFs for the first edge of the rectangle, duplicated in the orthogonal
// direction, to resemble DOFs for a quadrilateral face. The extra DOFs are
// ignored by FiniteElementSpace::AddDependencies.
Array<int> edof;
GetEdgeDofs(-1 - index, edof);
int nv = fec->DofForGeometry(Geometry::POINT);
int ne = fec->DofForGeometry(Geometry::SEGMENT);
int nn = 2*nv + ne;
dofs.SetSize(nn*nn);
dofs = edof[0];
// copy first two vertex DOFs
for (int i = 0; i < nv; i++)
{
dofs[i] = edof[i];
dofs[nv+i] = edof[nv+i];
}
// copy first edge DOFs
for (int i = 0; i < ne; i++)
{
dofs[4*nv + i] = edof[2*nv + i];
}
}
void
FiniteElementSpace::GetEntityDofs(int entity, int index, Array<int> &dofs) const
{
@@ -608,8 +574,7 @@ FiniteElementSpace::GetEntityDofs(int entity, int index, Array<int> &dofs) const
{
case 0: GetVertexDofs(index, dofs); break;
case 1: GetEdgeDofs(index, dofs); break;
case 2: (index >= 0) ? GetFaceDofs(index, dofs)
/* */ : GetDegenerateFaceDofs(index, dofs);
case 2: GetFaceDofs(index, dofs); break;
}
}
@@ -632,33 +597,28 @@ void FiniteElementSpace::BuildConformingInterpolation() const
// collect local edge/face dependencies
for (int entity = 1; entity <= 2; entity++)
{
const NCMesh::NCList &list = mesh->ncmesh->GetNCList(entity);
const NCMesh::NCList &list = (entity > 1) ? mesh->ncmesh->GetFaceList()
/* */ : mesh->ncmesh->GetEdgeList();
if (!list.masters.size()) { continue; }
Array<int> master_dofs, slave_dofs;
IsoparametricTransformation T;
DenseMatrix I;
if (entity > 1) { T.SetFE(&QuadrilateralFE); }
else { T.SetFE(&SegmentFE); }
Geometry::Type geom = (entity > 1) ? Geometry::SQUARE : Geometry::SEGMENT;
const FiniteElement* fe = fec->FiniteElementForGeometry(geom);
if (!fe) { continue; }
Array<int> master_dofs, slave_dofs;
DenseMatrix I(fe->GetDof());
// loop through all master edges/faces, constrain their slave edges/faces
for (unsigned mi = 0; mi < list.masters.size(); mi++)
{
const NCMesh::Master &master = list.masters[mi];
GetEntityDofs(entity, master.index, master_dofs);
if (!master_dofs.Size()) { continue; }
const FiniteElement* fe = fec->FiniteElementForGeometry(master.Geom());
if (!fe) { continue; }
switch (master.geom)
{
case Geometry::SQUARE: T.SetFE(&QuadrilateralFE); break;
case Geometry::TRIANGLE: T.SetFE(&TriangleFE); break;
case Geometry::SEGMENT: T.SetFE(&SegmentFE); break;
default: MFEM_ABORT("unsupported geometry");
}
for (int si = master.slaves_begin; si < master.slaves_end; si++)
{
const NCMesh::Slave &slave = list.slaves[si];
@@ -831,14 +791,8 @@ const Operator *FiniteElementSpace::GetElementRestriction(
{
// Check if we have a discontinuous space using the FE collection:
const L2_FECollection *dg_space = dynamic_cast<const L2_FECollection*>(fec);
if (dg_space)
{
if (L2E_nat.Ptr() == NULL)
{
L2E_nat.Reset(new L2ElementRestriction(*this));
}
return L2E_nat.Ptr();
}
if (dg_space) { return NULL; }
// TODO: support other DG collections.
if (e_ordering == ElementDofOrdering::LEXICOGRAPHIC)
{
if (L2E_lex.Ptr() == NULL)
@@ -951,7 +905,7 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
const DenseTensor &pmats = rtrans.point_matrices[geom];
const DenseTensor &pmats = rtrans.GetPointMatrices(geom);
int nmat = pmats.SizeK();
int ldof = fe->GetDof(); // assuming the same FE everywhere
@@ -990,9 +944,7 @@ FiniteElementSpace::RefinementOperator::RefinementOperator
: fespace(fespace)
, old_elem_dof(old_elem_dof)
{
const Mesh* mesh = fespace->GetMesh();
MFEM_VERIFY(mesh->ReduceInt(fespace->GetNDofs()) >=
mesh->ReduceInt(old_ndofs),
MFEM_VERIFY(fespace->GetNDofs() >= old_ndofs,
"Previous space is not coarser.");
width = old_ndofs * fespace->GetVDim();
@@ -1102,7 +1054,7 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator(
f_fes->fec->FiniteElementForGeometry(geom);
const FiniteElement *coarse_fe =
c_fes->fec->FiniteElementForGeometry(geom);
const DenseTensor &pmats = rtrans.point_matrices[geom];
const DenseTensor &pmats = rtrans.GetPointMatrices(geom);
lP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), pmats.SizeK());
lM.SetSize(fine_fe->GetDof(), fine_fe->GetDof(), pmats.SizeK());
@@ -1218,7 +1170,7 @@ void FiniteElementSpace::GetLocalDerefinementMatrices(Geometry::Type geom,
const CoarseFineTransformations &dtrans =
mesh->ncmesh->GetDerefinementTransforms();
const DenseTensor &pmats = dtrans.point_matrices[geom];
const DenseTensor &pmats = dtrans.GetPointMatrices(geom);
const int nmat = pmats.SizeK();
const int ldof = fe->GetDof();
@@ -1323,7 +1275,7 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
coarse_fes.fec->FiniteElementForGeometry(geom);
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
const DenseTensor &pmats = rtrans.point_matrices[geom];
const DenseTensor &pmats = rtrans.GetPointMatrices(geom);
int nmat = pmats.SizeK();
@@ -1416,26 +1368,31 @@ void FiniteElementSpace::UpdateNURBS()
void FiniteElementSpace::Construct()
{
// This method should be used only for non-NURBS spaces.
MFEM_VERIFY(!NURBSext, "internal error");
MFEM_ASSERT(!NURBSext, "internal error");
elem_dof = NULL;
bdrElem_dof = NULL;
nvdofs = mesh->GetNV() * fec->DofForGeometry(Geometry::POINT);
if ( mesh->Dimension() > 1 )
{
nedofs = mesh->GetNEdges() * fec->DofForGeometry(Geometry::SEGMENT);
}
else
{
nedofs = 0;
}
ndofs = 0;
nedofs = nfdofs = nbdofs = 0;
nfdofs = 0;
nbdofs = 0;
bdofs = NULL;
fdofs = NULL;
cP = NULL;
cR = NULL;
cP_is_set = false;
// 'Th' is initialized/destroyed before this method is called.
nvdofs = mesh->GetNV() * fec->DofForGeometry(Geometry::POINT);
if (mesh->Dimension() > 1)
{
nedofs = mesh->GetNEdges() * fec->DofForGeometry(Geometry::SEGMENT);
}
// Th is initialized/destroyed before this method is called.
if (mesh->GetNFaces() > 0)
{
@@ -1467,7 +1424,8 @@ void FiniteElementSpace::Construct()
bdofs[0] = 0;
for (int i = 0; i < mesh->GetNE(); i++)
{
nbdofs += fec->DofForGeometry(mesh->GetElementBaseGeometry(i));
Geometry::Type geom = mesh->GetElementBaseGeometry(i);
nbdofs += fec->DofForGeometry(geom);
bdofs[i+1] = nbdofs;
}
}
@@ -1478,7 +1436,7 @@ void FiniteElementSpace::Construct()
// later.
}
void FiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
void FiniteElementSpace::GetElementDofs (int i, Array<int> &dofs) const
{
if (elem_dof)
{
@@ -2555,7 +2513,7 @@ L2ProjectionGridTransfer::L2Projection::L2Projection(
Vector shape_lor(ndof_lor);
const Geometry::Type geom = fe_ho->GetGeomType();
const DenseTensor &pmats = cf_tr.point_matrices[geom];
const DenseTensor &pmats = cf_tr.GetPointMatrices(geom);
emb_tr.SetIdentityTransformation(geom);
for (int iho=0; iho<nel_ho; ++iho)
@@ -2578,7 +2536,7 @@ L2ProjectionGridTransfer::L2Projection::L2Projection(
// Create the transformation that embeds the fine low-order element
// within the coarse high-order element in reference space
emb_tr.GetPointMat() = pmats(cf_tr.embeddings[ilor].matrix);
emb_tr.GetPointMat() = pmats(iref);
emb_tr.FinalizeTransformation();
int order = fe_lor->GetOrder() + fe_ho->GetOrder() + el_tr->OrderW();
@@ -2679,70 +2637,6 @@ const Operator &L2ProjectionGridTransfer::BackwardOperator()
return *B;
}
L2ElementRestriction::L2ElementRestriction(const FiniteElementSpace &fes)
: ne(fes.GetNE()),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
ndof(ne > 0 ? fes.GetFE(0)->GetDof() : 0)
{
height = vdim*ne*ndof;
width = vdim*ne*ndof;
}
void L2ElementRestriction::Mult(const Vector &x, Vector &y) const
{
for (int iel=0; iel<ne; ++iel)
{
for (int vd=0; vd<vdim; ++vd)
{
for (int idof=0; idof<ndof; ++idof)
{
// E-vector dimensions (dofs, vdim, elements)
// L-vector dimensions: byVDIM: (vdim, dofs, element)
// byNODES: (dofs, elements, vdim)
int yidx = iel*vdim*ndof + vd*ndof + idof;
int xidx;
if (byvdim)
{
xidx = iel*ndof*vdim + idof*vdim + vd;
}
else
{
xidx = vd*ne*ndof + iel*ndof + idof;
}
y[yidx] = x[xidx];
}
}
}
}
void L2ElementRestriction::MultTranspose(const Vector &x, Vector &y) const
{
// Since this restriction is a permutation, the transpose is the inverse
for (int iel=0; iel<ne; ++iel)
{
for (int vd=0; vd<vdim; ++vd)
{
for (int idof=0; idof<ndof; ++idof)
{
// E-vector dimensions (dofs, vdim, elements)
// L-vector dimensions: byVDIM: (vdim, dofs, element)
// byNODES: (dofs, elements, vdim)
int xidx = iel*vdim*ndof + vd*ndof + idof;
int yidx;
if (byvdim)
{
yidx = iel*ndof*vdim + idof*vdim + vd;
}
else
{
yidx = vd*ne*ndof + iel*ndof + idof;
}
y[yidx] = x[xidx];
}
}
}
}
ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
ElementDofOrdering e_ordering)
+2 -22
View File
@@ -146,8 +146,6 @@ protected:
/// Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
void GetEntityDofs(int entity, int index, Array<int> &dofs) const;
// Get degenerate face DOFs: see explanation in method implementation.
void GetDegenerateFaceDofs(int index, Array<int> &dofs) const;
/// Calculate the cP and cR matrices for a nonconforming mesh.
void BuildConformingInterpolation() const;
@@ -158,7 +156,6 @@ protected:
static bool DofFinalizable(int dof, const Array<bool>& finalized,
const SparseMatrix& deps);
/// Replicate 'mat' in the vector dimension, according to vdim ordering mode.
void MakeVDimMatrix(SparseMatrix &mat) const;
/// GridFunction interpolation operator applicable after mesh refinement.
@@ -307,9 +304,8 @@ public:
The parameter @a e_ordering describes how the local DOFs in each element
should be ordered, see ElementDofOrdering.
For discontinuous spaces, the element restriction corresponds to a
permutation of the degrees of freedom, implemented by the
L2ElementRestriction class.
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;
@@ -900,22 +896,6 @@ public:
void MultTranspose(const Vector &x, Vector &y) const;
};
/// Operator that converts L2 FiniteElementSpace L-vectors to E-vectors.
/** Objects of this type are typically created and owned by FiniteElementSpace
objects, see FiniteElementSpace::GetElementRestriction(). L-vectors
corresponding to grid functions in L2 finite element spaces differ from
E-vectors only in the ordering of the degrees of freedom. */
class L2ElementRestriction : public Operator
{
const int ne;
const int vdim;
const bool byvdim;
const int ndof;
public:
L2ElementRestriction(const FiniteElementSpace&);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
};
/** @brief A class that performs interpolation from an E-vector to quadrature
point values and/or derivatives (Q-vectors). */
-1
View File
@@ -1716,7 +1716,6 @@ void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
Array<int> vdofs;
Vector vals;
HostWrite();
// maximal element attribute for each dof
dof_attr.SetSize(fes->GetVSize());
dof_attr = -1;
-2
View File
@@ -434,8 +434,6 @@ public:
/** The GridFunction is resized using the SetSize() method. */
virtual void SetSpace(FiniteElementSpace *f);
using Vector::MakeRef;
/** @brief Make the GridFunction reference external data on a new
FiniteElementSpace. */
/** This method changes the FiniteElementSpace associated with the
+170
View File
@@ -0,0 +1,170 @@
// 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 "ceed.hpp"
#include "../../general/device.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
typedef struct stat struct_stat;
#else
#define stat(dir, buf) _stat(dir, buf)
#define S_ISDIR(mode) _S_IFDIR(mode)
typedef struct _stat struct_stat;
#endif
namespace mfem
{
namespace internal
{
extern Ceed ceed;
std::string ceed_path;
}
#ifdef MFEM_USE_CEED
void InitCeedCoeff(Coefficient* Q, CeedData* ptr)
{
if (ConstantCoefficient* coeff = dynamic_cast<ConstantCoefficient*>(Q))
{
CeedConstCoeff* ceedCoeff = new CeedConstCoeff{coeff->constant};
ptr->coeff_type = CeedCoeff::Const;
ptr->coeff = (void*)ceedCoeff;
}
else if (GridFunctionCoefficient* coeff =
dynamic_cast<GridFunctionCoefficient*>(Q))
{
CeedGridCoeff* ceedCoeff = new CeedGridCoeff;
ceedCoeff->coeff = coeff->GetGridFunction();
ptr->coeff_type = CeedCoeff::Grid;
ptr->coeff = (void*)ceedCoeff;
}
else
{
MFEM_ABORT("This type of Coefficient is not supported.");
}
}
void InitCeedTensorBasisAndRestriction(const mfem::FiniteElementSpace &fes,
const mfem::IntegrationRule &ir,
Ceed ceed, CeedBasis *basis,
CeedElemRestriction *restr)
{
mfem::Mesh *mesh = fes.GetMesh();
const mfem::FiniteElement *fe = fes.GetFE(0);
const int order = fes.GetOrder(0);
mfem::Array<int> dof_map;
switch (mesh->Dimension())
{
case 1:
{
const mfem::H1_SegmentElement *h1_fe =
dynamic_cast<const mfem::H1_SegmentElement *>(fe);
MFEM_VERIFY(h1_fe, "invalid FE");
h1_fe->GetDofMap().Copy(dof_map);
break;
}
case 2:
{
const mfem::H1_QuadrilateralElement *h1_fe =
dynamic_cast<const mfem::H1_QuadrilateralElement *>(fe);
MFEM_VERIFY(h1_fe, "invalid FE");
h1_fe->GetDofMap().Copy(dof_map);
break;
}
case 3:
{
const mfem::H1_HexahedronElement *h1_fe =
dynamic_cast<const mfem::H1_HexahedronElement *>(fe);
MFEM_VERIFY(h1_fe, "invalid FE");
h1_fe->GetDofMap().Copy(dof_map);
break;
}
}
const mfem::FiniteElement *fe1d =
fes.FEColl()->FiniteElementForGeometry(mfem::Geometry::SEGMENT);
mfem::DenseMatrix shape1d(fe1d->GetDof(), ir.GetNPoints());
mfem::DenseMatrix grad1d(fe1d->GetDof(), ir.GetNPoints());
mfem::Vector qref1d(ir.GetNPoints()), qweight1d(ir.GetNPoints());
mfem::Vector shape_i(shape1d.Height());
mfem::DenseMatrix grad_i(grad1d.Height(), 1);
const mfem::H1_SegmentElement *h1_fe1d =
dynamic_cast<const mfem::H1_SegmentElement *>(fe1d);
MFEM_VERIFY(h1_fe1d, "invalid FE");
const mfem::Array<int> &dof_map_1d = h1_fe1d->GetDofMap();
for (int i = 0; i < ir.GetNPoints(); i++)
{
const mfem::IntegrationPoint &ip = ir.IntPoint(i);
qref1d(i) = ip.x;
qweight1d(i) = ip.weight;
fe1d->CalcShape(ip, shape_i);
fe1d->CalcDShape(ip, grad_i);
for (int j = 0; j < shape1d.Height(); j++)
{
shape1d(j, i) = shape_i(dof_map_1d[j]);
grad1d(j, i) = grad_i(dof_map_1d[j], 0);
}
}
CeedBasisCreateTensorH1(ceed, mesh->Dimension(), fes.GetVDim(), order + 1,
ir.GetNPoints(), shape1d.GetData(),
grad1d.GetData(), qref1d.GetData(),
qweight1d.GetData(), basis);
const mfem::Table &el_dof = fes.GetElementToDofTable();
mfem::Array<int> tp_el_dof(el_dof.Size_of_connections());
for (int i = 0; i < mesh->GetNE(); i++)
{
const int el_offset = fe->GetDof() * i;
for (int j = 0; j < fe->GetDof(); j++)
{
tp_el_dof[j + el_offset] = el_dof.GetJ()[dof_map[j] + el_offset];
}
}
CeedElemRestrictionCreate(ceed, mesh->GetNE(), fe->GetDof(),
fes.GetNDofs(), fes.GetVDim(), CEED_MEM_HOST, CEED_COPY_VALUES,
tp_el_dof.GetData(), restr);
}
const std::string &GetCeedPath()
{
if (internal::ceed_path.empty())
{
const char *install_dir = MFEM_INSTALL_DIR "/include/mfem/fem/libceed";
const char *source_dir = MFEM_SOURCE_DIR "/fem/libceed";
struct_stat m_stat;
if (stat(install_dir, &m_stat) == 0 && S_ISDIR(m_stat.st_mode))
{
internal::ceed_path = install_dir;
}
else if (stat(source_dir, &m_stat) == 0 && S_ISDIR(m_stat.st_mode))
{
internal::ceed_path = source_dir;
}
else
{
MFEM_ABORT("Cannot find libCEED kernels in MFEM_INSTALL_DIR or "
"MFEM_SOURCE_DIR");
}
}
#ifdef MFEM_DEBUG
mfem::out << "Using libCEED dir: " << internal::ceed_path << std::endl;
#endif
return internal::ceed_path;
}
#endif
} // namespace mfem
+81
View File
@@ -0,0 +1,81 @@
// 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_LIBCEED_HPP
#define MFEM_LIBCEED_HPP
#include "../gridfunc.hpp"
#include "../fespace.hpp"
#ifdef MFEM_USE_CEED
#include <ceed.h>
#else
typedef void* Ceed;
typedef int CeedInt;
typedef double CeedScalar;
#define CEED_QFUNCTION(name) int name
#endif
namespace mfem
{
#ifdef MFEM_USE_CEED
namespace internal { extern Ceed ceed; }
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
enum class CeedCoeff { Const, Grid };
struct CeedConstCoeff
{
double val;
};
struct CeedGridCoeff
{
GridFunction* coeff;
CeedBasis basis;
CeedElemRestriction restr;
CeedVector coeffVector;
};
struct CeedData
{
CeedOperator build_oper, oper;
CeedBasis basis, mesh_basis;
CeedElemRestriction restr, mesh_restr, restr_i, mesh_restr_i;
CeedQFunction apply_qfunc, build_qfunc;
CeedVector node_coords, rho;
CeedCoeff coeff_type;
void* coeff;
BuildContext build_ctx;
CeedVector u, v;
};
/// Identifies the type of coefficient of the Integrator to initialize accordingly the CeedData
void InitCeedCoeff(Coefficient* Q, CeedData* ptr);
/// Initialize a tensor CeedBasis and a CeedElemRestriction
void InitCeedTensorBasisAndRestriction(const mfem::FiniteElementSpace &fes,
const mfem::IntegrationRule &ir,
Ceed ceed, CeedBasis *basis,
CeedElemRestriction *restr);
const std::string &GetCeedPath();
#endif
}
#endif // MFEM_LIBCEED_HPP
+145
View File
@@ -0,0 +1,145 @@
// 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 "ceed.hpp"
#include "../../general/device.hpp"
#include "diffusion.h"
namespace mfem
{
#ifdef MFEM_USE_CEED
void CeedPADiffusionAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm, CeedData& ceedData)
{
Ceed ceed(internal::ceed);
mfem::Mesh *mesh = fes.GetMesh();
const int ir_order = irm.GetOrder();
const mfem::IntegrationRule &ir =
mfem::IntRules.Get(mfem::Geometry::SEGMENT, ir_order);
CeedInt nqpts, nelem = mesh->GetNE(), dim = mesh->SpaceDimension();
mesh->EnsureNodes();
InitCeedTensorBasisAndRestriction(fes, ir, ceed, &ceedData.basis, &ceedData.restr);
const mfem::FiniteElementSpace *mesh_fes = mesh->GetNodalFESpace();
MFEM_VERIFY(mesh_fes, "the Mesh has no nodal FE space");
InitCeedTensorBasisAndRestriction(*mesh_fes, ir, ceed, &ceedData.mesh_basis, &ceedData.mesh_restr);
CeedBasisGetNumQuadraturePoints(ceedData.basis, &nqpts);
CeedElemRestrictionCreateIdentity(ceed, nelem, nqpts,
nqpts * nelem, dim * (dim + 1) / 2, &ceedData.restr_i);
CeedElemRestrictionCreateIdentity(ceed, nelem, nqpts,
nqpts * nelem, 1, &ceedData.mesh_restr_i);
CeedVectorCreate(ceed, mesh->GetNodes()->Size(), &ceedData.node_coords);
CeedVectorSetArray(ceedData.node_coords, CEED_MEM_HOST, CEED_USE_POINTER,
mesh->GetNodes()->GetData());
CeedVectorCreate(ceed, nelem * nqpts * dim * (dim + 1) / 2, &ceedData.rho);
// Context data to be passed to the 'f_build_diff' Q-function.
ceedData.build_ctx.dim = mesh->Dimension();
ceedData.build_ctx.space_dim = mesh->SpaceDimension();
std::string diff_qf_file = GetCeedPath() + "/diffusion.h";
std::string diff_qf;
// Create the Q-function that builds the diff operator (i.e. computes its
// quadrature data) and set its context data.
switch (ceedData.coeff_type)
{
case CeedCoeff::Const:
diff_qf = diff_qf_file + ":f_build_diff_const";
CeedQFunctionCreateInterior(ceed, 1, f_build_diff_const,
diff_qf.c_str(),
&ceedData.build_qfunc);
ceedData.build_ctx.coeff = ((CeedConstCoeff*)ceedData.coeff)->val;
break;
case CeedCoeff::Grid:
diff_qf = diff_qf_file + ":f_build_diff_grid";
CeedQFunctionCreateInterior(ceed, 1, f_build_diff_grid,
diff_qf.c_str(),
&ceedData.build_qfunc);
CeedQFunctionAddInput(ceedData.build_qfunc, "coeff", 1, CEED_EVAL_INTERP);
break;
default:
MFEM_ABORT("This coeff_type is not handled");
}
CeedQFunctionAddInput(ceedData.build_qfunc, "dx", dim * dim, CEED_EVAL_GRAD);
CeedQFunctionAddInput(ceedData.build_qfunc, "weights", 1, CEED_EVAL_WEIGHT);
CeedQFunctionAddOutput(ceedData.build_qfunc, "rho", dim * (dim + 1) / 2,
CEED_EVAL_NONE);
CeedQFunctionSetContext(ceedData.build_qfunc, &ceedData.build_ctx,
sizeof(ceedData.build_ctx));
// Create the operator that builds the quadrature data for the diff operator.
CeedOperatorCreate(ceed, ceedData.build_qfunc, NULL, NULL,
&ceedData.build_oper);
CeedTransposeMode lmode = CEED_NOTRANSPOSE;
if (mesh_fes->GetOrdering()==Ordering::byVDIM)
{
lmode = CEED_TRANSPOSE;
}
if (ceedData.coeff_type==CeedCoeff::Grid)
{
CeedGridCoeff* ceedCoeff = (CeedGridCoeff*)ceedData.coeff;
InitCeedTensorBasisAndRestriction(*ceedCoeff->coeff->FESpace(), ir, ceed, &ceedCoeff->basis,
&ceedCoeff->restr);
CeedVectorCreate(ceed, ceedCoeff->coeff->FESpace()->GetNDofs(),
&ceedCoeff->coeffVector);
CeedVectorSetArray(ceedCoeff->coeffVector, CEED_MEM_HOST, CEED_USE_POINTER,
ceedCoeff->coeff->GetData());
CeedOperatorSetField(ceedData.build_oper, "coeff", ceedCoeff->restr,
CEED_NOTRANSPOSE, ceedCoeff->basis, ceedCoeff->coeffVector);
}
CeedOperatorSetField(ceedData.build_oper, "dx", ceedData.mesh_restr, lmode,
ceedData.mesh_basis, CEED_VECTOR_ACTIVE);
CeedOperatorSetField(ceedData.build_oper, "weights", ceedData.mesh_restr_i,
CEED_NOTRANSPOSE,
ceedData.mesh_basis, CEED_VECTOR_NONE);
CeedOperatorSetField(ceedData.build_oper, "rho", ceedData.restr_i,
CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
// Compute the quadrature data for the diff operator.
CeedOperatorApply(ceedData.build_oper, ceedData.node_coords, ceedData.rho,
CEED_REQUEST_IMMEDIATE);
// Create the Q-function that defines the action of the diff operator.
diff_qf = diff_qf_file + ":f_apply_diff";
CeedQFunctionCreateInterior(ceed, 1, f_apply_diff,
diff_qf.c_str(),
&ceedData.apply_qfunc);
CeedQFunctionAddInput(ceedData.apply_qfunc, "u", dim, CEED_EVAL_GRAD);
CeedQFunctionAddInput(ceedData.apply_qfunc, "rho", dim * (dim + 1) / 2,
CEED_EVAL_NONE);
CeedQFunctionAddOutput(ceedData.apply_qfunc, "v", dim, CEED_EVAL_GRAD);
CeedQFunctionSetContext(ceedData.apply_qfunc, &ceedData.build_ctx,
sizeof(ceedData.build_ctx));
// Create the diff operator.
CeedOperatorCreate(ceed, ceedData.apply_qfunc, NULL, NULL, &ceedData.oper);
CeedOperatorSetField(ceedData.oper, "u", ceedData.restr, CEED_NOTRANSPOSE,
ceedData.basis, CEED_VECTOR_ACTIVE);
CeedOperatorSetField(ceedData.oper, "rho", ceedData.restr_i, CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, ceedData.rho);
CeedOperatorSetField(ceedData.oper, "v", ceedData.restr, CEED_NOTRANSPOSE,
ceedData.basis, CEED_VECTOR_ACTIVE);
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.u);
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.v);
}
#endif
}
+200
View File
@@ -0,0 +1,200 @@
// 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.
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
/// libCEED Q-function for building quadrature data for a diffusion operator with a constant coefficient
CEED_QFUNCTION(f_build_diff_const)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext*)ctx;
// in[0] is Jacobians with shape [dim, nc=dim, Q]
// in[1] is quadrature weights, size (Q)
//
// At every quadrature point, compute qw/det(J).adj(J).adj(J)^T and store
// the symmetric part of the result.
const CeedScalar coeff = bc->coeff;
const CeedScalar *J = in[0], *qw = in[1];
CeedScalar *qd = out[0];
switch (bc->dim + 10 * bc->space_dim)
{
case 11:
for (CeedInt i = 0; i < Q; i++)
{
qd[i] = coeff * qw[i] / J[i];
}
break;
case 22:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J12 = J[i + Q * 2];
const CeedScalar J22 = J[i + Q * 3];
const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
qd[i + Q * 0] = coeff * w * (J12 * J12 + J22 * J22);
qd[i + Q * 1] = - coeff * w * (J11 * J12 + J21 * J22);
qd[i + Q * 2] = coeff * w * (J11 * J11 + J21 * J21);
}
break;
case 33:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J31 = J[i + Q * 2];
const CeedScalar J12 = J[i + Q * 3];
const CeedScalar J22 = J[i + Q * 4];
const CeedScalar J32 = J[i + Q * 5];
const CeedScalar J13 = J[i + Q * 6];
const CeedScalar J23 = J[i + Q * 7];
const CeedScalar J33 = J[i + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[i] / (J11 * A11 + J21 * A12 + J31 * A13);
qd[i + Q * 0] = coeff * w * (A11 * A11 + A12 * A12 + A13 * A13);
qd[i + Q * 1] = coeff * w * (A11 * A21 + A12 * A22 + A13 * A23);
qd[i + Q * 2] = coeff * w * (A11 * A31 + A12 * A32 + A13 * A33);
qd[i + Q * 3] = coeff * w * (A21 * A21 + A22 * A22 + A23 * A23);
qd[i + Q * 4] = coeff * w * (A21 * A31 + A22 * A32 + A23 * A33);
qd[i + Q * 5] = coeff * w * (A31 * A31 + A32 * A32 + A33 * A33);
}
break;
}
return 0;
}
/// libCEED Q-function for building quadrature data for a diffusion operator with a grid function coefficient
CEED_QFUNCTION(f_build_diff_grid)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext *)ctx;
// in[1] is Jacobians with shape [dim, nc=dim, Q]
// in[2] is quadrature weights, size (Q)
//
// At every quadrature point, compute qw/det(J).adj(J).adj(J)^T and store
// the symmetric part of the result.
const CeedScalar *c = in[0], *J = in[1], *qw = in[2];
CeedScalar *qd = out[0];
switch (bc->dim + 10 * bc->space_dim)
{
case 11:
for (CeedInt i = 0; i < Q; i++)
{
qd[i] = c[i] * qw[i] / J[i];
}
break;
case 22:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
const CeedScalar coeff = c[i];
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J12 = J[i + Q * 2];
const CeedScalar J22 = J[i + Q * 3];
const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
qd[i + Q * 0] = coeff * w * (J12 * J12 + J22 * J22);
qd[i + Q * 1] = - coeff * w * (J11 * J12 + J21 * J22);
qd[i + Q * 2] = coeff * w * (J11 * J11 + J21 * J21);
}
break;
case 33:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar coeff = c[i];
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J31 = J[i + Q * 2];
const CeedScalar J12 = J[i + Q * 3];
const CeedScalar J22 = J[i + Q * 4];
const CeedScalar J32 = J[i + Q * 5];
const CeedScalar J13 = J[i + Q * 6];
const CeedScalar J23 = J[i + Q * 7];
const CeedScalar J33 = J[i + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[i] / (J11 * A11 + J21 * A12 + J31 * A13);
qd[i + Q * 0] = coeff * w * (A11 * A11 + A12 * A12 + A13 * A13);
qd[i + Q * 1] = coeff * w * (A11 * A21 + A12 * A22 + A13 * A23);
qd[i + Q * 2] = coeff * w * (A11 * A31 + A12 * A32 + A13 * A33);
qd[i + Q * 3] = coeff * w * (A21 * A21 + A22 * A22 + A23 * A23);
qd[i + Q * 4] = coeff * w * (A21 * A31 + A22 * A32 + A23 * A33);
qd[i + Q * 5] = coeff * w * (A31 * A31 + A32 * A32 + A33 * A33);
}
break;
}
return 0;
}
/// libCEED Q-function for applying a diff operator
CEED_QFUNCTION(f_apply_diff)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext *)ctx;
// in[0], out[0] have shape [dim, nc=1, Q]
const CeedScalar *ug = in[0], *qd = in[1];
CeedScalar *vg = out[0];
switch (bc->dim)
{
case 1:
for (CeedInt i = 0; i < Q; i++)
{
vg[i] = ug[i] * qd[i];
}
break;
case 2:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar ug0 = ug[i + Q * 0];
const CeedScalar ug1 = ug[i + Q * 1];
vg[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 1] * ug1;
vg[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 2] * ug1;
}
break;
case 3:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar ug0 = ug[i + Q * 0];
const CeedScalar ug1 = ug[i + Q * 1];
const CeedScalar ug2 = ug[i + Q * 2];
vg[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 1] * ug1 + qd[i + Q * 2] * ug2;
vg[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 3] * ug1 + qd[i + Q * 4] * ug2;
vg[i + Q * 2] = qd[i + Q * 2] * ug0 + qd[i + Q * 4] * ug1 + qd[i + Q * 5] * ug2;
}
break;
}
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
// 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_LIBCEED_DIFF_HPP
#define MFEM_LIBCEED_DIFF_HPP
#include "ceed.hpp"
#include "../fespace.hpp"
namespace mfem
{
#ifdef MFEM_USE_CEED
/// Initialize a Diffusion Integrator using libCEED
void CeedPADiffusionAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &ir, CeedData& ceedData);
#endif
}
#endif // MFEM_LIBCEED_DIFF_HPP
+141
View File
@@ -0,0 +1,141 @@
// 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 "ceed.hpp"
#include "../../general/device.hpp"
#include "mass.h"
namespace mfem
{
#ifdef MFEM_USE_CEED
void CeedPAMassAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm, CeedData& ceedData)
{
Ceed ceed(internal::ceed);
mfem::Mesh *mesh = fes.GetMesh();
const int ir_order = irm.GetOrder();
const mfem::IntegrationRule &ir =
mfem::IntRules.Get(mfem::Geometry::SEGMENT, ir_order);
CeedInt nqpts, nelem = mesh->GetNE();
InitCeedTensorBasisAndRestriction(fes, ir, ceed, &ceedData.basis, &ceedData.restr);
mesh->EnsureNodes();
const mfem::FiniteElementSpace *mesh_fes = mesh->GetNodalFESpace();
MFEM_VERIFY(mesh_fes, "the Mesh has no nodal FE space");
InitCeedTensorBasisAndRestriction(*mesh_fes, ir, ceed, &ceedData.mesh_basis, &ceedData.mesh_restr);
CeedBasisGetNumQuadraturePoints(ceedData.basis, &nqpts);
CeedElemRestrictionCreateIdentity(ceed, nelem, nqpts,
nqpts*nelem, 1, &ceedData.restr_i);
CeedElemRestrictionCreateIdentity(ceed, nelem, nqpts,
nqpts*nelem, 1, &ceedData.mesh_restr_i);
CeedVectorCreate(ceed, mesh->GetNodes()->Size(), &ceedData.node_coords);
CeedVectorSetArray(ceedData.node_coords, CEED_MEM_HOST, CEED_USE_POINTER,
mesh->GetNodes()->GetData());
CeedVectorCreate(ceed, nelem*nqpts, &ceedData.rho);
// Context data to be passed to the 'f_build_mass' Q-function.
ceedData.build_ctx.dim = mesh->Dimension();
ceedData.build_ctx.space_dim = mesh->SpaceDimension();
std::string mass_qf_file = GetCeedPath() + "/mass.h";
std::string mass_qf;
// Create the Q-function that builds the mass operator (i.e. computes its
// quadrature data) and set its context data.
switch (ceedData.coeff_type)
{
case CeedCoeff::Const:
mass_qf = mass_qf_file + ":f_build_mass_const";
CeedQFunctionCreateInterior(ceed, 1, f_build_mass_const,
mass_qf.c_str(),
&ceedData.build_qfunc);
ceedData.build_ctx.coeff = ((CeedConstCoeff*)ceedData.coeff)->val;
break;
case CeedCoeff::Grid:
mass_qf = mass_qf_file + ":f_build_mass_grid";
CeedQFunctionCreateInterior(ceed, 1, f_build_mass_grid,
mass_qf.c_str(),
&ceedData.build_qfunc);
CeedQFunctionAddInput(ceedData.build_qfunc, "coeff", 1, CEED_EVAL_INTERP);
break;
default:
MFEM_ABORT("This coeff_type is not handled");
}
CeedQFunctionAddInput(ceedData.build_qfunc, "dx", mesh->SpaceDimension()*mesh->SpaceDimension(),
CEED_EVAL_GRAD);
CeedQFunctionAddInput(ceedData.build_qfunc, "weights", 1, CEED_EVAL_WEIGHT);
CeedQFunctionAddOutput(ceedData.build_qfunc, "rho", 1, CEED_EVAL_NONE);
CeedQFunctionSetContext(ceedData.build_qfunc, &ceedData.build_ctx,
sizeof(ceedData.build_ctx));
// Create the operator that builds the quadrature data for the mass operator.
CeedOperatorCreate(ceed, ceedData.build_qfunc, NULL, NULL,
&ceedData.build_oper);
CeedTransposeMode lmode = CEED_NOTRANSPOSE;
if (mesh_fes->GetOrdering()==Ordering::byVDIM)
{
lmode = CEED_TRANSPOSE;
}
if (ceedData.coeff_type==CeedCoeff::Grid)
{
CeedGridCoeff* ceedCoeff = (CeedGridCoeff*)ceedData.coeff;
InitCeedTensorBasisAndRestriction(*ceedCoeff->coeff->FESpace(), ir, ceed, &ceedCoeff->basis,
&ceedCoeff->restr);
CeedVectorCreate(ceed, ceedCoeff->coeff->FESpace()->GetNDofs(),
&ceedCoeff->coeffVector);
CeedVectorSetArray(ceedCoeff->coeffVector, CEED_MEM_HOST, CEED_USE_POINTER,
ceedCoeff->coeff->GetData());
CeedOperatorSetField(ceedData.build_oper, "coeff", ceedCoeff->restr, CEED_NOTRANSPOSE,
ceedCoeff->basis, ceedCoeff->coeffVector);
}
CeedOperatorSetField(ceedData.build_oper, "dx", ceedData.mesh_restr, lmode,
ceedData.mesh_basis, CEED_VECTOR_ACTIVE);
CeedOperatorSetField(ceedData.build_oper, "weights", ceedData.mesh_restr_i,
CEED_NOTRANSPOSE,
ceedData.mesh_basis, CEED_VECTOR_NONE);
CeedOperatorSetField(ceedData.build_oper, "rho", ceedData.restr_i,
CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
// Compute the quadrature data for the mass operator.
CeedOperatorApply(ceedData.build_oper, ceedData.node_coords, ceedData.rho,
CEED_REQUEST_IMMEDIATE);
// Create the Q-function that defines the action of the mass operator.
mass_qf = mass_qf_file + ":f_apply_mass";
CeedQFunctionCreateInterior(ceed, 1, f_apply_mass,
mass_qf.c_str(), &ceedData.apply_qfunc);
CeedQFunctionAddInput(ceedData.apply_qfunc, "u", 1, CEED_EVAL_INTERP);
CeedQFunctionAddInput(ceedData.apply_qfunc, "rho", 1, CEED_EVAL_NONE);
CeedQFunctionAddOutput(ceedData.apply_qfunc, "v", 1, CEED_EVAL_INTERP);
// Create the mass operator.
CeedOperatorCreate(ceed, ceedData.apply_qfunc, NULL, NULL, &ceedData.oper);
CeedOperatorSetField(ceedData.oper, "u", ceedData.restr, CEED_NOTRANSPOSE,
ceedData.basis, CEED_VECTOR_ACTIVE);
CeedOperatorSetField(ceedData.oper, "rho", ceedData.restr_i, CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, ceedData.rho);
CeedOperatorSetField(ceedData.oper, "v", ceedData.restr, CEED_NOTRANSPOSE,
ceedData.basis, CEED_VECTOR_ACTIVE);
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.u);
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.v);
}
#endif
}
+107
View File
@@ -0,0 +1,107 @@
// 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.
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
/// libCEED Q-function for building quadrature data for a mass operator with a constant coefficient
CEED_QFUNCTION(f_build_mass_const)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
// in[0] is Jacobians with shape [dim, nc=dim, Q]
// in[1] is quadrature weights, size (Q)
BuildContext *bc = (BuildContext *)ctx;
const CeedScalar coeff = bc->coeff;
const CeedScalar *J = in[0], *qw = in[1];
CeedScalar *rho = out[0];
switch (bc->dim + 10*bc->space_dim)
{
case 11:
for (CeedInt i=0; i<Q; i++)
{
rho[i] = coeff * J[i] * qw[i];
}
break;
case 22:
for (CeedInt i=0; i<Q; i++)
{
// 0 2
// 1 3
rho[i] = coeff * (J[i+Q*0]*J[i+Q*3] - J[i+Q*1]*J[i+Q*2]) * qw[i];
}
break;
case 33:
for (CeedInt i=0; i<Q; i++)
{
// 0 3 6
// 1 4 7
// 2 5 8
rho[i] = (J[i+Q*0]*(J[i+Q*4]*J[i+Q*8] - J[i+Q*5]*J[i+Q*7]) -
J[i+Q*1]*(J[i+Q*3]*J[i+Q*8] - J[i+Q*5]*J[i+Q*6]) +
J[i+Q*2]*(J[i+Q*3]*J[i+Q*7] - J[i+Q*4]*J[i+Q*6])) * coeff * qw[i];
}
break;
}
return 0;
}
/// libCEED Q-function for building quadrature data for a mass operator with a grid function coefficient
CEED_QFUNCTION(f_build_mass_grid)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
// in[0] is Jacobians with shape [dim, nc=dim, Q]
// in[1] is quadrature weights, size (Q)
BuildContext *bc = (BuildContext *)ctx;
const CeedScalar *c = in[0], *J = in[1], *qw = in[2];
CeedScalar *rho = out[0];
switch (bc->dim + 10*bc->space_dim)
{
case 11:
for (CeedInt i=0; i<Q; i++)
{
rho[i] = c[i] * J[i] * qw[i];
}
break;
case 22:
for (CeedInt i=0; i<Q; i++)
{
// 0 2
// 1 3
rho[i] = c[i] * (J[i+Q*0]*J[i+Q*3] - J[i+Q*1]*J[i+Q*2]) * qw[i];
}
break;
case 33:
for (CeedInt i=0; i<Q; i++)
{
// 0 3 6
// 1 4 7
// 2 5 8
rho[i] = (J[i+Q*0]*(J[i+Q*4]*J[i+Q*8] - J[i+Q*5]*J[i+Q*7]) -
J[i+Q*1]*(J[i+Q*3]*J[i+Q*8] - J[i+Q*5]*J[i+Q*6]) +
J[i+Q*2]*(J[i+Q*3]*J[i+Q*7] - J[i+Q*4]*J[i+Q*6])) * c[i] * qw[i];
}
break;
}
return 0;
}
/// libCEED Q-function for applying a mass operator
CEED_QFUNCTION(f_apply_mass)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
const CeedScalar *u = in[0], *w = in[1];
CeedScalar *v = out[0];
for (CeedInt i=0; i<Q; i++)
{
v[i] = w[i] * u[i];
}
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
// 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_LIBCEED_MASS_HPP
#define MFEM_LIBCEED_MASS_HPP
#include "ceed.hpp"
#include "../fespace.hpp"
namespace mfem
{
#ifdef MFEM_USE_CEED
/// Initialize a Mass Integrator using libCEED
void CeedPAMassAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &ir, CeedData& ceedData);
#endif
}
#endif // MFEM_LIBCEED_MASS_HPP
+128
View File
@@ -0,0 +1,128 @@
// 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 "ceed.hpp"
#include "../../general/device.hpp"
#include "mechanics.h"
namespace mfem
{
#ifdef MFEM_USE_CEED
void CeedPAMechanicsAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm,
const double* ktan_ptr,
CeedData& ceedData)
{
Ceed ceed(internal::ceed);
mfem::Mesh *mesh = fes.GetMesh();
const int ir_order = irm.GetOrder();
const mfem::IntegrationRule &ir =
mfem::IntRules.Get(mfem::Geometry::SEGMENT, ir_order);
CeedInt nqpts, nelem = mesh->GetNE(), dim = mesh->SpaceDimension();
mesh->EnsureNodes();
InitCeedTensorBasisAndRestriction(fes, ir, ceed, &ceedData.basis, &ceedData.restr);
const mfem::FiniteElementSpace *mesh_fes = mesh->GetNodalFESpace();
MFEM_VERIFY(mesh_fes, "the Mesh has no nodal FE space");
InitCeedTensorBasisAndRestriction(*mesh_fes, ir, ceed, &ceedData.mesh_basis, &ceedData.mesh_restr);
CeedBasisGetNumQuadraturePoints(ceedData.basis, &nqpts);
CeedElemRestrictionCreateIdentity(ceed, nelem, nqpts,
nqpts * nelem, dim * dim * dim * dim, &ceedData.restr_i);
CeedElemRestrictionCreateIdentity(ceed, nelem, nqpts,
nqpts * nelem, 1, &ceedData.mesh_restr_i);
CeedVectorCreate(ceed, mesh->GetNodes()->Size(), &ceedData.node_coords);
CeedVectorSetArray(ceedData.node_coords, CEED_MEM_HOST, CEED_USE_POINTER,
mesh->GetNodes()->GetData());
CeedVectorCreate(ceed, nelem * nqpts * dim * dim * dim * dim, &ceedData.rho);
// Context data to be passed to the 'f_build_diff' Q-function.
ceedData.build_ctx.dim = mesh->Dimension();
ceedData.build_ctx.space_dim = mesh->SpaceDimension();
std::string mech_qf_file = GetCeedPath() + "/mechanics.h";
std::string mech_qf;
// Create the Q-function that builds the diff operator (i.e. computes its
// quadrature data) and set its context data.
mech_qf = mech_qf_file + ":f_build_mech";
CeedQFunctionCreateInterior(ceed, 1, f_build_mech,
mech_qf.c_str(),
&ceedData.build_qfunc);
CeedQFunctionAddInput(ceedData.build_qfunc, "ktan", dim * dim * dim * dim,
CEED_EVAL_NONE);
CeedQFunctionAddInput(ceedData.build_qfunc, "dx", dim * dim, CEED_EVAL_GRAD);
CeedQFunctionAddInput(ceedData.build_qfunc, "weights", 1, CEED_EVAL_WEIGHT);
CeedQFunctionAddOutput(ceedData.build_qfunc, "qd", dim * dim * dim * dim,
CEED_EVAL_NONE);
CeedQFunctionSetContext(ceedData.build_qfunc, &ceedData.build_ctx,
sizeof(ceedData.build_ctx));
// Create the operator that builds the quadrature data for the diff operator.
CeedOperatorCreate(ceed, ceedData.build_qfunc, NULL, NULL,
&ceedData.build_oper);
CeedTransposeMode lmode = CEED_NOTRANSPOSE;
if (mesh_fes->GetOrdering()==Ordering::byVDIM)
{
lmode = CEED_TRANSPOSE;
}
CeedVector ktan;
CeedVectorCreate(ceed, nelem * nqpts * dim * dim * dim * dim, &ktan);
CeedVectorSetArray(ktan, CEED_MEM_DEVICE, CEED_USE_POINTER, const_cast<double*>(ktan_ptr));
CeedOperatorSetField(ceedData.build_oper, "ktan", ceedData.restr_i,
CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, ktan);
CeedOperatorSetField(ceedData.build_oper, "dx", ceedData.mesh_restr, lmode,
ceedData.mesh_basis, CEED_VECTOR_ACTIVE);
CeedOperatorSetField(ceedData.build_oper, "weights", ceedData.mesh_restr_i,
CEED_NOTRANSPOSE,
ceedData.mesh_basis, CEED_VECTOR_NONE);
CeedOperatorSetField(ceedData.build_oper, "qd", ceedData.restr_i,
CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
// Compute the quadrature data for the diff operator.
CeedOperatorApply(ceedData.build_oper, ceedData.node_coords, ceedData.rho,
CEED_REQUEST_IMMEDIATE);
// Create the Q-function that defines the action of the diff operator.
mech_qf = mech_qf_file + ":f_apply_mech";
CeedQFunctionCreateInterior(ceed, 1, f_apply_mech,
mech_qf.c_str(),
&ceedData.apply_qfunc);
CeedQFunctionAddInput(ceedData.apply_qfunc, "u", dim * dim, CEED_EVAL_GRAD);
CeedQFunctionAddInput(ceedData.apply_qfunc, "qd", dim * dim * dim * dim,
CEED_EVAL_NONE);
CeedQFunctionAddOutput(ceedData.apply_qfunc, "v", dim * dim, CEED_EVAL_GRAD);
CeedQFunctionSetContext(ceedData.apply_qfunc, &ceedData.build_ctx,
sizeof(ceedData.build_ctx));
// Create the diff operator.
CeedOperatorCreate(ceed, ceedData.apply_qfunc, NULL, NULL, &ceedData.oper);
CeedOperatorSetField(ceedData.oper, "u", ceedData.restr, CEED_NOTRANSPOSE,
ceedData.basis, CEED_VECTOR_ACTIVE);
CeedOperatorSetField(ceedData.oper, "qd", ceedData.restr_i, CEED_NOTRANSPOSE,
CEED_BASIS_COLLOCATED, ceedData.rho);
CeedOperatorSetField(ceedData.oper, "v", ceedData.restr, CEED_NOTRANSPOSE,
ceedData.basis, CEED_VECTOR_ACTIVE);
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.u);
CeedVectorCreate(ceed, fes.GetNDofs(), &ceedData.v);
}
#endif
}
+181
View File
@@ -0,0 +1,181 @@
// 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.
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
/// libCEED Q-function for building quadrature data for a diffusion operator with a constant coefficient
CEED_QFUNCTION(f_build_mech)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext*)ctx;
// in[0] is ktan
// in[1] is Jacobians with shape [dim, nc=dim, Q]
// in[2] is quadrature weights, size (Q)
//
// At every quadrature point, compute qw/det(J).adj(J).adj(J)^T and store
// the symmetric part of the result.
const CeedScalar *ktan = in[0], *J = in[1], *qw = in[2];
CeedScalar *qd = out[0];
switch (bc->dim + 10 * bc->space_dim)
{
case 11:
for (CeedInt i = 0; i < Q; i++)
{
qd[i] = ktan[i] * qw[i] / J[i];
}
break;
case 22:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
// const CeedScalar J11 = J[i + Q * 0];
// const CeedScalar J21 = J[i + Q * 1];
// const CeedScalar J12 = J[i + Q * 2];
// const CeedScalar J22 = J[i + Q * 3];
// const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
// qd[i + Q * 0] = coeff * w * (J12 * J12 + J22 * J22);
// qd[i + Q * 1] = - coeff * w * (J11 * J12 + J21 * J22);
// qd[i + Q * 2] = coeff * w * (J11 * J11 + J21 * J21);
//TODO
}
break;
case 33:
for (CeedInt q = 0; q < Q; q++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar J11 = J[q + Q * 0];
const CeedScalar J21 = J[q + Q * 1];
const CeedScalar J31 = J[q + Q * 2];
const CeedScalar J12 = J[q + Q * 3];
const CeedScalar J22 = J[q + Q * 4];
const CeedScalar J32 = J[q + Q * 5];
const CeedScalar J13 = J[q + Q * 6];
const CeedScalar J23 = J[q + Q * 7];
const CeedScalar J33 = J[q + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[q] / (J11 * A11 + J21 * A12 + J31 * A13);
// Load ktan
CeedScalar K[3][3][3][3];
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
for (int l = 0; l < 3; ++l) {
for (int m = 0; m < 3; ++m) {
K[j][k][l][m] = ktan [q + (j + k*3 + l*3*3 + m*3*3*3) * Q];
}
}
}
}
// ktan*J^-1
CeedScalar tmp[3][3][3][3];
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
for (int l = 0; l < 3; ++l) {
tmp[j][k][l][0] = K[j][k][l][0] * A11 + K[j][k][l][1] * A21 + K[j][k][l][2] * A31;
tmp[j][k][l][1] = K[j][k][l][0] * A12 + K[j][k][l][1] * A22 + K[j][k][l][2] * A32;
tmp[j][k][l][2] = K[j][k][l][0] * A13 + K[j][k][l][1] * A23 + K[j][k][l][2] * A33;
}
}
}
// J^-T*ktan*J^-1
for (int k = 0; k < 3; ++k) {
for (int l = 0; l < 3; ++l) {
for (int n = 0; n < 3; ++n) {
qd[q + (0 + k*3 + l*3*3 + n*3*3*3)*Q] = w * (A11 * tmp[0][k][l][n] + A21 * tmp[1][k][l][n] + A31 * tmp[2][k][l][n]);
qd[q + (1 + k*3 + l*3*3 + n*3*3*3)*Q] = w * (A12 * tmp[0][k][l][n] + A22 * tmp[1][k][l][n] + A32 * tmp[2][k][l][n]);
qd[q + (2 + k*3 + l*3*3 + n*3*3*3)*Q] = w * (A13 * tmp[0][k][l][n] + A23 * tmp[1][k][l][n] + A33 * tmp[2][k][l][n]);
}
}
}
}
break;
}
return 0;
}
/// libCEED Q-function for applying a diff operator
CEED_QFUNCTION(f_apply_mech)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext *)ctx;
// in[0], out[0] have shape [dim, nc=1, Q]
const CeedScalar *ug = in[0], *qd = in[1];
CeedScalar *vg = out[0];
switch (bc->dim)
{
case 1:
for (CeedInt i = 0; i < Q; i++)
{
vg[i] = ug[i] * qd[i];
}
break;
case 2:
// for (CeedInt i = 0; i < Q; i++)
// {
// const CeedScalar ug0 = ug[i + Q * 0];
// const CeedScalar ug1 = ug[i + Q * 1];
// vg[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 1] * ug1;
// vg[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 2] * ug1;
// }
break;
case 3:
for (CeedInt q = 0; q < Q; q++)
{
// Read spatial derivatives of u components
const CeedScalar uJ[3][3] = {{ug[q+(0+0*3)*Q],
ug[q+(0+1*3)*Q],
ug[q+(0+2*3)*Q]},
{ug[q+(1+0*3)*Q],
ug[q+(1+1*3)*Q],
ug[q+(1+2*3)*Q]},
{ug[q+(2+0*3)*Q],
ug[q+(2+1*3)*Q],
ug[q+(2+2*3)*Q]}
};
// Load quadrature data
CeedScalar K[3][3][3][3];
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
for (int l = 0; l < 3; ++l) {
for (int m = 0; m < 3; ++m) {
K[j][k][l][m] = qd [q + (j + k*3 + l*3*3 + m*3*3*3) * Q];
}
}
}
}
// double contraction
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 3; ++k) {
vg[q + (j+k*3)*Q] = 0.0;
for (int l = 0; l < 3; ++l) {
for (int m = 0; m < 3; ++m) {
vg[q + (j+k*3)*Q] += K[j][k][l][m] * uJ[m][l];
}
}
}
}
}
break;
}
return 0;
}
+33
View File
@@ -0,0 +1,33 @@
// 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_LIBCEED_MECH_HPP
#define MFEM_LIBCEED_MECH_HPP
#include "ceed.hpp"
#include "../fespace.hpp"
namespace mfem
{
#ifdef MFEM_USE_CEED
/// Initialize a Mechanics Integrator using libCEED
void CeedPAMechanicsAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm,
const double* ktan_ptr,
CeedData& ceedData);
#endif
}
#endif // MFEM_LIBCEED_NECH_HPP
-4
View File
@@ -138,11 +138,7 @@ void LinearForm::Assemble()
eltrans = fes -> GetBdrElementTransformation (i);
for (int k=0; k < blfi.Size(); k++)
{
if (blfi_marker[k] &&
(*blfi_marker[k])[bdr_attr-1] == 0) { continue; }
blfi[k]->AssembleRHSElementVect(*fes->GetBE(i), *eltrans, elemvect);
AddElementVector (vdofs, elemvect);
}
}
+6 -12
View File
@@ -350,6 +350,7 @@ void VectorFEDomainLFIntegrator::AssembleDeltaElementVect(
vshape.Mult(vec, elvect);
}
void VectorBoundaryFluxLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
@@ -396,26 +397,19 @@ void VectorFEBoundaryFluxLFIntegrator::AssembleRHSElementVect(
if (ir == NULL)
{
int intorder = 2*el.GetOrder(); // <----------
if (F == NULL)
{
intorder -= el.GetOrder() + 1;
}
ir = &IntRules.Get(el.GetGeomType(), intorder);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint (&ip);
double val = ip.weight*F.Eval(Tr, ip);
el.CalcShape(ip, shape);
double val = ip.weight;
if (F)
{
Tr.SetIntPoint (&ip);
val *= F->Eval(Tr, ip);
}
elvect.Add(val, shape);
add(elvect, val, shape, elvect);
}
}
+2 -3
View File
@@ -279,12 +279,11 @@ public:
class VectorFEBoundaryFluxLFIntegrator : public LinearFormIntegrator
{
private:
Coefficient *F;
Coefficient &F;
Vector shape;
public:
VectorFEBoundaryFluxLFIntegrator() : F(NULL) { }
VectorFEBoundaryFluxLFIntegrator(Coefficient &f) : F(&f) { }
VectorFEBoundaryFluxLFIntegrator(Coefficient &f) : F(f) { }
virtual void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
+3 -72
View File
@@ -65,8 +65,6 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
Vector el_x;
const FiniteElement *fe;
ElementTransformation *T;
Mesh *mesh = fes->GetMesh();
double energy = 0.0;
if (dnfi.Size())
@@ -86,81 +84,14 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
if (fnfi.Size())
{
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
Array<int> vdofs2;
for (int i = 0; i < mesh->GetNumFaces(); i++)
{
tr = mesh->GetInteriorFaceTransformations(i);
if (tr != NULL)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
fes->GetElementVDofs(tr->Elem2No, vdofs2);
vdofs.Append (vdofs2);
x.GetSubVector(vdofs, el_x);
fe1 = fes->GetFE(tr->Elem1No);
fe2 = fes->GetFE(tr->Elem2No);
for (int k = 0; k < fnfi.Size(); k++)
{
energy += fnfi[k]->GetFaceEnergy(*fe1, *fe2, *tr, el_x);
}
}
}
MFEM_ABORT("TODO: add energy contribution from interior face terms");
}
if (bfnfi.Size())
{
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bfnfi.Size(); k++)
{
if (bfnfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bfnfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary face integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
tr = mesh->GetBdrFaceTransformations (i);
if (tr != NULL)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
x.GetSubVector(vdofs, el_x);
fe1 = fes->GetFE(tr->Elem1No);
// The fe2 object is really a dummy and not used on the boundaries,
// but we can't dereference a NULL pointer, and we don't want to
// actually make a fake element.
fe2 = fe1;
for (int k = 0; k < bfnfi.Size(); k++)
{
if (bfnfi_marker[k] &&
(*bfnfi_marker[k])[bdr_attr-1] == 0) { continue; }
energy += bfnfi[k]->GetFaceEnergy(*fe1, *fe2, *tr, el_x);
}
}
}
MFEM_ABORT("TODO: add energy contribution from boundary face terms");
}
return energy;
}
+1 -1
View File
@@ -111,7 +111,7 @@ public:
be fes->GetVSize(). */
double GetGridFunctionEnergy(const Vector &x) const;
/// Compute the energy corresponding to the state @a x.
/// Compute the enery corresponding to the state @a x.
/** In general, @a x may have non-homogeneous essential boundary values.
The state @a x must be a true-dof vector. */
-8
View File
@@ -55,14 +55,6 @@ double NonlinearFormIntegrator::GetElementEnergy(
return 0.0;
}
double NonlinearFormIntegrator::GetFaceEnergy(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Tr, const Vector &elfun)
{
mfem_error("NonlinearFormIntegrator::GetFaceEnergy"
" is not overloaded!");
return 0.0;
}
void BlockNonlinearFormIntegrator::AssembleElementVector(
const Array<const FiniteElement *> &el,
+1 -7
View File
@@ -63,17 +63,11 @@ public:
FaceElementTransformations &Tr,
const Vector &elfun, DenseMatrix &elmat);
/// Compute the local energy/functional
/// Compute the local energy
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun);
/// Compute the face(s) contribution to the energy/functional
virtual double GetFaceEnergy(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Tr,
const Vector &elfun);
virtual ~NonlinearFormIntegrator() { }
};
+6 -13
View File
@@ -38,24 +38,19 @@ 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* Coeff2D_t @dim(Q2D, NE);
typedef double* Coeff3D_t @dim(Q3D, NE);
typedef double* SymmOperator2D_t @dim(Q2D, 3, NE);
typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
@kernel void DiffusionSetup2D(const int NE,
@restrict const double *W,
@restrict const Jacobian2D_t J,
@restrict const Coeff2D_t C,
@restrict SymmOperator2D_t op,
const bool const_c) {
const double COEFF,
@restrict SymmOperator2D_t op) {
for (int e = 0; e < NE; ++e; @outer) {
for (int q = 0; q < Q2D; ++q; @inner) {
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e);
const double J21 = J(q, 0, 1, e), J22 = J(q, 1, 1, e);
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / ((J11 * J22) - (J21 * J12));
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)
@@ -66,9 +61,8 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
@kernel void DiffusionSetup3D(const int NE,
@restrict const double *W,
@restrict const Jacobian3D_t J,
@restrict const Coeff3D_t C,
@restrict SymmOperator3D_t op,
const bool const_c) {
const double COEFF,
@restrict SymmOperator3D_t op) {
for (int e = 0; e < NE; ++e; @outer) {
for (int q = 0; q < Q3D; ++q; @inner) {
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e), J13 = J(q, 2, 0, e);
@@ -78,8 +72,7 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
const double detJ = ((J11 * J22 * J33) + (J12 * J23 * J31) + (J13 * J21 * J32) -
(J13 * J22 * J31) - (J12 * J21 * J33) - (J11 * J23 * J32));
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / detJ;
const double c_detJ = W[q] * COEFF / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
+44 -110
View File
@@ -98,8 +98,6 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
gcomm = NULL;
gfdofs = NULL;
P = NULL;
Pconf = NULL;
R = NULL;
@@ -150,37 +148,20 @@ void ParFiniteElementSpace::Construct()
// cut space.
ConstructTrueDofs();
ngedofs = ngfdofs = 0;
gfdofs = NULL;
// calculate number of ghost DOFs
ngvdofs = pncmesh->GetNGhostVertices()
* fec->DofForGeometry(Geometry::POINT);
ngedofs = ngfdofs = 0;
if (pmesh->Dimension() > 1)
{
ngedofs = pncmesh->GetNGhostEdges()
* fec->DofForGeometry(Geometry::SEGMENT);
}
if (pmesh->Dimension() > 2)
{
if (fdofs != NULL) // have mixed faces
{
gfdofs = new int[pncmesh->GetNGhostFaces()+1];
gfdofs[0] = 0;
for (int i = 0; i < pncmesh->GetNGhostFaces(); i++)
{
int ghost = pncmesh->GetNFaces() + i;
ngfdofs += fec->DofForGeometry(pncmesh->GetFaceGeometry(ghost));
gfdofs[i+1] = ngfdofs;
}
}
else
{
ngfdofs = pncmesh->GetNGhostFaces()
* fec->DofForGeometry(pncmesh->GetFaceGeometry(0));
}
ngfdofs = pncmesh->GetNGhostFaces()
* fec->DofForGeometry(pncmesh->GetGhostFaceGeometry(0));
}
// total number of ghost DOFs. Ghost DOFs start at index 'ndofs', i.e.,
@@ -1343,18 +1324,20 @@ void ParFiniteElementSpace::GetGhostEdgeDofs(const MeshId &edge_id,
void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
Array<int> &dofs) const
{
int nfv, V[4], E[4], Eo[4];
nfv = pmesh->pncmesh->GetFaceVerticesEdges(face_id, V, E, Eo);
const int ghost_face_index = face_id.index - pncmesh->GetNFaces();
MFEM_ASSERT(pncmesh->GetGhostFaceGeometry(ghost_face_index)
== Geometry::SQUARE, "");
int nv = fec->DofForGeometry(Geometry::POINT);
int ne = fec->DofForGeometry(Geometry::SEGMENT);
int nf = fec->DofForGeometry((nfv == 3) ?
Geometry::TRIANGLE : Geometry::SQUARE);
int nf = fec->DofForGeometry(Geometry::SQUARE);
dofs.SetSize(4*nv + 4*ne + nf);
dofs.SetSize(nfv*(nv + ne) + nf);
int V[4], E[4], Eo[4];
pmesh->pncmesh->GetFaceVerticesEdges(face_id, V, E, Eo);
int offset = 0;
for (int i = 0; i < nfv; i++)
for (int i = 0; i < 4; i++)
{
int ghost = pncmesh->GetNVertices();
int first = (V[i] < ghost) ? V[i]*nv : (ndofs + (V[i] - ghost)*nv);
@@ -1364,7 +1347,7 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
}
}
for (int i = 0; i < nfv; i++)
for (int i = 0; i < 4; i++)
{
int ghost = pncmesh->GetNEdges();
int first = (E[i] < ghost) ? nvdofs + E[i]*ne
@@ -1377,10 +1360,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
}
}
const int ghost_face_index = face_id.index - pncmesh->GetNFaces();
int first = ndofs + ngvdofs + ngedofs;
first += gfdofs ? gfdofs[ghost_face_index] : nf*ghost_face_index;
// Assuming all ghost faces have the same number of dofs:
int first = ndofs + ngvdofs + ngedofs + ghost_face_index*nf;
for (int j = 0; j < nf; j++)
{
dofs[offset++] = first + j;
@@ -1422,19 +1403,12 @@ void ParFiniteElementSpace::GetBareDofs(int entity, int index,
break;
default:
ned = fec->DofForGeometry(pncmesh->GetFaceGeometry(index));
MFEM_ASSERT(!pmesh->HasGeometry(Geometry::TRIANGLE), "");
ned = fec->DofForGeometry(Geometry::SQUARE);
ghost = pncmesh->GetNFaces();
if (index < ghost) // regular face
{
first = nvdofs + nedofs + (fdofs ? fdofs[index] : index*ned);
}
else // ghost face
{
index -= ghost;
first = ndofs + ngvdofs + ngedofs +
(gfdofs ? gfdofs[index] : index*ned);
}
first = (index < ghost)
? nvdofs + nedofs + index*ned // regular face
: ndofs + ngvdofs + ngedofs + (index - ghost)*ned; // ghost
break;
}
@@ -1470,30 +1444,16 @@ int ParFiniteElementSpace::PackDof(int entity, int index, int edof) const
: ndofs + ngvdofs + (index - ghost)*ned + edof; // ghost edge
default:
MFEM_ASSERT(!pmesh->HasGeometry(Geometry::TRIANGLE), "");
ghost = pncmesh->GetNFaces();
ned = fec->DofForGeometry(pncmesh->GetFaceGeometry(index));
ned = fec->DofForGeometry(Geometry::SQUARE);
if (index < ghost) // regular face
{
return nvdofs + nedofs + (fdofs ? fdofs[index] : index*ned) + edof;
}
else // ghost face
{
index -= ghost;
return ndofs + ngvdofs + ngedofs +
(gfdofs ? gfdofs[index] : index*ned) + edof;
}
return (index < ghost)
? nvdofs + nedofs + index*ned + edof // regular face
: ndofs + ngvdofs + ngedofs + (index - ghost)*ned + edof; //ghost
}
}
static int bisect(int* array, int size, int value)
{
int* end = array + size;
int* pos = std::upper_bound(array, end, value);
MFEM_VERIFY(pos != end, "value not found");
return pos - array;
}
/** Dissect a DOF number to obtain the entity type (0=vertex, 1=edge, 2=face),
* entity index and the DOF number within the entity.
*/
@@ -1519,17 +1479,9 @@ void ParFiniteElementSpace::UnpackDof(int dof,
dof -= nedofs;
if (dof < nfdofs) // regular face
{
if (fdofs) // have mixed faces
{
index = bisect(fdofs+1, mesh->GetNFaces(), dof);
edof = dof - fdofs[index];
}
else // uniform faces
{
int nf = fec->DofForGeometry(pncmesh->GetFaceGeometry(0));
index = dof / nf, edof = dof % nf;
}
entity = 2;
MFEM_ASSERT(!pmesh->HasGeometry(Geometry::TRIANGLE), "");
int nf = fec->DofForGeometry(Geometry::SQUARE);
entity = 2, index = dof / nf, edof = dof % nf;
return;
}
MFEM_ABORT("Cannot unpack internal DOF");
@@ -1553,17 +1505,8 @@ void ParFiniteElementSpace::UnpackDof(int dof,
dof -= ngedofs;
if (dof < ngfdofs) // ghost face
{
if (gfdofs) // have mixed faces
{
index = bisect(gfdofs+1, pncmesh->GetNGhostFaces(), dof);
edof = dof - gfdofs[index];
}
else // uniform faces
{
int nf = fec->DofForGeometry(pncmesh->GetFaceGeometry(0));
index = pncmesh->GetNFaces() + dof / nf, edof = dof % nf;
}
entity = 2;
int nf = fec->DofForGeometry(pncmesh->GetGhostFaceGeometry(0));
entity = 2, index = pncmesh->GetNFaces() + dof / nf, edof = dof % nf;
return;
}
MFEM_ABORT("Out of range DOF.");
@@ -1746,7 +1689,7 @@ void NeighborRowMessage::Encode(int rank)
mfem::out << "Rank " << pncmesh->MyRank << " sending to " << rank
<< ": ent " << ri.entity << ", index " << ri.index
<< ", edof " << ri.edof << " (id " << id.element << "/"
<< int(id.local) << ")" << std::endl;
<< id.local << ")" << std::endl;
#endif
// handle orientation and sign change
@@ -1789,6 +1732,8 @@ void NeighborRowMessage::Decode(int rank)
rows.clear();
rows.reserve(nrows);
Geometry::Type fgeom = pncmesh->GetFaceGeometry();
// read rows
for (int ent = 0, gi = 0; ent < 3; ent++)
{
@@ -1807,9 +1752,8 @@ void NeighborRowMessage::Decode(int rank)
}
else if (ent == 2)
{
Geometry::Type geom = pncmesh->GetFaceGeometry(id.index);
int fo = pncmesh->GetFaceOrientation(id.index);
ind = fec->DofOrderForOrientation(geom, fo);
ind = fec->DofOrderForOrientation(fgeom, fo);
}
double s = 1.0;
@@ -1898,7 +1842,7 @@ void ParFiniteElementSpace
for (int i = 0; i < dof_group.Size(); i++)
{
os << i << ": ";
if (i < (nvdofs + nedofs + nfdofs) || i >= ndofs)
if (i < (nvdofs + nedofs + nfdofs) || i > ndofs)
{
int ent, idx, edof;
UnpackDof(i, ent, idx, edof);
@@ -1980,7 +1924,15 @@ int ParFiniteElementSpace
if (!list.masters.size()) { continue; }
IsoparametricTransformation T;
DenseMatrix I;
if (entity > 1) { T.SetFE(&QuadrilateralFE); }
else { T.SetFE(&SegmentFE); }
Geometry::Type geom = (entity > 1) ?
Geometry::SQUARE : Geometry::SEGMENT;
const FiniteElement* fe = fec->FiniteElementForGeometry(geom);
if (!fe) { continue; }
DenseMatrix I(fe->GetDof());
// process masters that we own or that affect our edges/faces
for (unsigned mi = 0; mi < list.masters.size(); mi++)
@@ -1994,17 +1946,6 @@ int ParFiniteElementSpace
if (!master_dofs.Size()) { continue; }
const FiniteElement* fe = fec->FiniteElementForGeometry(mf.Geom());
if (!fe) { continue; }
switch (mf.Geom())
{
case Geometry::SQUARE: T.SetFE(&QuadrilateralFE); break;
case Geometry::TRIANGLE: T.SetFE(&TriangleFE); break;
case Geometry::SEGMENT: T.SetFE(&SegmentFE); break;
default: MFEM_ABORT("unsupported geometry");
}
// constrain slaves that exist in our mesh
for (int si = mf.slaves_begin; si < mf.slaves_end; si++)
{
@@ -2055,8 +1996,6 @@ int ParFiniteElementSpace
(l == 1) ? (const MeshId&) list.masters[i]
/* */ : (const MeshId&) list.slaves[i];
if (id.index < 0) { continue; }
GroupId owner = pncmesh->GetEntityOwnerId(entity, id.index);
GroupId group = pncmesh->GetEntityGroupId(entity, id.index);
@@ -2553,9 +2492,6 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
MFEM_VERIFY(Nonconforming(), "Not implemented for conforming meshes.");
MFEM_VERIFY(pmesh->GetNumGeometries(pmesh->Dimension()) == 1,
"Not implemented for mixed meshes.");
MFEM_VERIFY(old_dof_offsets[nrk], "Missing previous (finer) space.");
MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
"Previous space is not finer.");
@@ -2569,7 +2505,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
Vector row;
ParNCMesh* pncmesh = pmesh->pncmesh;
Geometry::Type geom = pncmesh->GetElementGeometry(0); // TODO mixed meshes
Geometry::Type geom = pncmesh->GetElementGeometry();
int ldof = fec->FiniteElementForGeometry(geom)->GetDof();
const CoarseFineTransformations &dtrans = pncmesh->GetDerefinementTransforms();
@@ -2793,8 +2729,6 @@ void ParFiniteElementSpace::Destroy()
delete Pconf; Pconf = NULL;
delete R; R = NULL;
delete [] gfdofs; gfdofs = NULL;
delete gcomm; gcomm = NULL;
num_face_nbr_dofs = -1;
+1 -2
View File
@@ -46,7 +46,6 @@ private:
/// Number of vertex/edge/face/total ghost DOFs (nonconforming case).
int ngvdofs, ngedofs, ngfdofs, ngdofs;
int* gfdofs;
/// The group of each local dof.
Array<int> ldof_group;
@@ -114,7 +113,7 @@ private:
void GetGhostFaceDofs(const MeshId &face_id, Array<int> &dofs) const;
void GetGhostDofs(int entity, const MeshId &id, Array<int> &dofs) const;
/// Return the dofs associated with the interior of the given mesh entity.
// Return the dofs associated with the interior of the given mesh entity.
void GetBareDofs(int entity, int index, Array<int> &dofs) const;
int PackDof(int entity, int index, int edof) const;
+2 -4
View File
@@ -225,13 +225,11 @@ void ParGridFunction::ExchangeFaceNbrData()
MPI_Request *recv_requests = requests + num_face_nbrs;
MPI_Status *statuses = new MPI_Status[num_face_nbrs];
const double *h_data = this->HostRead();
for (int i = 0; i < send_data.Size(); i++)
{
send_data[i] = h_data[send_ldof[i]];
send_data[i] = data[send_ldof[i]];
}
double *h_face_nbr_data = face_nbr_data.HostWrite();
for (int fn = 0; fn < num_face_nbrs; fn++)
{
int nbr_rank = pmesh->GetFaceNbrRank(fn);
@@ -241,7 +239,7 @@ void ParGridFunction::ExchangeFaceNbrData()
send_offset[fn+1] - send_offset[fn],
MPI_DOUBLE, nbr_rank, tag, MyComm, &send_requests[fn]);
MPI_Irecv(&h_face_nbr_data[recv_offset[fn]],
MPI_Irecv(&face_nbr_data(recv_offset[fn]),
recv_offset[fn+1] - recv_offset[fn],
MPI_DOUBLE, nbr_rank, tag, MyComm, &recv_requests[fn]);
}
-2
View File
@@ -112,8 +112,6 @@ public:
/// Associate a new parallel space with the ParGridFunction.
void SetSpace(ParFiniteElementSpace *f);
using GridFunction::MakeRef;
/** @brief Make the ParGridFunction reference external data on a new
FiniteElementSpace. */
/** This method changes the FiniteElementSpace associated with the
+2 -2
View File
@@ -46,7 +46,7 @@ double ParNonlinearForm::GetParGridFunctionEnergy(const Vector &x) const
void ParNonlinearForm::Mult(const Vector &x, Vector &y) const
{
NonlinearForm::Mult(x, y); // x --(P)--> aux1 --(A_local)--> aux2
Y.MakeRef(aux2, 0); // aux2 contains A_local.P.x
Y.SetData(aux2.GetData()); // aux2 contains A_local.P.x
if (fnfi.Size())
{
@@ -58,7 +58,7 @@ void ParNonlinearForm::Mult(const Vector &x, Vector &y) const
Array<int> vdofs1, vdofs2;
Vector el_x, el_y;
X.MakeRef(aux1, 0); // aux1 contains P.x
X.SetData(aux1.GetData()); // aux1 contains P.x
X.ExchangeFaceNbrData();
const int n_shared_faces = pmesh->GetNSharedFaces();
for (int i = 0; i < n_shared_faces; i++)
+8 -6
View File
@@ -16,7 +16,9 @@
#include "fem.hpp"
#include <axom/sidre.hpp>
#ifdef MFEM_USE_MPI
#include <sidre/IOManager.hpp>
#endif
#include <string>
#include <iomanip> // for setw, setfill
@@ -202,10 +204,10 @@ SidreDataCollection::get_file_path(const std::string &filename) const
axom::sidre::View *
SidreDataCollection::AllocNamedBuffer(const std::string& buffer_name,
axom::sidre::IndexType sz,
axom::sidre::SidreLength sz,
axom::sidre::TypeID type)
{
sz = std::max(sz, sidre::IndexType(0));
sz = std::max(sz, sidre::SidreLength(0));
sidre::Group *f = named_buffers_grp();
sidre::View *v = NULL;
@@ -823,7 +825,7 @@ void SidreDataCollection::Save(const std::string& filename,
void SidreDataCollection::
addScalarBasedGridFunction(const std::string &field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset)
axom::sidre::SidreLength offset)
{
sidre::Group* grp = m_bp_grp->getGroup("fields/" + field_name);
MFEM_ASSERT(grp != NULL, "field " << field_name << " does not exist");
@@ -886,7 +888,7 @@ addScalarBasedGridFunction(const std::string &field_name, GridFunction *gf,
void SidreDataCollection::
addVectorBasedGridFunction(const std::string& field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset)
axom::sidre::SidreLength offset)
{
sidre::Group* grp = m_bp_grp->getGroup("fields/" + field_name);
MFEM_ASSERT(grp != NULL, "field " << field_name << " does not exist");
@@ -1011,7 +1013,7 @@ DeregisterFieldInBPIndex(const std::string& field_name)
void SidreDataCollection::RegisterField(const std::string &field_name,
GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset)
axom::sidre::SidreLength offset)
{
if ( field_name.empty() || buffer_name.empty() ||
gf == NULL || gf->FESpace() == NULL )
+5 -5
View File
@@ -25,7 +25,7 @@
# pragma GCC diagnostic ignored "-Wpedantic"
# endif
#endif
#include <axom/sidre.hpp>
#include <sidre/sidre.hpp>
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
# pragma GCC diagnostic pop
#endif
@@ -246,7 +246,7 @@ public:
*/
void RegisterField(const std::string &field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::IndexType offset);
axom::sidre::SidreLength offset);
/// Registers an attribute field in the Sidre DataStore
/** The registration process is similar to that of RegisterField()
@@ -385,7 +385,7 @@ public:
*/
axom::sidre::View *
AllocNamedBuffer(const std::string& buffer_name,
axom::sidre::IndexType sz,
axom::sidre::SidreLength sz,
axom::sidre::TypeID type =
axom::sidre::DOUBLE_ID);
@@ -469,7 +469,7 @@ private:
void addScalarBasedGridFunction(const std::string& field_name,
GridFunction* gf,
const std::string &buffer_name,
axom::sidre::IndexType offset);
axom::sidre::SidreLength offset);
/**
* \brief A private helper function to set up the views associated with the
@@ -483,7 +483,7 @@ private:
void addVectorBasedGridFunction(const std::string& field_name,
GridFunction* gf,
const std::string &buffer_name,
axom::sidre::IndexType offset);
axom::sidre::SidreLength offset);
/** @brief A private helper function to set up the Views associated with
attribute field named @a field_name */
+13 -190
View File
@@ -12,7 +12,6 @@
#include "tmop.hpp"
#include "linearform.hpp"
#include "pgridfunc.hpp"
#include "tmop_tools.hpp"
namespace mfem
{
@@ -769,7 +768,7 @@ void TMOP_Metric_352::AssembleH(const DenseMatrix &Jpt,
void TargetConstructor::ComputeAvgVolume() const
{
MFEM_VERIFY(nodes, "Nodes are not given!");
MFEM_ASSERT(avg_volume == 0.0, "The average volume is already computed!");
MFEM_ASSERT(avg_volume == 0.0, "the average volume is already computed!");
Mesh *mesh = nodes->FESpace()->GetMesh();
const int NE = mesh->GetNE();
@@ -788,13 +787,9 @@ void TargetConstructor::ComputeAvgVolume() const
volume += ip.weight * Tr.Weight();
}
}
NCMesh *ncmesh = mesh->ncmesh;
if (Parallel() == false)
if (!Parallel())
{
avg_volume = (ncmesh == NULL) ?
volume / NE : volume / ncmesh->GetNumRootElements();
avg_volume = volume / NE;
}
#ifdef MFEM_USE_MPI
else
@@ -802,8 +797,7 @@ void TargetConstructor::ComputeAvgVolume() const
double area_NE[4];
area_NE[0] = volume; area_NE[1] = NE;
MPI_Allreduce(area_NE, area_NE + 2, 2, MPI_DOUBLE, MPI_SUM, comm);
avg_volume = (ncmesh == NULL) ?
area_NE[2] / area_NE[3] : area_NE[2] / ncmesh->GetNumRootElements();
avg_volume = area_NE[2] / area_NE[3];
}
#endif
}
@@ -811,7 +805,6 @@ void TargetConstructor::ComputeAvgVolume() const
// virtual method
void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_ASSERT(target_type == IDEAL_SHAPE_UNIT_SIZE || nodes != NULL, "");
@@ -834,15 +827,7 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
{
if (avg_volume == 0.0) { ComputeAvgVolume(); }
DenseMatrix W(Wideal.Height());
NCMesh *ncmesh = nodes->FESpace()->GetMesh()->ncmesh;
double el_volume = avg_volume;
if (ncmesh)
{
el_volume = avg_volume / ncmesh->GetElementSizeReduction(e_id);
}
W.Set(std::pow(volume_scale * el_volume / Wideal.Det(),
W.Set(std::pow(volume_scale * avg_volume / Wideal.Det(),
1./W.Height()), Wideal);
for (int i = 0; i < ir.GetNPoints(); i++) { Jtr(i) = W; }
break;
@@ -868,7 +853,7 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
if (target_type == IDEAL_SHAPE_GIVEN_SIZE)
{
const double det = Jtr(i).Det();
MFEM_VERIFY(det > 0.0, "The given mesh is inverted!");
MFEM_VERIFY(det > 0.0, "Initial mesh is inverted!");
Jtr(i).Set(std::pow(det / detW, 1./dim), Wideal);
}
}
@@ -879,162 +864,6 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
}
}
void AnalyticAdaptTC::SetAnalyticTargetSpec(Coefficient *sspec,
VectorCoefficient *vspec,
MatrixCoefficient *mspec)
{
scalar_tspec = sspec;
vector_tspec = vspec;
matrix_tspec = mspec;
}
void AnalyticAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
DenseMatrix point_mat;
point_mat.UseExternalData(elfun.GetData(), fe.GetDof(), fe.GetDim());
switch (target_type)
{
case GIVEN_FULL:
{
MFEM_VERIFY(matrix_tspec != NULL,
"Target type GIVEN_FULL requires a MatrixCoefficient.");
IsoparametricTransformation Tpr;
Tpr.SetFE(&fe);
Tpr.ElementNo = e_id;
Tpr.GetPointMat().Transpose(point_mat);
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
Tpr.SetIntPoint(&ip);
matrix_tspec->Eval(Jtr(i), Tpr, ip);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
#ifdef MFEM_USE_MPI
void DiscreteAdaptTC::SetParDiscreteTargetSpec(ParGridFunction &tspec)
{
target_spec.SetSize(tspec.Size());
target_spec = tspec;
tspec_fes = tspec.FESpace();
// Default evaluator is based on CG advection.
if (adapt_eval == NULL) { adapt_eval = new AdvectorCG; }
adapt_eval->SetParMetaInfo(*tspec.ParFESpace()->GetParMesh(),
*tspec.FESpace()->FEColl(),
tspec.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec.FESpace()->GetMesh()->GetNodes(), target_spec);
}
#endif
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(GridFunction &tspec)
{
target_spec.SetSize(tspec.Size());
target_spec = tspec;
tspec_fes = tspec.FESpace();
// Default evaluator is based on CG advection.
if (adapt_eval == NULL) { adapt_eval = new AdvectorCG; }
adapt_eval->SetSerialMetaInfo(*tspec.FESpace()->GetMesh(),
*tspec.FESpace()->FEColl(),
tspec.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec.FESpace()->GetMesh()->GetNodes(), target_spec);
}
void DiscreteAdaptTC::UpdateTargetSpecification(const Vector &new_x)
{
MFEM_VERIFY(target_spec.Size() > 0, "Target specification is not set!");
adapt_eval->ComputeAtNewPosition(new_x, target_spec);
}
void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_VERIFY(tspec_fes, "A call to SetDiscreteTargerSpec() is needed.");
switch (target_type)
{
case IDEAL_SHAPE_GIVEN_SIZE:
{
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fe.GetGeomType());
const int dim = Wideal.Height(),
ntspec_dofs = tspec_fes->GetFE(0)->GetDof();
Vector shape(ntspec_dofs), tspec_vals(ntspec_dofs);
Array<int> dofs;
tspec_fes->GetElementDofs(e_id, dofs);
target_spec.GetSubVector(dofs, tspec_vals);
const double min_size = tspec_vals.Min();
MFEM_ASSERT(min_size > 0.0,
"Non-positive size propagated in the target definition.");
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
const double size = std::max(shape * tspec_vals, min_size);
Jtr(i).Set(std::pow(size / Wideal.Det(), 1.0/dim), Wideal);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec,
int num_comp)
{
delete fes;
delete mesh;
mesh = new Mesh(m, true);
fes = new FiniteElementSpace(mesh, &fec, num_comp);
}
#ifdef MFEM_USE_MPI
void AdaptivityEvaluator::SetParMetaInfo(const ParMesh &m,
const FiniteElementCollection &fec,
int num_comp)
{
delete pfes;
delete pmesh;
pmesh = new ParMesh(m, true);
pfes = new ParFiniteElementSpace(pmesh, &fec, num_comp);
}
#endif
AdaptivityEvaluator::~AdaptivityEvaluator()
{
delete fes;
delete mesh;
#ifdef MFEM_USE_MPI
delete pfes;
delete pmesh;
#endif
}
void TMOP_Integrator::EnableLimiting(const GridFunction &n0,
const GridFunction &dist, Coefficient &w0,
TMOP_LimiterFunction *lfunc)
@@ -1092,7 +921,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
// Limited case.
Vector shape, p, p0, d_vals;
@@ -1161,7 +990,6 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy += weight * val;
}
delete Tpr;
return energy;
}
@@ -1188,7 +1016,7 @@ void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
elvect = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
// Limited case.
DenseMatrix pos0;
@@ -1244,8 +1072,6 @@ void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
P *= weight_m;
AddMultABt(DS, P, PMatO);
// TODO: derivatives of adaptivity-based targets.
if (coeff0)
{
el.CalcShape(ip, shape);
@@ -1281,7 +1107,7 @@ void TMOP_Integrator::AssembleElementGrad(const FiniteElement &el,
elmat = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
// Limited case.
DenseMatrix pos0, grad_grad;
@@ -1334,8 +1160,6 @@ void TMOP_Integrator::AssembleElementGrad(const FiniteElement &el,
metric->AssembleH(Jpt, DS, weight_m, elmat);
// TODO: derivatives of adaptivity-based targets.
if (coeff0)
{
el.CalcShape(ip, shape);
@@ -1410,12 +1234,11 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
for (int i = 0; i < fes->GetNE(); i++)
{
fe = fes->GetFE(i);
targetC->ComputeElementTargets(i, *fe, *ir, Jtr);
fes->GetElementVDofs(i, vdofs);
x.GetSubVector(vdofs, x_vals);
PMatI.UseExternalData(x_vals.GetData(), dof, dim);
targetC->ComputeElementTargets(i, *fe, *ir, x_vals, Jtr);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
@@ -1451,6 +1274,9 @@ void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
const IntegrationRule &ir = metric_gf.FESpace()->GetFE(i)->GetNodes();
const int nsp = ir.GetNPoints(), dof = fe_pos.GetDof();
W.SetSize(dim, dim, nsp);
tc.ComputeElementTargets(i, fe_pos, ir, W);
dshape.SetSize(dof, dim);
pos.SetSize(dof, dim);
posV.SetDataAndSize(pos.Data(), dof * dim);
@@ -1459,9 +1285,6 @@ void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
nodes.FESpace()->GetElementVDofs(i, pos_dofs);
nodes.GetSubVector(pos_dofs, posV);
W.SetSize(dim, dim, nsp);
tc.ComputeElementTargets(i, fe_pos, ir, posV, W);
for (int j = 0; j < nsp; j++)
{
const DenseMatrix &Wj = W(j);
+3 -124
View File
@@ -12,6 +12,7 @@
#ifndef MFEM_TMOP_HPP
#define MFEM_TMOP_HPP
#include "../config/config.hpp"
#include "../linalg/invariants.hpp"
#include "nonlininteg.hpp"
@@ -513,51 +514,6 @@ public:
virtual ~TMOP_QuadraticLimiter() { }
};
class FiniteElementCollection;
class FiniteElementSpace;
class ParFiniteElementSpace;
class AdaptivityEvaluator
{
protected:
// Owned.
Mesh *mesh;
FiniteElementSpace *fes;
#ifdef MFEM_USE_MPI
// Owned.
ParMesh *pmesh;
ParFiniteElementSpace *pfes;
#endif
public:
AdaptivityEvaluator() : mesh(NULL), fes(NULL)
{
#ifdef MFEM_USE_MPI
pmesh = NULL;
pfes = NULL;
#endif
}
virtual ~AdaptivityEvaluator();
/** Specifies the Mesh and FiniteElementCollection of the solution that will
be evaluated. The given mesh will be copied into the internal object. */
void SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec, int num_comp);
#ifdef MFEM_USE_MPI
/// Parallel version of SetSerialMetaInfo.
void SetParMetaInfo(const ParMesh &m,
const FiniteElementCollection &fec, int num_comp);
#endif
// TODO use GridFunctions to make clear it's on the ldofs?
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field) = 0;
virtual void ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field) = 0;
};
/** @brief Base class representing target-matrix construction algorithms for
mesh optimization via the target-matrix optimization paradigm (TMOP). */
@@ -582,11 +538,9 @@ public:
IDEAL_SHAPE_GIVEN_SIZE, /**<
Ideal shape, given size/volume; the given nodes define the target
volume at all quadrature points. */
GIVEN_SHAPE_AND_SIZE, /**<
GIVEN_SHAPE_AND_SIZE /**<
Given shape, given size/volume; the given nodes define the exact target
Jacobian matrix at all quadrature points. */
GIVEN_FULL /**<
Full target tensor is specified at every quadrature point. */
};
protected:
@@ -635,89 +589,14 @@ public:
void SetVolumeScale(double vol_scale) { volume_scale = vol_scale; }
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun. */
transformation Jacobians for each quadrature point in the element. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
class AnalyticAdaptTC : public TargetConstructor
{
protected:
// Analytic target specification.
Coefficient *scalar_tspec;
VectorCoefficient *vector_tspec;
MatrixCoefficient *matrix_tspec;
public:
AnalyticAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
scalar_tspec(NULL), vector_tspec(NULL), matrix_tspec(NULL) { }
virtual void SetAnalyticTargetSpec(Coefficient *sspec,
VectorCoefficient *vspec,
MatrixCoefficient *mspec);
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
class ParGridFunction;
class DiscreteAdaptTC : public TargetConstructor
{
protected:
// Discrete target specification.
// Data is owned, updated by UpdateTargetSpecification.
Vector target_spec;
// Note: do not use the Nodes of this space as they may not be on the
// positions corresponding to the values of tspec.
const FiniteElementSpace *tspec_fes;
// Evaluation of the discrete target specification on different meshes.
// Owned.
AdaptivityEvaluator *adapt_eval;
public:
DiscreteAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
target_spec(), tspec_fes(NULL), adapt_eval(NULL) { }
virtual ~DiscreteAdaptTC() { delete adapt_eval; }
virtual void SetSerialDiscreteTargetSpec(GridFunction &tspec);
#ifdef MFEM_USE_MPI
virtual void SetParDiscreteTargetSpec(ParGridFunction &tspec);
#endif
/** Used to update the target specification after the mesh has changed. The
new mesh positions are given by new_x. */
void UpdateTargetSpecification(const Vector &new_x);
void SetAdaptivityEvaluator(AdaptivityEvaluator *ae)
{
if (adapt_eval) { delete adapt_eval; }
adapt_eval = ae;
}
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun.
Note that this function assumes that UpdateTargetSpecification() has
been called with the position vector corresponding to @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
/** @brief A TMOP integrator class based on any given TMOP_QualityMetric and
TargetConstructor.
-518
View File
@@ -1,518 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "tmop_tools.hpp"
#include "nonlinearform.hpp"
#include "pnonlinearform.hpp"
#include "../general/osockstream.hpp"
namespace mfem
{
using namespace mfem;
void AdvectorCG::SetInitialField(const Vector &init_nodes,
const Vector &init_field)
{
nodes0 = init_nodes;
field0 = init_field;
}
void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field)
{
int myid = 0;
Mesh *m = mesh;
#ifdef MFEM_USE_MPI
if (pfes) { MPI_Comm_rank(pfes->GetComm(), &myid); }
if (pmesh) { m = pmesh; }
#endif
MFEM_VERIFY(m != NULL, "No mesh has been given to the AdaptivityEvaluator.");
// This will be used to move the positions.
GridFunction *mesh_nodes = m->GetNodes();
*mesh_nodes = nodes0;
new_field = field0;
// Velocity of the positions.
GridFunction u(mesh_nodes->FESpace());
subtract(new_nodes, nodes0, u);
TimeDependentOperator *oper = NULL;
// This must be the fes of the ind, associated with the object's mesh.
if (fes) { oper = new SerialAdvectorCGOper(nodes0, u, *fes); }
#ifdef MFEM_USE_MPI
else if (pfes) { oper = new ParAdvectorCGOper(nodes0, u, *pfes); }
#endif
MFEM_VERIFY(oper != NULL,
"No FE space has been given to the AdaptivityEvaluator.");
ode_solver.Init(*oper);
// Compute some time step [mesh_size / speed].
double min_h = std::numeric_limits<double>::infinity();
for (int i = 0; i < m->GetNE(); i++)
{
min_h = std::min(min_h, m->GetElementSize(i));
}
double v_max = 0.0;
const int s = u.FESpace()->GetVSize() / 2;
for (int i = 0; i < s; i++)
{
const double vel = u(i) * u(i) + u(i+s) * u(i+s);
v_max = std::max(v_max, vel);
}
if (v_max == 0.0)
{
// No need to change the field.
return;
}
v_max = std::sqrt(v_max);
double dt = 0.5 * min_h / v_max;
double glob_dt = dt;
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(&dt, &glob_dt, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
}
#endif
double t = 0.0;
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
if (t + glob_dt >= 1.0)
{
#ifdef MFEM_DEBUG
if (myid == 0)
{
mfem::out << "Remap took " << ti << " steps." << std::endl;
}
#endif
glob_dt = 1.0 - t;
last_step = true;
}
ode_solver.Step(new_field, t, glob_dt);
}
// Trim the overshoots and undershoots.
const double minv = field0.Min(), maxv = field0.Max();
for (int i = 0; i < new_field.Size(); i++)
{
if (new_field(i) < minv) { new_field(i) = minv; }
if (new_field(i) > maxv) { new_field(i) = maxv; }
}
nodes0 = new_nodes;
field0 = new_field;
delete oper;
}
SerialAdvectorCGOper::SerialAdvectorCGOper(const Vector &x_start,
GridFunction &vel,
FiniteElementSpace &fes)
: TimeDependentOperator(fes.GetVSize()),
x0(x_start), x_now(*fes.GetMesh()->GetNodes()),
u(vel), u_coeff(&u), M(&fes), K(&fes)
{
ConvectionIntegrator *Kinteg = new ConvectionIntegrator(u_coeff);
K.AddDomainIntegrator(Kinteg);
K.Assemble(0);
K.Finalize(0);
MassIntegrator *Minteg = new MassIntegrator;
M.AddDomainIntegrator(Minteg);
M.Assemble();
M.Finalize();
}
void SerialAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
{
// Move the mesh.
const double t = GetTime();
add(x0, t, u, x_now);
// Assemble on the new mesh.
K.BilinearForm::operator=(0.0);
K.Assemble();
Vector rhs(K.Size());
K.Mult(ind, rhs);
M.BilinearForm::operator=(0.0);
M.Assemble();
di_dt = 0.0;
CGSolver lin_solver;
DSmoother prec;
lin_solver.SetPreconditioner(prec);
lin_solver.SetOperator(M.SpMat());
lin_solver.SetRelTol(1e-12); lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(rhs, di_dt);
}
#ifdef MFEM_USE_MPI
ParAdvectorCGOper::ParAdvectorCGOper(const Vector &x_start,
GridFunction &vel,
ParFiniteElementSpace &pfes)
: TimeDependentOperator(pfes.GetVSize()),
x0(x_start), x_now(*pfes.GetMesh()->GetNodes()),
u(vel), u_coeff(&u), M(&pfes), K(&pfes)
{
ConvectionIntegrator *Kinteg = new ConvectionIntegrator(u_coeff);
K.AddDomainIntegrator(Kinteg);
K.Assemble(0);
K.Finalize(0);
MassIntegrator *Minteg = new MassIntegrator;
M.AddDomainIntegrator(Minteg);
M.Assemble();
M.Finalize();
}
void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
{
// Move the mesh.
const double t = GetTime();
add(x0, t, u, x_now);
// Assemble on the new mesh.
K.BilinearForm::operator=(0.0);
K.Assemble();
ParGridFunction rhs(K.ParFESpace());
K.Mult(ind, rhs);
M.BilinearForm::operator=(0.0);
M.Assemble();
HypreParVector *RHS = rhs.ParallelAssemble();
HypreParVector X(K.ParFESpace());
X = 0.0;
HypreParMatrix *Mh = M.ParallelAssemble();
CGSolver lin_solver(M.ParFESpace()->GetParMesh()->GetComm());
HypreSmoother prec;
prec.SetType(HypreSmoother::Jacobi, 1);
lin_solver.SetPreconditioner(prec);
lin_solver.SetOperator(*Mh);
lin_solver.SetRelTol(1e-8);
lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(*RHS, X);
K.ParFESpace()->GetProlongationMatrix()->Mult(X, di_dt);
delete Mh;
delete RHS;
}
#endif
double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const FiniteElementSpace *fes = NULL;
double energy_in = 0.0;
#ifdef MFEM_USE_MPI
const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
if (parallel)
{
fes = p_nlf->FESpace();
energy_in = p_nlf->GetEnergy(x);
}
#endif
const bool serial = !parallel;
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
if (serial)
{
fes = nlf->FESpace();
energy_in = nlf->GetEnergy(x);
}
const bool have_b = (b.Size() == Height());
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_out(x.Size()), x_out_loc(fes->GetVSize());
bool x_out_ok = false;
double scale = 1.0, energy_out;
double norm0 = Norm(r);
// Decreases the scaling of the update until the new mesh is valid.
for (int i = 0; i < 12; i++)
{
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP) {x_out_loc.SetData(x_out.GetData());}
else {cP->Mult(x_out,x_out_loc);}
energy_out = nlf->GetGridFunctionEnergy(x_out_loc);
}
#ifdef MFEM_USE_MPI
else
{
fes->GetProlongationMatrix()->Mult(x_out, x_out_loc);
energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
}
#endif
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Increasing energy.\n"; }
scale *= 0.5; continue;
}
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
int jac_ok_all = jac_ok;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (jac_ok_all == 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= 0.5; continue;
}
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { scale = 0.0; }
return scale;
}
void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
{
if (discr_tc)
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
Vector x_loc(nlf->ParFESpace()->GetVSize());
nlf->ParFESpace()->GetProlongationMatrix()->Mult(x, x_loc);
discr_tc->UpdateTargetSpecification(x_loc);
#endif
}
else { discr_tc->UpdateTargetSpecification(x); }
}
}
double TMOPDescentNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const FiniteElementSpace *fes = NULL;
double energy_in = 0.0;
#ifdef MFEM_USE_MPI
const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
if (parallel)
{
fes = p_nlf->FESpace();
energy_in = p_nlf->GetEnergy(x);
}
#endif
const bool serial = !parallel;
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
if (serial)
{
fes = nlf->FESpace();
energy_in = nlf->GetEnergy(x);
}
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_loc(fes->GetVSize());
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_loc.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
}
double min_detJ_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (print_level >= 0)
{
mfem::out << "Minimum det(J) = " << min_detJ_all << '\n';
}
Vector x_out(x.Size());
bool x_out_ok = false;
double scale = 1.0, energy_out;
for (int i = 0; i < 7; i++)
{
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP) {x_loc.SetData(x_out.GetData());}
else {cP->Mult(x_out,x_loc);}
energy_out = nlf->GetGridFunctionEnergy(x_loc);
}
#ifdef MFEM_USE_MPI
else
{
fes->GetProlongationMatrix()->Mult(x_out, x_loc);
energy_out = p_nlf->GetParGridFunctionEnergy(x_loc);
}
#endif
if (energy_out > energy_in || std::isnan(energy_out) != 0)
{
scale *= 0.5;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { return 0.0; }
return scale;
}
void TMOPDescentNewtonSolver::ProcessNewState(const Vector &x) const
{
if (discr_tc)
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
Vector x_loc(nlf->ParFESpace()->GetVSize());
nlf->ParFESpace()->GetProlongationMatrix()->Mult(x, x_loc);
discr_tc->UpdateTargetSpecification(x_loc);
#endif
}
else { discr_tc->UpdateTargetSpecification(x); }
}
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_tmop_metric_p(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, ParMesh &pmesh,
char *title, int position)
{
L2_FECollection fec(order, pmesh.Dimension(), BasisType::GaussLobatto);
ParFiniteElementSpace fes(&pmesh, &fec, 1);
ParGridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, pmesh, metric);
socketstream sock;
if (pmesh.GetMyRank() == 0)
{
sock.open("localhost", 19916);
sock << "solution\n";
}
pmesh.PrintAsOne(sock);
metric.SaveAsOne(sock);
if (pmesh.GetMyRank() == 0)
{
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA\n";
}
}
#endif
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, Mesh &mesh,
char *title, int position)
{
L2_FECollection fec(order, mesh.Dimension(), BasisType::GaussLobatto);
FiniteElementSpace fes(&mesh, &fec, 1);
GridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, mesh, metric);
osockstream sock(19916, "localhost");
sock << "solution\n";
mesh.Print(sock);
metric.Save(sock);
sock.send();
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA\n";
}
}
-140
View File
@@ -1,140 +0,0 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_TMOP_TOOLS_HPP
#define MFEM_TMOP_TOOLS_HPP
#include "bilinearform.hpp"
#include "pbilinearform.hpp"
#include "tmop.hpp"
namespace mfem
{
// Performs the full remap advection loop.
class AdvectorCG : public AdaptivityEvaluator
{
private:
RK4Solver ode_solver;
Vector nodes0;
Vector field0;
public:
AdvectorCG() : AdaptivityEvaluator(), ode_solver(), nodes0(), field0() { }
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field);
virtual void ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field);
};
/// Performs a single remap advection step in serial.
class SerialAdvectorCGOper : public TimeDependentOperator
{
protected:
const Vector &x0;
Vector &x_now;
GridFunction &u;
VectorGridFunctionCoefficient u_coeff;
mutable BilinearForm M, K;
public:
/** Here @a fes is the FESpace of the function that will be moved. Note
that Mult() moves the nodes of the mesh corresponding to @a fes. */
SerialAdvectorCGOper(const Vector &x_start, GridFunction &vel,
FiniteElementSpace &fes);
virtual void Mult(const Vector &ind, Vector &di_dt) const;
};
#ifdef MFEM_USE_MPI
/// Performs a single remap advection step in parallel.
class ParAdvectorCGOper : public TimeDependentOperator
{
protected:
const Vector &x0;
Vector &x_now;
GridFunction &u;
VectorGridFunctionCoefficient u_coeff;
mutable ParBilinearForm M, K;
public:
/** Here @a pfes is the ParFESpace of the function that will be moved. Note
that Mult() moves the nodes of the mesh corresponding to @a pfes. */
ParAdvectorCGOper(const Vector &x_start, GridFunction &vel,
ParFiniteElementSpace &pfes);
virtual void Mult(const Vector &ind, Vector &di_dt) const;
};
#endif
class TMOPNewtonSolver : public NewtonSolver
{
private:
bool parallel;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
mutable DiscreteAdaptTC *discr_tc;
public:
#ifdef MFEM_USE_MPI
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule)
: NewtonSolver(comm), parallel(true), ir(irule), discr_tc(NULL) { }
#endif
TMOPNewtonSolver(const IntegrationRule &irule)
: NewtonSolver(), parallel(false), ir(irule), discr_tc(NULL) { }
void SetDiscreteAdaptTC(DiscreteAdaptTC *tc) { discr_tc = tc; }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
};
/// Allows negative Jacobians. Used for untangling.
class TMOPDescentNewtonSolver : public NewtonSolver
{
private:
bool parallel;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
mutable DiscreteAdaptTC *discr_tc;
public:
#ifdef MFEM_USE_MPI
TMOPDescentNewtonSolver(MPI_Comm comm, const IntegrationRule &irule)
: NewtonSolver(comm), parallel(true), ir(irule), discr_tc(NULL) { }
#endif
TMOPDescentNewtonSolver(const IntegrationRule &irule)
: NewtonSolver(), parallel(false), ir(irule), discr_tc(NULL) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
};
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, Mesh &pmesh,
char *title, int position);
#ifdef MFEM_USE_MPI
void vis_tmop_metric_p(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, ParMesh &pmesh,
char *title, int position);
#endif
}
#endif
+2 -7
View File
@@ -433,7 +433,7 @@ class BlockArray
public:
BlockArray(int block_size = 16*1024);
BlockArray(const BlockArray<T> &other); // deep copy
~BlockArray() { Destroy(); }
~BlockArray();
/// Allocate and construct a new item in the array, return its index.
int Append();
@@ -463,9 +463,6 @@ public:
/// Return the current capacity of the BlockArray.
int Capacity() const { return blocks.Size()*(mask+1); }
/// Destroy all items, set size to zero.
void DeleteAll() { Destroy(); blocks.DeleteAll(); size = 0; }
void Swap(BlockArray<T> &other);
long MemoryUsage() const;
@@ -570,8 +567,6 @@ protected:
MFEM_ASSERT(index >= 0 && index < size,
"Out of bounds access: " << index << ", size = " << size);
}
void Destroy();
};
@@ -1003,7 +998,7 @@ long BlockArray<T>::MemoryUsage() const
}
template<typename T>
void BlockArray<T>::Destroy()
BlockArray<T>::~BlockArray()
{
int bsize = size & mask;
for (int i = blocks.Size(); i != 0; )
+5 -33
View File
@@ -322,9 +322,7 @@ struct VarMessage
std::string data;
MPI_Request send_request;
/** Non-blocking send to processor 'rank'. Returns immediately. Completion
(as tested by MPI_Wait/Test) does not mean the message was received --
it may be on its way or just buffered locally. */
/// Non-blocking send to processor 'rank'.
void Isend(int rank, MPI_Comm comm)
{
Encode(rank);
@@ -332,20 +330,12 @@ struct VarMessage
&send_request);
}
/** Non-blocking synchronous send to processor 'rank'. Returns immediately.
Completion (MPI_Wait/Test) means that the message was received. */
void Issend(int rank, MPI_Comm comm)
{
Encode(rank);
MPI_Issend((void*) data.data(), data.length(), MPI_BYTE, rank, Tag, comm,
&send_request);
}
/// Helper to send all messages in a rank-to-message map container.
template<typename MapT>
static void IsendAll(MapT& rank_msg, MPI_Comm comm)
{
for (auto it = rank_msg.begin(); it != rank_msg.end(); ++it)
typename MapT::iterator it;
for (it = rank_msg.begin(); it != rank_msg.end(); ++it)
{
it->second.Isend(it->first, comm);
}
@@ -355,32 +345,14 @@ struct VarMessage
template<typename MapT>
static void WaitAllSent(MapT& rank_msg)
{
for (auto it = rank_msg.begin(); it != rank_msg.end(); ++it)
typename MapT::iterator it;
for (it = rank_msg.begin(); it != rank_msg.end(); ++it)
{
MPI_Wait(&it->second.send_request, MPI_STATUS_IGNORE);
it->second.Clear();
}
}
/** Return true if all messages in the map container were sent, otherwise
return false, without waiting. */
template<typename MapT>
static bool TestAllSent(MapT& rank_msg)
{
for (auto it = rank_msg.begin(); it != rank_msg.end(); ++it)
{
VarMessage &msg = it->second;
if (msg.send_request != MPI_REQUEST_NULL)
{
int sent;
MPI_Test(&msg.send_request, &sent, MPI_STATUS_IGNORE);
if (!sent) { return false; }
msg.Clear();
}
}
return true;
}
/** Blocking probe for incoming message of this type from any rank.
Returns the rank and message size. */
static void Probe(int &rank, int &size, MPI_Comm comm)
+74 -9
View File
@@ -12,6 +12,9 @@
#include "forall.hpp"
#include "cuda.hpp"
#include "occa.hpp"
#ifdef MFEM_USE_CEED
#include <ceed.h>
#endif
#include <string>
#include <map>
@@ -29,20 +32,24 @@ namespace internal
occa::device occaDevice;
#endif
#ifdef MFEM_USE_CEED
Ceed ceed;
#endif
// Backends listed by priority, high to low:
static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
{
Backend::OCCA_CUDA, Backend::RAJA_CUDA, Backend::CUDA,
Backend::OCCA_CUDA, Backend::RAJA_CUDA, Backend::CEED_CUDA, Backend::CUDA,
Backend::HIP,
Backend::OCCA_OMP, Backend::RAJA_OMP, Backend::OMP,
Backend::OCCA_CPU, Backend::RAJA_CPU, Backend::CPU
Backend::OCCA_CPU, Backend::RAJA_CPU, Backend::CEED_CPU, Backend::CPU
};
// Backend names listed by priority, high to low:
static const char *backend_name[Backend::NUM_BACKENDS] =
{
"occa-cuda", "raja-cuda", "cuda", "hip", "occa-omp", "raja-omp", "omp",
"occa-cpu", "raja-cpu", "cpu"
"occa-cuda", "raja-cuda", "ceed-cuda", "cuda", "hip", "occa-omp", "raja-omp", "omp",
"occa-cpu", "raja-cpu", "ceed-cpu", "cpu"
};
} // namespace mfem::internal
@@ -54,7 +61,11 @@ Device Device::device_singleton;
Device::~Device()
{
if (destroy_mm) { mm.Destroy(); }
if (destroy_mm)
{
free(ceed_option);
mm.Destroy();
}
}
void Device::Configure(const std::string &device, const int dev)
@@ -64,15 +75,29 @@ void Device::Configure(const std::string &device, const int dev)
{
bmap[internal::backend_name[i]] = internal::backend_list[i];
}
std::string::size_type beg = 0, end;
std::string::size_type beg = 0, end, option;
while (1)
{
end = device.find(',', beg);
end = (end != std::string::npos) ? end : device.size();
const std::string bname = device.substr(beg, end - beg);
std::map<std::string, Backend::Id>::iterator it = bmap.find(bname);
MFEM_VERIFY(it != bmap.end(), "invalid backend name: '" << bname << '\'');
Get().MarkBackend(it->second);
option = bname.find(':');
if (option==std::string::npos)//No option
{
const std::string backend = bname;
std::map<std::string, Backend::Id>::iterator it = bmap.find(backend);
MFEM_VERIFY(it != bmap.end(), "invalid backend name: '" << backend << '\'');
Get().MarkBackend(it->second);
}
else
{
const std::string backend = bname.substr(0, option);
const std::string boption = bname.substr(option+1);
Get().ceed_option = strdup(boption.c_str());
std::map<std::string, Backend::Id>::iterator it = bmap.find(backend);
MFEM_VERIFY(it != bmap.end(), "invalid backend name: '" << backend << '\'');
Get().MarkBackend(it->second);
}
if (end == device.size()) { break; }
beg = end + 1;
}
@@ -82,6 +107,10 @@ void Device::Configure(const std::string &device, const int dev)
{
Get().MarkBackend(Backend::CUDA);
}
if (Allows(Backend::CEED_CUDA))
{
Get().MarkBackend(Backend::CUDA);
}
// Perform setup.
Get().Setup(dev);
@@ -223,6 +252,20 @@ static void OccaDeviceSetup(const int dev)
#endif
}
static void CeedDeviceSetup(const char* ceed_spec)
{
#ifdef MFEM_USE_CEED
CeedInit(ceed_spec, &internal::ceed);
const char *ceed_backend;
CeedGetResource(internal::ceed, &ceed_backend);
mfem::out << "libCEED backend: " << ceed_backend << std::endl;
if (strcmp(ceed_spec, ceed_backend) && strcmp(ceed_spec, "/cpu/self"))
{
std::cout << std::endl << "WARNING!!!\nlibCEED is not using the requested backend!!!\nWARNING!!!\n" << std::endl;
}
#endif
}
void Device::Setup(const int device)
{
MFEM_VERIFY(ngpu == -1, "the mfem::Device is already configured!");
@@ -251,6 +294,28 @@ void Device::Setup(const int device)
if (Allows(Backend::RAJA_CUDA)) { RajaDeviceSetup(dev, ngpu); }
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
if (Allows(Backend::CEED_CPU))
{
if (!ceed_option)
{
CeedDeviceSetup("/cpu/self");
}
else
{
CeedDeviceSetup(ceed_option);
}
}
if (Allows(Backend::CEED_CUDA))
{
if (!ceed_option)
{
CeedDeviceSetup("/gpu/cuda/gen");
}
else
{
CeedDeviceSetup(ceed_option);
}
}
}
} // mfem
+23 -7
View File
@@ -54,7 +54,14 @@ struct Backend
OCCA_OMP = 1 << 8,
/** @brief [device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES
and MFEM_USE_CUDA = YES. */
OCCA_CUDA = 1 << 9
OCCA_CUDA = 1 << 9,
/** @brief [host] CEED backend: GPU backends can still be used, but
with expensive memory transfers.
Enabled when MFEM_USE_CEED = YES. */
CEED_CPU = 1 << 10,
/** @brief [device] Ceed backends working in colaboration with the Cuda backend.
Enabled when MFEM_USE_CEED = YES and MFEM_USE_CUDA = YES. */
CEED_CUDA = 1 << 11
};
/** @brief Additional useful constants. For example, the *_MASK constants can
@@ -62,7 +69,7 @@ struct Backend
enum
{
/// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
NUM_BACKENDS = 10,
NUM_BACKENDS = 12,
/// Biwise-OR of all CPU backends
CPU_MASK = CPU | RAJA_CPU | OCCA_CPU,
@@ -72,6 +79,8 @@ struct Backend
HIP_MASK = HIP,
/// Biwise-OR of all OpenMP backends
OMP_MASK = OMP | RAJA_OMP | OCCA_OMP,
/// Bitwise-OR of all CEED backends
CEED_MASK = CEED_CPU | CEED_CUDA,
/// Biwise-OR of all device backends
DEVICE_MASK = CUDA_MASK | HIP_MASK,
@@ -116,6 +125,7 @@ private:
MemoryType mem_type; ///< Current Device MemoryType
MemoryClass mem_class; ///< Current Device MemoryClass
char *ceed_option = NULL;
Device(Device const&);
void operator=(Device const&);
static Device& Get() { return device_singleton; }
@@ -187,7 +197,13 @@ 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. */
is already enabled.
* The backend 'ceed-cpu' delegates to a libCEED CPU backend the setup and
evaluation of the operator.
* The backend 'ceed-cuda' delegates to a libCEED CUDA backend the setup
and evaluation of the operator and enables the 'cuda' backend to avoid
transfer between host and device.
*/
void Configure(const std::string &device, const int dev = 0);
/// Print the configuration of the MFEM virtual device object.
@@ -220,7 +236,7 @@ public:
/** @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; }
static void SetGPUAwareMPI(const bool force = true)
{ Get().mpi_gpu_aware = force; }
@@ -276,7 +292,7 @@ inline T *Write(Memory<T> &mem, int size, bool on_dev = true)
/** @brief Shortcut to Write(const Memory<T> &mem, int size, false) */
template <typename T>
inline T *HostWrite(Memory<T> &mem, int size)
inline const T *HostWrite(const Memory<T> &mem, int size)
{
return mfem::Write(mem, size, false);
}
@@ -298,9 +314,9 @@ inline T *ReadWrite(Memory<T> &mem, int size, bool on_dev = true)
}
}
/** @brief Shortcut to ReadWrite(Memory<T> &mem, int size, false) */
/** @brief Shortcut to ReadWrite(const Memory<T> &mem, int size, false) */
template <typename T>
inline T *HostReadWrite(Memory<T> &mem, int size)
inline const T *HostReadWrite(const Memory<T> &mem, int size)
{
return mfem::ReadWrite(mem, size, false);
}
-6
View File
@@ -138,10 +138,4 @@ void mfem_warning(const char *msg = NULL);
// Generate a warning message - always generated, regardless of MFEM_DEBUG.
#define MFEM_WARNING(msg) _MFEM_MESSAGE("MFEM Warning: " << msg, 1)
// Macro that checks (in MFEM_DEBUG mode) that i is in the range [imin,imax).
#define MFEM_ASSERT_INDEX_IN_RANGE(i,imin,imax) \
MFEM_ASSERT((imin) <= (i) && (i) < (imax), \
"invalid index " #i << " = " << (i) << \
", valid range is [" << (imin) << ',' << (imax) << ')')
#endif
+40 -10
View File
@@ -91,8 +91,7 @@ using RAJA::statement::Segs;
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
void RajaCudaWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
RAJA::forall<RAJA::cuda_exec<BLOCKS>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
@@ -103,8 +102,8 @@ void RajaCudaWrap2D(const int N, DBODY &&d_body,
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::CudaKernelAsync<
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
RAJA::statement::CudaKernel<
RAJA::statement::For<0, RAJA::cuda_block_x_loop,
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
@@ -116,6 +115,7 @@ void RajaCudaWrap2D(const int N, DBODY &&d_body,
const int k = n*BZ + threadIdx.z;
if (k >= N) { return; }
d_body(k);
MFEM_SYNC_THREAD;
});
MFEM_GPU_CHECK(cudaGetLastError());
}
@@ -126,15 +126,15 @@ void RajaCudaWrap3D(const int N, DBODY &&d_body,
{
MFEM_VERIFY(N>0, "");
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::CudaKernelAsync<
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
RAJA::statement::CudaKernel<
RAJA::statement::For<0, RAJA::cuda_block_x_loop,
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,N), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,Z)),
[=] RAJA_DEVICE (const int k) { d_body(k); });
[=] RAJA_DEVICE (const int k) { d_body(k); MFEM_SYNC_THREAD; });
MFEM_GPU_CHECK(cudaGetLastError());
}
@@ -147,11 +147,35 @@ void RajaCudaWrap3D(const int N, DBODY &&d_body,
using RAJA::statement::Segs;
template <typename HBODY>
void RajaOmpWrap(const int N, HBODY &&h_body)
void RajaOmpWrap1D(const int N, HBODY &&h_body)
{
RAJA::forall<RAJA::omp_parallel_for_exec>(RAJA::RangeSegment(0,N), h_body);
}
template <typename HBODY>
void RajaOmpWrap2D(const int N, HBODY &&h_body,
const int X, const int Y, const int BZ)
{
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::For<0, RAJA::omp_parallel_for_exec,
RAJA::statement::Lambda<0, Segs<0>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,N), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,BZ)),
[=] (int k) { h_body(k); });
}
template <typename HBODY>
void RajaOmpWrap3D(const int N, HBODY &&h_body,
const int X, const int Y, const int Z)
{
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::For<0, RAJA::omp_parallel_for_exec,
RAJA::statement::Lambda<0, Segs<0>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,N), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,Z)),
[=] (int k) { h_body(k); });
}
#endif
@@ -336,8 +360,14 @@ inline void ForallWrap(const bool use_dev, const int N,
#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); }
if (DIM == 1 && Device::Allows(Backend::OMP_MASK & ~Backend::OMP))
{ return RajaOmpWrap1D(N, h_body); }
if (DIM == 2 && Device::Allows(Backend::OMP_MASK & ~Backend::OMP))
{ return RajaOmpWrap2D(N, h_body, X, Y, Z); }
if (DIM == 3 && Device::Allows(Backend::OMP_MASK & ~Backend::OMP))
{ return RajaOmpWrap3D(N, h_body, X, Y, Z); }
#endif
#ifdef MFEM_USE_OPENMP
-5
View File
@@ -107,9 +107,4 @@ void SetGlobalMPI_Comm(MPI_Comm comm);
} // namespace mfem
// Request a global object to be instantiated for each thread in its TLS.
#define MFEM_THREAD_LOCAL thread_local
#endif
+9 -32
View File
@@ -79,22 +79,22 @@ public:
/// Get item whose parents are p1, p2... Create it if it doesn't exist.
T* Get(int p1, int p2);
T* Get(int p1, int p2, int p3, int p4 = -1 /* p4 optional */);
T* Get(int p1, int p2, int p3, int p4);
/// Get id of item whose parents are p1, p2... Create it if it doesn't exist.
int GetId(int p1, int p2);
int GetId(int p1, int p2, int p3, int p4 = -1);
int GetId(int p1, int p2, int p3, int p4);
/// Find item whose parents are p1, p2... Return NULL if it doesn't exist.
T* Find(int p1, int p2);
T* Find(int p1, int p2, int p3, int p4 = -1);
T* Find(int p1, int p2, int p3, int p4);
const T* Find(int p1, int p2) const;
const T* Find(int p1, int p2, int p3, int p4 = -1) const;
const T* Find(int p1, int p2, int p3, int p4) const;
/// Find id of item whose parents are p1, p2... Return -1 if it doesn't exist.
int FindId(int p1, int p2) const;
int FindId(int p1, int p2, int p3, int p4 = -1) const;
int FindId(int p1, int p2, int p3, int p4) const;
/// Return the number of elements currently stored in the HashTable.
int Size() const { return Base::Size() - unused.Size(); }
@@ -113,12 +113,9 @@ public:
/** Its id will be reused by newly added items. */
void Delete(int id);
/// Remove all items.
void DeleteAll();
/// Make an item hashed under different parent IDs.
void Reparent(int id, int new_p1, int new_p2);
void Reparent(int id, int new_p1, int new_p2, int new_p3, int new_p4 = -1);
void Reparent(int id, int new_p1, int new_p2, int new_p3, int new_p4);
/// Return total size of allocated memory (tables plus items), in bytes.
long MemoryUsage() const;
@@ -249,18 +246,6 @@ inline void sort4(int &a, int &b, int &c, int &d)
sort3(b, c, d);
}
inline void sort4_ext(int &a, int &b, int &c, int &d)
{
if (d < 0) // support optional last index
{
sort3(a, b, c);
}
else
{
sort4(a, b, c, d);
}
}
} // internal
template<typename T>
@@ -310,7 +295,7 @@ template<typename T>
int HashTable<T>::GetId(int p1, int p2, int p3, int p4)
{
// search for the item in the hashtable
internal::sort4_ext(p1, p2, p3, p4);
internal::sort4(p1, p2, p3, p4);
int idx = Hash(p1, p2, p3);
int id = SearchList(table[idx], p1, p2, p3);
if (id >= 0) { return id; }
@@ -376,7 +361,7 @@ int HashTable<T>::FindId(int p1, int p2) const
template<typename T>
int HashTable<T>::FindId(int p1, int p2, int p3, int p4) const
{
internal::sort4_ext(p1, p2, p3, p4);
internal::sort4(p1, p2, p3, p4);
return SearchList(table[Hash(p1, p2, p3)], p1, p2, p3);
}
@@ -474,14 +459,6 @@ void HashTable<T>::Delete(int id)
unused.Append(id); // add its id to the unused ids
}
template<typename T>
void HashTable<T>::DeleteAll()
{
Base::DeleteAll();
for (int i = 0; i <= mask; i++) { table[i] = -1; }
unused.DeleteAll();
}
template<typename T>
void HashTable<T>::Reparent(int id, int new_p1, int new_p2)
{
@@ -504,7 +481,7 @@ void HashTable<T>::Reparent(int id,
T& item = Base::At(id);
Unlink(Hash(item), id);
internal::sort4_ext(new_p1, new_p2, new_p3, new_p4);
internal::sort4(new_p1, new_p2, new_p3, new_p4);
item.p1 = new_p1;
item.p2 = new_p2;
item.p3 = new_p3;
+1 -23
View File
@@ -11,7 +11,7 @@
#include "../general/forall.hpp"
#include <cstring> // std::memcpy, std::memcmp
#include <cstring> // std::memcpy
#include <list>
#include <unordered_map>
@@ -57,15 +57,6 @@ MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
}
// Instantiate Memory<T>::PrintFlags for T = int and T = double.
template void Memory<int>::PrintFlags() const;
template void Memory<double>::PrintFlags() const;
// Instantiate Memory<T>::CompareHostAndDevice for T = int and T = double.
template int Memory<int>::CompareHostAndDevice(int size) const;
template int Memory<double>::CompareHostAndDevice(int size) const;
namespace internal
{
@@ -704,19 +695,6 @@ void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
}
int MemoryManager::CompareHostAndDevice_(void *h_ptr, size_t size,
unsigned flags)
{
void *d_ptr = (flags & Mem::ALIAS) ?
mm.GetAliasDevicePtr(h_ptr, size, false) :
mm.GetDevicePtr(h_ptr, size, false);
char *h_buf = new char[size];
CuMemcpyDtoH(h_buf, d_ptr, size);
int res = std::memcmp(h_ptr, h_buf, size);
delete [] h_buf;
return res;
}
void MemoryPrintFlags(unsigned flags)
{
+1 -30
View File
@@ -377,16 +377,6 @@ public:
/// 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;
/// Print the internal flags.
/** This method can be useful for debugging. It is explicitly instantiated
for Memory<T> with T = int and T = double. */
inline void PrintFlags() const;
/// If both the host and the device data are valid, compare their contents.
/** This method can be useful for debugging. It is explicitly instantiated
for Memory<T> with T = int and T = double. */
inline int CompareHostAndDevice(int size) const;
};
@@ -457,11 +447,6 @@ private:
static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
std::size_t size, unsigned &dest_flags);
// Compare the contents of the host and the device memory - useful for
// debugging.
static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
/// Adds an address in the map
void *Insert(void *ptr, const std::size_t bytes);
@@ -742,24 +727,10 @@ inline void Memory<T>::CopyToHost(T *dest, int size) const
/** @brief Print the state of a Memory object based on its internal flags.
Useful in a debugger. See also Memory<T>::PrintFlags(). */
Useful in a debugger. */
extern void MemoryPrintFlags(unsigned flags);
template <typename T>
inline void Memory<T>::PrintFlags() const
{
MemoryPrintFlags(flags);
}
template <typename T>
inline int Memory<T>::CompareHostAndDevice(int size) const
{
if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
}
/// The (single) global memory manager object
extern MemoryManager mm;
+1 -2
View File
@@ -4456,8 +4456,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.HostReadWrite();
double x_col;
double *yp = y, x_col;
const double *xp = x;
// the '4' here can be tuned for given platform and compiler
if (n <= 4)
+3 -18
View File
@@ -721,26 +721,11 @@ public:
{ return const_cast<DenseTensor&>(*this)(k); }
double &operator()(int i, int j, int k)
{
MFEM_ASSERT_INDEX_IN_RANGE(i, 0, SizeI());
MFEM_ASSERT_INDEX_IN_RANGE(j, 0, SizeJ());
MFEM_ASSERT_INDEX_IN_RANGE(k, 0, SizeK());
return tdata[i+SizeI()*(j+SizeJ()*k)];
}
{ return tdata[i+SizeI()*(j+SizeJ()*k)]; }
const double &operator()(int i, int j, int k) const
{
MFEM_ASSERT_INDEX_IN_RANGE(i, 0, SizeI());
MFEM_ASSERT_INDEX_IN_RANGE(j, 0, SizeJ());
MFEM_ASSERT_INDEX_IN_RANGE(k, 0, SizeK());
return tdata[i+SizeI()*(j+SizeJ()*k)];
}
{ return tdata[i+SizeI()*(j+SizeJ()*k)]; }
double *GetData(int k)
{
MFEM_ASSERT_INDEX_IN_RANGE(k, 0, SizeK());
return tdata+k*Mk.Height()*Mk.Width();
}
double *GetData(int k) { return tdata+k*Mk.Height()*Mk.Width(); }
double *Data() { return tdata; }
+28 -271
View File
@@ -185,7 +185,7 @@ Vector * HypreParVector::GlobalVector() const
HypreParVector& HypreParVector::operator=(double d)
{
Vector::operator=(d);
hypre_ParVectorSetConstantValues(x,d);
return *this;
}
@@ -198,7 +198,10 @@ HypreParVector& HypreParVector::operator=(const HypreParVector &y)
}
#endif
Vector::operator=(y);
for (int i = 0; i < size; i++)
{
data[i] = y.data[i];
}
return *this;
}
@@ -1021,8 +1024,6 @@ HypreParMatrix * HypreParMatrix::Transpose() const
HYPRE_Int HypreParMatrix::Mult(HypreParVector &x, HypreParVector &y,
double a, double b)
{
x.HostRead();
(b == 0.0) ? y.HostWrite() : y.HostReadWrite();
return hypre_ParCSRMatrixMatvec(a, A, x, b, y);
}
@@ -1034,7 +1035,7 @@ void HypreParMatrix::Mult(double a, const Vector &x, double b, Vector &y) const
<< ", expected size = " << Height());
auto x_data = x.HostRead();
auto y_data = (b == 0.0) ? y.HostWrite() : y.HostReadWrite();
auto y_data = y.HostWrite();
if (X == NULL)
{
X = new HypreParVector(A->comm,
@@ -1066,7 +1067,7 @@ 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 = (b == 0.0) ? y.HostWrite() : y.HostReadWrite();
auto y_data = y.HostWrite();
if (X == NULL)
{
X = new HypreParVector(A->comm,
@@ -2008,12 +2009,10 @@ void HypreSmoother::Mult(const HypreParVector &b, HypreParVector &x) const
return;
}
b.HostRead();
if (!iterative_mode)
{
if (type == 0 && relax_times == 1)
{
x.HostWrite();
HYPRE_ParCSRDiagScale(NULL, *A, b, x);
if (relax_weight != 1.0)
{
@@ -2023,7 +2022,6 @@ void HypreSmoother::Mult(const HypreParVector &b, HypreParVector &x) const
}
x = 0.0;
}
x.HostReadWrite();
if (V == NULL)
{
@@ -2073,25 +2071,21 @@ void HypreSmoother::Mult(const Vector &b, Vector &x) const
mfem_error("HypreSmoother::Mult (...) : HypreParMatrix A is missing");
return;
}
auto b_data = b.HostRead();
auto x_data = iterative_mode ? x.HostReadWrite() : 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);
@@ -2208,14 +2202,7 @@ HypreSolver::~HypreSolver()
}
HyprePCG::HyprePCG(MPI_Comm comm) : precond(NULL)
{
iterative_mode = true;
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
}
HyprePCG::HyprePCG(HypreParMatrix &_A) : HypreSolver(&_A), precond(NULL)
HyprePCG::HyprePCG(HypreParMatrix &_A) : HypreSolver(&_A)
{
MPI_Comm comm;
@@ -2226,26 +2213,6 @@ HyprePCG::HyprePCG(HypreParMatrix &_A) : HypreSolver(&_A), precond(NULL)
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
}
void HyprePCG::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
if (precond)
{
precond->SetOperator(*A);
this->SetPreconditioner(*precond);
}
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
void HyprePCG::SetTol(double tol)
{
HYPRE_PCGSetTol(pcg_solver, tol);
@@ -2266,14 +2233,12 @@ void HyprePCG::SetPrintLevel(int print_lvl)
HYPRE_ParCSRPCGSetPrintLevel(pcg_solver, print_lvl);
}
void HyprePCG::SetPreconditioner(HypreSolver &_precond)
void HyprePCG::SetPreconditioner(HypreSolver &precond)
{
precond = &_precond;
HYPRE_ParCSRPCGSetPrecond(pcg_solver,
_precond.SolveFcn(),
_precond.SetupFcn(),
_precond);
precond.SolveFcn(),
precond.SetupFcn(),
precond);
}
void HyprePCG::SetResidualConvergenceOptions(int res_frequency, double rtol)
@@ -2371,57 +2336,24 @@ HyprePCG::~HyprePCG()
}
HypreGMRES::HypreGMRES(MPI_Comm comm) : precond(NULL)
{
iterative_mode = true;
HYPRE_ParCSRGMRESCreate(comm, &gmres_solver);
SetDefaultOptions();
}
HypreGMRES::HypreGMRES(HypreParMatrix &_A) : HypreSolver(&_A)
{
MPI_Comm comm;
int k_dim = 50;
int max_iter = 100;
double tol = 1e-6;
iterative_mode = true;
HYPRE_ParCSRMatrixGetComm(*A, &comm);
HYPRE_ParCSRGMRESCreate(comm, &gmres_solver);
SetDefaultOptions();
}
void HypreGMRES::SetDefaultOptions()
{
int k_dim = 50;
int max_iter = 100;
double tol = 1e-6;
HYPRE_ParCSRGMRESSetKDim(gmres_solver, k_dim);
HYPRE_ParCSRGMRESSetMaxIter(gmres_solver, max_iter);
HYPRE_ParCSRGMRESSetTol(gmres_solver, tol);
}
void HypreGMRES::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
if (precond)
{
precond->SetOperator(*A);
this->SetPreconditioner(*precond);
}
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
void HypreGMRES::SetTol(double tol)
{
HYPRE_GMRESSetTol(gmres_solver, tol);
@@ -2447,14 +2379,12 @@ void HypreGMRES::SetPrintLevel(int print_lvl)
HYPRE_GMRESSetPrintLevel(gmres_solver, print_lvl);
}
void HypreGMRES::SetPreconditioner(HypreSolver &_precond)
void HypreGMRES::SetPreconditioner(HypreSolver &precond)
{
precond = &_precond;
HYPRE_ParCSRGMRESSetPrecond(gmres_solver,
_precond.SolveFcn(),
_precond.SetupFcn(),
_precond);
precond.SolveFcn(),
precond.SetupFcn(),
precond);
}
void HypreGMRES::Mult(const HypreParVector &b, HypreParVector &x) const
@@ -2531,40 +2461,10 @@ HypreGMRES::~HypreGMRES()
}
void HypreDiagScale::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
HypreParaSails::HypreParaSails(MPI_Comm comm)
{
HYPRE_ParaSailsCreate(comm, &sai_precond);
SetDefaultOptions();
}
HypreParaSails::HypreParaSails(HypreParMatrix &A) : HypreSolver(&A)
{
MPI_Comm comm;
HYPRE_ParCSRMatrixGetComm(A, &comm);
HYPRE_ParaSailsCreate(comm, &sai_precond);
SetDefaultOptions();
}
void HypreParaSails::SetDefaultOptions()
{
int sai_max_levels = 1;
double sai_threshold = 0.1;
double sai_filter = 0.1;
@@ -2573,36 +2473,9 @@ void HypreParaSails::SetDefaultOptions()
int sai_reuse = 0;
int sai_logging = 1;
HYPRE_ParaSailsSetParams(sai_precond, sai_threshold, sai_max_levels);
HYPRE_ParaSailsSetFilter(sai_precond, sai_filter);
HYPRE_ParaSailsSetSym(sai_precond, sai_sym);
HYPRE_ParaSailsSetLoadbal(sai_precond, sai_loadbal);
HYPRE_ParaSailsSetReuse(sai_precond, sai_reuse);
HYPRE_ParaSailsSetLogging(sai_precond, sai_logging);
}
HYPRE_ParCSRMatrixGetComm(A, &comm);
void HypreParaSails::ResetSAIPrecond(MPI_Comm comm)
{
HYPRE_Int sai_max_levels;
HYPRE_Real sai_threshold;
HYPRE_Real sai_filter;
HYPRE_Int sai_sym;
HYPRE_Real sai_loadbal;
HYPRE_Int sai_reuse;
HYPRE_Int sai_logging;
// hypre_ParAMGData *amg_data = (hypre_ParAMGData *)sai_precond;
HYPRE_ParaSailsGetNlevels(sai_precond, &sai_max_levels);
HYPRE_ParaSailsGetThresh(sai_precond, &sai_threshold);
HYPRE_ParaSailsGetFilter(sai_precond, &sai_filter);
HYPRE_ParaSailsGetSym(sai_precond, &sai_sym);
HYPRE_ParaSailsGetLoadbal(sai_precond, &sai_loadbal);
HYPRE_ParaSailsGetReuse(sai_precond, &sai_reuse);
HYPRE_ParaSailsGetLogging(sai_precond, &sai_logging);
HYPRE_ParaSailsDestroy(sai_precond);
HYPRE_ParaSailsCreate(comm, &sai_precond);
HYPRE_ParaSailsSetParams(sai_precond, sai_threshold, sai_max_levels);
HYPRE_ParaSailsSetFilter(sai_precond, sai_filter);
HYPRE_ParaSailsSetSym(sai_precond, sai_sym);
@@ -2611,28 +2484,6 @@ void HypreParaSails::ResetSAIPrecond(MPI_Comm comm)
HYPRE_ParaSailsSetLogging(sai_precond, sai_logging);
}
void HypreParaSails::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
if (A)
{
MPI_Comm comm;
HYPRE_ParCSRMatrixGetComm(*A, &comm);
ResetSAIPrecond(comm);
}
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
void HypreParaSails::SetSymmetry(int sym)
{
HYPRE_ParaSailsSetSym(sai_precond, sym);
@@ -2644,30 +2495,19 @@ HypreParaSails::~HypreParaSails()
}
HypreEuclid::HypreEuclid(MPI_Comm comm)
{
HYPRE_EuclidCreate(comm, &euc_precond);
SetDefaultOptions();
}
HypreEuclid::HypreEuclid(HypreParMatrix &A) : HypreSolver(&A)
{
MPI_Comm comm;
HYPRE_ParCSRMatrixGetComm(A, &comm);
HYPRE_EuclidCreate(comm, &euc_precond);
SetDefaultOptions();
}
void HypreEuclid::SetDefaultOptions()
{
int euc_level = 1; // We use ILU(1)
int euc_stats = 0; // No logging
int euc_mem = 0; // No memory logging
int euc_bj = 0; // 1: Use Block Jacobi
int euc_ro_sc = 0; // 1: Use Row scaling
HYPRE_ParCSRMatrixGetComm(A, &comm);
HYPRE_EuclidCreate(comm, &euc_precond);
HYPRE_EuclidSetLevel(euc_precond, euc_level);
HYPRE_EuclidSetStats(euc_precond, euc_stats);
HYPRE_EuclidSetMem(euc_precond, euc_mem);
@@ -2675,38 +2515,6 @@ void HypreEuclid::SetDefaultOptions()
HYPRE_EuclidSetRowScale(euc_precond, euc_ro_sc);
}
void HypreEuclid::ResetEuclidPrecond(MPI_Comm comm)
{
// Euclid does not seem to offer access to its current configuration, so we
// simply reset it to its default options.
HYPRE_EuclidDestroy(euc_precond);
HYPRE_EuclidCreate(comm, &euc_precond);
SetDefaultOptions();
}
void HypreEuclid::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
if (A)
{
MPI_Comm comm;
HYPRE_ParCSRMatrixGetComm(*new_A, &comm);
ResetEuclidPrecond(comm);
}
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
HypreEuclid::~HypreEuclid()
{
HYPRE_EuclidDestroy(euc_precond);
@@ -2982,18 +2790,9 @@ HypreBoomerAMG::~HypreBoomerAMG()
HYPRE_BoomerAMGDestroy(amg_precond);
}
HypreAMS::HypreAMS(ParFiniteElementSpace *edge_fespace)
{
Init(edge_fespace);
}
HypreAMS::HypreAMS(HypreParMatrix &A, ParFiniteElementSpace *edge_fespace)
: HypreSolver(&A)
{
Init(edge_fespace);
}
void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
{
int cycle_type = 13;
int rlx_type = 2;
@@ -3177,22 +2976,6 @@ void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
error_mode = IGNORE_HYPRE_ERRORS;
}
void HypreAMS::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
HypreAMS::~HypreAMS()
{
HYPRE_AMSDestroy(ams);
@@ -3213,18 +2996,8 @@ void HypreAMS::SetPrintLevel(int print_lvl)
HYPRE_AMSSetPrintLevel(ams, print_lvl);
}
HypreADS::HypreADS(ParFiniteElementSpace *face_fespace)
{
Init(face_fespace);
}
HypreADS::HypreADS(HypreParMatrix &A, ParFiniteElementSpace *face_fespace)
: HypreSolver(&A)
{
Init(face_fespace);
}
void HypreADS::Init(ParFiniteElementSpace *face_fespace)
{
int cycle_type = 11;
int rlx_type = 2;
@@ -3447,22 +3220,6 @@ void HypreADS::Init(ParFiniteElementSpace *face_fespace)
error_mode = IGNORE_HYPRE_ERRORS;
}
void HypreADS::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(new_A, "new Operator must be a HypreParMatrix!");
// update base classes: Operator, Solver, HypreSolver
height = new_A->Height();
width = new_A->Width();
A = const_cast<HypreParMatrix *>(new_A);
setup_called = 0;
delete X;
delete B;
B = X = NULL;
}
HypreADS::~HypreADS()
{
HYPRE_ADSDestroy(ads);
-55
View File
@@ -735,15 +735,9 @@ class HyprePCG : public HypreSolver
private:
HYPRE_Solver pcg_solver;
HypreSolver * precond;
public:
HyprePCG(MPI_Comm comm);
HyprePCG(HypreParMatrix &_A);
virtual void SetOperator(const Operator &op);
void SetTol(double tol);
void SetMaxIter(int max_iter);
void SetLogging(int logging);
@@ -790,18 +784,9 @@ class HypreGMRES : public HypreSolver
private:
HYPRE_Solver gmres_solver;
HypreSolver * precond;
/// Default, generally robust, GMRES options
void SetDefaultOptions();
public:
HypreGMRES(MPI_Comm comm);
HypreGMRES(HypreParMatrix &_A);
virtual void SetOperator(const Operator &op);
void SetTol(double tol);
void SetMaxIter(int max_iter);
void SetKDim(int dim);
@@ -853,8 +838,6 @@ public:
explicit HypreDiagScale(HypreParMatrix &A) : HypreSolver(&A) { }
virtual operator HYPRE_Solver() const { return NULL; }
virtual void SetOperator(const Operator &op);
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRDiagScaleSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
@@ -870,21 +853,9 @@ class HypreParaSails : public HypreSolver
private:
HYPRE_Solver sai_precond;
/// Default, generally robust, ParaSails options
void SetDefaultOptions();
// If sai_precond is NULL, this method allocates it and sets default options.
// Otherwise the method saves the options from sai_precond, destroys it,
// allocates a new object, and sets its options to the saved values.
void ResetSAIPrecond(MPI_Comm comm);
public:
HypreParaSails(MPI_Comm comm);
HypreParaSails(HypreParMatrix &A);
virtual void SetOperator(const Operator &op);
void SetSymmetry(int sym);
/// The typecast to HYPRE_Solver returns the internal sai_precond
@@ -911,21 +882,9 @@ class HypreEuclid : public HypreSolver
private:
HYPRE_Solver euc_precond;
/// Default, generally robust, Euclid options
void SetDefaultOptions();
// If euc_precond is NULL, this method allocates it and sets default options.
// Otherwise the method saves the options from euc_precond, destroys it,
// allocates a new object, and sets its options to the saved values.
void ResetEuclidPrecond(MPI_Comm comm);
public:
HypreEuclid(MPI_Comm comm);
HypreEuclid(HypreParMatrix &A);
virtual void SetOperator(const Operator &op);
/// The typecast to HYPRE_Solver returns the internal euc_precond
virtual operator HYPRE_Solver() const { return euc_precond; }
@@ -1004,9 +963,6 @@ HypreParMatrix* DiscreteCurl(ParFiniteElementSpace *face_fespace,
class HypreAMS : public HypreSolver
{
private:
/// Constuct AMS solver from finite element space
void Init(ParFiniteElementSpace *edge_space);
HYPRE_Solver ams;
/// Vertex coordinates
@@ -1017,12 +973,8 @@ private:
HypreParMatrix *Pi, *Pix, *Piy, *Piz;
public:
HypreAMS(ParFiniteElementSpace *edge_fespace);
HypreAMS(HypreParMatrix &A, ParFiniteElementSpace *edge_fespace);
virtual void SetOperator(const Operator &op);
void SetPrintLevel(int print_lvl);
/// Set this option when solving a curl-curl problem with zero mass term
@@ -1043,9 +995,6 @@ public:
class HypreADS : public HypreSolver
{
private:
/// Constuct ADS solver from finite element space
void Init(ParFiniteElementSpace *face_fespace);
HYPRE_Solver ads;
/// Vertex coordinates
@@ -1060,12 +1009,8 @@ private:
HypreParMatrix *RT_Pi, *RT_Pix, *RT_Piy, *RT_Piz;
public:
HypreADS(ParFiniteElementSpace *face_fespace);
HypreADS(HypreParMatrix &A, ParFiniteElementSpace *face_fespace);
virtual void SetOperator(const Operator &op);
void SetPrintLevel(int print_lvl);
/// The typecast to HYPRE_Solver returns the internal ads object
+34 -129
View File
@@ -422,12 +422,6 @@ PetscParVector& PetscParVector::operator*=(PetscScalar s)
return *this;
}
PetscParVector& PetscParVector::operator+=(PetscScalar s)
{
ierr = VecShift(x,s); PCHKERRQ(x,ierr);
return *this;
}
void PetscParVector::PlaceArray(PetscScalar *temp_data)
{
ierr = VecPlaceArray(x,temp_data); PCHKERRQ(x,ierr);
@@ -2309,6 +2303,17 @@ void PetscLinearSolver::SetOperator(const Operator &op)
(dynamic_cast<const PetscParMatrix *>(&op));
const Operator *oA = dynamic_cast<const Operator *>(&op);
// Preserve Pmat if already set
KSP ksp = (KSP)obj;
Mat P = NULL;
PetscBool pmat;
ierr = KSPGetOperatorsSet(ksp,NULL,&pmat); PCHKERRQ(ksp,ierr);
if (pmat)
{
ierr = KSPGetOperators(ksp,NULL,&P); PCHKERRQ(ksp,ierr);
ierr = PetscObjectReference((PetscObject)P); PCHKERRQ(ksp,ierr);
}
// update base classes: Operator, Solver, PetscLinearSolver
bool delete_pA = false;
if (!pA)
@@ -2331,7 +2336,6 @@ void PetscLinearSolver::SetOperator(const Operator &op)
MFEM_VERIFY(pA, "Unsupported operation!");
// Set operators into PETSc KSP
KSP ksp = (KSP)obj;
Mat A = pA->A;
if (operatorset)
{
@@ -2352,7 +2356,15 @@ void PetscLinearSolver::SetOperator(const Operator &op)
wrap = false;
}
}
ierr = KSPSetOperators(ksp,A,A); PCHKERRQ(ksp,ierr);
if (P)
{
ierr = KSPSetOperators(ksp,A,P); PCHKERRQ(ksp,ierr);
ierr = MatDestroy(&P); PCHKERRQ(ksp,ierr);
}
else
{
ierr = KSPSetOperators(ksp,A,A); PCHKERRQ(ksp,ierr);
}
// Update PetscSolver
operatorset = true;
@@ -2488,7 +2500,7 @@ void PetscLinearSolver::SetPreconditioner(Solver &precond)
}
}
void PetscLinearSolver::MultKernel(const Vector &b, Vector &x, bool trans) const
void PetscLinearSolver::Mult(const Vector &b, Vector &x) const
{
KSP ksp = (KSP)obj;
@@ -2516,28 +2528,11 @@ void PetscLinearSolver::MultKernel(const Vector &b, Vector &x, bool trans) const
PCHKERRQ(ksp, ierr);
// Solve the system.
if (trans)
{
ierr = KSPSolveTranspose(ksp, B->x, X->x); PCHKERRQ(ksp,ierr);
}
else
{
ierr = KSPSolve(ksp, B->x, X->x); PCHKERRQ(ksp,ierr);
}
ierr = KSPSolve(ksp, B->x, X->x); PCHKERRQ(ksp,ierr);
B->ResetArray();
X->ResetArray();
}
void PetscLinearSolver::Mult(const Vector &b, Vector &x) const
{
(*this).MultKernel(b,x,false);
}
void PetscLinearSolver::MultTranspose(const Vector &b, Vector &x) const
{
(*this).MultKernel(b,x,true);
}
PetscLinearSolver::~PetscLinearSolver()
{
MPI_Comm comm;
@@ -2659,8 +2654,7 @@ void PetscPreconditioner::SetOperator(const Operator &op)
if (delete_pA) { delete pA; };
}
void PetscPreconditioner::MultKernel(const Vector &b, Vector &x,
bool trans) const
void PetscPreconditioner::Mult(const Vector &b, Vector &x) const
{
PC pc = (PC)obj;
@@ -2685,28 +2679,11 @@ void PetscPreconditioner::MultKernel(const Vector &b, Vector &x,
Customize();
// Apply the preconditioner.
if (trans)
{
ierr = PCApplyTranspose(pc, B->x, X->x); PCHKERRQ(pc, ierr);
}
else
{
ierr = PCApply(pc, B->x, X->x); PCHKERRQ(pc, ierr);
}
ierr = PCApply(pc, B->x, X->x); PCHKERRQ(pc, ierr);
B->ResetArray();
X->ResetArray();
}
void PetscPreconditioner::Mult(const Vector &b, Vector &x) const
{
(*this).MultKernel(b,x,false);
}
void PetscPreconditioner::MultTranspose(const Vector &b, Vector &x) const
{
(*this).MultKernel(b,x,true);
}
PetscPreconditioner::~PetscPreconditioner()
{
MPI_Comm comm;
@@ -3211,27 +3188,26 @@ PetscFieldSplitSolver::PetscFieldSplitSolver(MPI_Comm comm, Operator &op,
: PetscPreconditioner(comm,op,prefix)
{
PC pc = (PC)obj;
ierr = PCSetType(pc,PCFIELDSPLIT); PCHKERRQ(pc,ierr);
Mat pA;
ierr = PCGetOperators(pc,&pA,NULL); PCHKERRQ(pc,ierr);
// Check if pA is of type MATNEST
// (this requirement can be removed when we can pass fields).
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)pA,MATNEST,&isnest);
PCHKERRQ(pA,ierr);
MFEM_VERIFY(isnest,
"PetscFieldSplitSolver needs the matrix in nested format.");
PetscInt nr = 0;
IS *isrow = NULL;
if (isnest) // we now the fields
{
ierr = MatNestGetSize(pA,&nr,NULL); PCHKERRQ(pc,ierr);
ierr = PetscCalloc1(nr,&isrow); CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = MatNestGetISs(pA,isrow,NULL); PCHKERRQ(pc,ierr);
}
PetscInt nr;
IS *isrow;
ierr = PCSetType(pc,PCFIELDSPLIT); PCHKERRQ(pc,ierr);
ierr = MatNestGetSize(pA,&nr,NULL); PCHKERRQ(pc,ierr);
ierr = PetscCalloc1(nr,&isrow); CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = MatNestGetISs(pA,isrow,NULL); PCHKERRQ(pc,ierr);
// We need to customize here, before setting the index sets.
// This is because PCFieldSplitSetType customizes the function
// pointers. SubSolver options will be processed during PCApply
Customize();
for (PetscInt i=0; i<nr; i++)
@@ -3599,7 +3575,6 @@ void PetscODESolver::Run(Vector &x, double &t, double &dt, double t_final)
} // namespace mfem
#include "petsc/private/petscimpl.h"
#include "petsc/private/matimpl.h"
// auxiliary functions
static PetscErrorCode __mfem_ts_monitor(TS ts, PetscInt it, PetscReal t, Vec x,
@@ -3755,37 +3730,17 @@ static PetscErrorCode __mfem_ts_ijacobian(TS ts, PetscReal t, Vec x,
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
}
// Get nonzerostate
PetscObjectState nonzerostate;
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
// Avoid unneeded copy of the matrix by hacking
Mat B;
B = pA->ReleaseMat(false);
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
if (delete_pA) { delete pA; }
// Matrix-free case
if (A && A != P)
{
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
}
// When using MATNEST and PCFIELDSPLIT, the second setup of the
// preconditioner fails because MatCreateSubMatrix_Nest does not
// actually return a matrix. Instead, for efficiency reasons,
// it returns a reference to the submatrix. The second time it
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
// aborts since the two submatrices are actually different.
// We circumvent this issue by incrementing the nonzero state
// (i.e. PETSc thinks the operator sparsity pattern has changed)
// This does not impact performances in the case of MATNEST
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
CHKERRQ(ierr);
if (isnest) { P->nonzerostate = nonzerostate + 1; }
// Jacobian reusage
ierr = PetscObjectStateGet((PetscObject)P,&ts_ctx->cached_ijacstate);
CHKERRQ(ierr);
@@ -3942,7 +3897,6 @@ static PetscErrorCode __mfem_ts_computesplits(TS ts,PetscReal t,Vec x,Vec xp,
ierr = MatAXPY(*pJxp,-1.0,*pJx,SAME_NONZERO_PATTERN); PCHKERRQ(ts,ierr);
}
// Matrix-free cases
if (Ax && Ax != Jx)
{
ierr = MatAssemblyBegin(Ax,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
@@ -4029,31 +3983,12 @@ static PetscErrorCode __mfem_ts_rhsjacobian(TS ts, PetscReal t, Vec x,
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
}
// Get nonzerostate
PetscObjectState nonzerostate;
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
// Avoid unneeded copy of the matrix by hacking
Mat B;
B = pA->ReleaseMat(false);
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
if (delete_pA) { delete pA; }
// When using MATNEST and PCFIELDSPLIT, the second setup of the
// preconditioner fails because MatCreateSubMatrix_Nest does not
// actually return a matrix. Instead, for efficiency reasons,
// it returns a reference to the submatrix. The second time it
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
// aborts since the two submatrices are actually different.
// We circumvent this issue by incrementing the nonzero state
// (i.e. PETSc thinks the operator sparsity pattern has changed)
// This does not impact performances in the case of MATNEST
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
CHKERRQ(ierr);
if (isnest) { P->nonzerostate = nonzerostate + 1; }
// Matrix-free case
if (A && A != P)
{
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
@@ -4157,30 +4092,10 @@ static PetscErrorCode __mfem_snes_jacobian(SNES snes, Vec x, Mat A, Mat P,
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
}
// Get nonzerostate
PetscObjectState nonzerostate;
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
// Avoid unneeded copy of the matrix by hacking
Mat B = pA->ReleaseMat(false);
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
if (delete_pA) { delete pA; }
// When using MATNEST and PCFIELDSPLIT, the second setup of the
// preconditioner fails because MatCreateSubMatrix_Nest does not
// actually return a matrix. Instead, for efficiency reasons,
// it returns a reference to the submatrix. The second time it
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
// aborts since the two submatrices are actually different.
// We circumvent this issue by incrementing the nonzero state
// (i.e. PETSc thinks the operator sparsity pattern has changed)
// This does not impact performances in the case of MATNEST
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
CHKERRQ(ierr);
if (isnest) { P->nonzerostate = nonzerostate + 1; }
// Matrix-free case
if (A && A != P)
{
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
@@ -4528,11 +4443,6 @@ PetscErrorCode MakeShellPC(PC pc, mfem::Solver &precond, bool ownsop)
ctx->factory = NULL;
ctx->numprec = 0;
// In case the PC was already of type SHELL, this will destroy any
// previous user-defined data structure
// We cannot call PCReset as it will wipe out any operator already set
ierr = PCSetType(pc,PCNONE); CHKERRQ(ierr);
ierr = PCSetType(pc,PCSHELL); CHKERRQ(ierr);
ierr = PCShellSetName(pc,"MFEM Solver (unknown Pmat)"); CHKERRQ(ierr);
ierr = PCShellSetContext(pc,(void *)ctx); CHKERRQ(ierr);
@@ -4557,11 +4467,6 @@ PetscErrorCode MakeShellPCWithFactory(PC pc,
ctx->factory = factory;
ctx->numprec = 0;
// In case the PC was already of type SHELL, this will destroy any
// previous user-defined data structure
// We cannot call PCReset as it will wipe out any operator already set
ierr = PCSetType(pc,PCNONE); CHKERRQ(ierr);
ierr = PCSetType(pc,PCSHELL); CHKERRQ(ierr);
ierr = PCShellSetName(pc,factory->GetName()); CHKERRQ(ierr);
ierr = PCShellSetContext(pc,(void *)ctx); CHKERRQ(ierr);
-10
View File
@@ -170,7 +170,6 @@ public:
PetscParVector& operator+= (const PetscParVector &y);
PetscParVector& operator-= (const PetscParVector &y);
PetscParVector& operator*= (PetscScalar d);
PetscParVector& operator+= (PetscScalar d);
/** @brief Temporarily replace the data of the PETSc Vec object. To return to
the original data array, call ResetArray().
@@ -602,7 +601,6 @@ class PetscLinearSolver : public PetscSolver, public Solver
private:
/// Internal flag to handle HypreParMatrix conversion or not.
bool wrap;
void MultKernel(const Vector &b, Vector &x, bool trans) const;
public:
PetscLinearSolver(MPI_Comm comm, const std::string &prefix = std::string(),
@@ -618,8 +616,6 @@ public:
const std::string &prefix = std::string());
virtual ~PetscLinearSolver();
/// Sets the operator to be used for mat-vec operations and
/// for the construction of the preconditioner
virtual void SetOperator(const Operator &op);
/// Allows to prescribe a different operator (@a pop) to construct
@@ -627,12 +623,10 @@ public:
void SetOperator(const Operator &op, const Operator &pop);
/// Sets the solver to perform preconditioning
/// preserves the linear operator for the mat-vec
void SetPreconditioner(Solver &precond);
/// Application of the solver.
virtual void Mult(const Vector &b, Vector &x) const;
virtual void MultTranspose(const Vector &b, Vector &x) const;
/// Conversion function to PETSc's KSP type.
operator KSP() const { return (KSP)obj; }
@@ -652,9 +646,6 @@ public:
/// Abstract class for PETSc's preconditioners.
class PetscPreconditioner : public PetscSolver, public Solver
{
private:
void MultKernel(const Vector &b, Vector &x, bool trans) const;
public:
PetscPreconditioner(MPI_Comm comm,
const std::string &prefix = std::string());
@@ -668,7 +659,6 @@ public:
/// Application of the preconditioner.
virtual void Mult(const Vector &b, Vector &x) const;
virtual void MultTranspose(const Vector &b, Vector &x) const;
/// Conversion function to PETSc's PC type.
operator PC() const { return (PC)obj; }
-2
View File
@@ -1311,8 +1311,6 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
}
add(x, -c_scale, c, x);
ProcessNewState(x);
oper->Mult(x, r);
if (have_b)
{
-4
View File
@@ -283,10 +283,6 @@ public:
value of 0 indicates a failure, interrupting the Newton iteration. */
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const
{ return 1.0; }
/** @brief This method can be overloaded in derived classes to perform
computations that need knowledge of the newest Newton state. */
virtual void ProcessNewState(const Vector &x) const { }
};
/** Adaptive restarted GMRES.
+2 -4
View File
@@ -2640,11 +2640,9 @@ SparseMatrix &SparseMatrix::operator=(double a)
{
if (Rows == NULL)
{
const int nnz = J.Capacity();
double *h_A = HostWrite(A, nnz);
for (int i = 0; i < nnz; i++)
for (int i = 0, nnz = I[height]; i < nnz; i++)
{
h_A[i] = a;
A[i] = a;
}
}
else
-48
View File
@@ -152,54 +152,6 @@ public:
/// Return the element data, i.e. the array #A, const version.
inline const double *GetData() const { return A; }
// Memory access methods for the #I array.
Memory<int> &GetMemoryI() { return I; }
const Memory<int> &GetMemoryI() const { return I; }
const int *ReadI(bool on_dev = true) const
{ return mfem::Read(I, Height()+1, on_dev); }
int *WriteI(bool on_dev = true)
{ return mfem::Write(I, Height()+1, on_dev); }
int *ReadWriteI(bool on_dev = true)
{ return mfem::ReadWrite(I, Height()+1, on_dev); }
const int *HostReadI() const
{ return mfem::Read(I, Height()+1, false); }
int *HostWriteI()
{ return mfem::Write(I, Height()+1, false); }
int *HostReadWriteI()
{ return mfem::ReadWrite(I, Height()+1, false); }
// Memory access methods for the #J array.
Memory<int> &GetMemoryJ() { return J; }
const Memory<int> &GetMemoryJ() const { return J; }
const int *ReadJ(bool on_dev = true) const
{ return mfem::Read(J, J.Capacity(), on_dev); }
int *WriteJ(bool on_dev = true)
{ return mfem::Write(J, J.Capacity(), on_dev); }
int *ReadWriteJ(bool on_dev = true)
{ return mfem::ReadWrite(J, J.Capacity(), on_dev); }
const int *HostReadJ() const
{ return mfem::Read(J, J.Capacity(), false); }
int *HostWriteJ()
{ return mfem::Write(J, J.Capacity(), false); }
int *HostReadWriteJ()
{ return mfem::ReadWrite(J, J.Capacity(), false); }
// Memory access methods for the #A array.
Memory<double> &GetMemoryData() { return A; }
const Memory<double> &GetMemoryData() const { return A; }
const double *ReadData(bool on_dev = true) const
{ return mfem::Read(A, A.Capacity(), on_dev); }
double *WriteData(bool on_dev = true)
{ return mfem::Write(A, A.Capacity(), on_dev); }
double *ReadWriteData(bool on_dev = true)
{ return mfem::ReadWrite(A, A.Capacity(), on_dev); }
const double *HostReadData() const
{ return mfem::Read(A, A.Capacity(), false); }
double *HostWriteData()
{ return mfem::Write(A, A.Capacity(), false); }
double *HostReadWriteData()
{ return mfem::ReadWrite(A, A.Capacity(), false); }
/// Returns the number of elements in row @a i.
int RowSize(const int i) const;
+3 -15
View File
@@ -155,16 +155,12 @@ void CVODESolver::Init(TimeDependentOperator &f_)
// Get the vector length
long local_size = f_.Height();
#ifdef MFEM_USE_MPI
long global_size;
#endif
if (Parallel())
{
#ifdef MFEM_USE_MPI
MPI_Allreduce(&local_size, &global_size, 1, MPI_LONG, MPI_SUM,
NV_COMM_P(y));
#endif
}
// Get current time
@@ -173,7 +169,7 @@ void CVODESolver::Init(TimeDependentOperator &f_)
if (sundials_mem)
{
// Check if the problem size has changed since the last Init() call
int resize = 0;
int resize;
if (!Parallel())
{
resize = (NV_LENGTH_S(y) != local_size);
@@ -569,16 +565,12 @@ void ARKStepSolver::Init(TimeDependentOperator &f_)
// Get the vector length
long local_size = f_.Height();
#ifdef MFEM_USE_MPI
long global_size;
#endif
if (Parallel())
{
#ifdef MFEM_USE_MPI
MPI_Allreduce(&local_size, &global_size, 1, MPI_LONG, MPI_SUM,
NV_COMM_P(y));
#endif
}
// Get current time
@@ -587,7 +579,7 @@ void ARKStepSolver::Init(TimeDependentOperator &f_)
if (sundials_mem)
{
// Check if the problem size has changed since the last Init() call
int resize = 0;
int resize;
if (!Parallel())
{
resize = (NV_LENGTH_S(y) != local_size);
@@ -1057,22 +1049,18 @@ void KINSolver::SetOperator(const Operator &op)
// Get the vector length
long local_size = height;
#ifdef MFEM_USE_MPI
long global_size;
#endif
if (Parallel())
{
#ifdef MFEM_USE_MPI
MPI_Allreduce(&local_size, &global_size, 1, MPI_LONG, MPI_SUM,
NV_COMM_P(y));
#endif
}
if (sundials_mem)
{
// Check if the problem size has changed since the last SetOperator call
int resize = 0;
int resize;
if (!Parallel())
{
resize = (NV_LENGTH_S(y) != local_size);
-9
View File
@@ -20,10 +20,6 @@
#include "superlu_defs.h"
#include "superlu_ddefs.h"
#if XSDK_INDEX_SIZE == 64
#error "SuperLUDist has been built with 64bit integers. This is not supported"
#endif
using namespace std;
namespace mfem
@@ -134,11 +130,6 @@ SuperLURowLocMatrix::SuperLURowLocMatrix( const HypreParMatrix & hypParMat )
// hypre_CSRMatrix.
hypre_CSRMatrix * csr_op = hypre_MergeDiagAndOffd(parcsr_op);
hypre_CSRMatrixSetDataOwner(csr_op,0);
#if MFEM_HYPRE_VERSION >= 21600
MFEM_VERIFY(csr_op->num_rows < INT_MAX,"SuperLU: number of local rows "
"is too large to store as an integer.");
hypre_CSRMatrixBigJtoJ(csr_op);
#endif
int m = parcsr_op->global_num_rows;
int n = parcsr_op->global_num_cols;
+1 -2
View File
@@ -834,10 +834,9 @@ double Vector::Sum() const
{
double sum = 0.0;
const double *h_data = this->HostRead();
for (int i = 0; i < size; i++)
{
sum += h_data[i];
sum += data[i];
}
return sum;
-20
View File
@@ -140,13 +140,6 @@ public:
@sa NewDataAndSize(). */
inline void NewMemoryAndSize(const Memory<double> &mem, int s, bool own_mem);
/// Reset the Vector to be a reference to a sub-vector of @a base.
inline void MakeRef(Vector &base, int offset, int size);
/** @brief Reset the Vector to be a reference to a sub-vector of @a base
without changing its current size. */
inline void MakeRef(Vector &base, int offset);
/// Set the Vector data (host pointer) ownership flag.
void MakeDataOwner() const { data.SetHostPtrOwner(true); }
@@ -462,19 +455,6 @@ inline void Vector::NewMemoryAndSize(const Memory<double> &mem, int s,
if (!own_mem) { data.ClearOwnerFlags(); }
}
inline void Vector::MakeRef(Vector &base, int offset, int s)
{
data.Delete();
size = s;
data.MakeAlias(base.GetMemory(), offset, s);
}
inline void Vector::MakeRef(Vector &base, int offset)
{
data.Delete();
data.MakeAlias(base.GetMemory(), offset, size);
}
inline void Vector::Destroy()
{
const bool use_dev = data.UseDevice();
+7 -3
View File
@@ -258,7 +258,7 @@ endif
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
MFEM_REQ_LIB_DEPS = SUPERLU METIS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
SUITESPARSE STRUMPACK GECKO GNUTLS NETCDF PETSC MPFR PUMI OCCA RAJA
SUITESPARSE STRUMPACK GECKO GNUTLS NETCDF PETSC MPFR PUMI OCCA CEED RAJA
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
define mfem_check_dependency
@@ -319,7 +319,7 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE MFEM_USE_GECKO\
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GNUTLS MFEM_USE_NETCDF\
MFEM_USE_PETSC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_CONDUIT MFEM_USE_PUMI\
MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_OCCA MFEM_USE_RAJA\
MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_RAJA\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR
# List of makefile variables that will be written to config.mk:
@@ -387,7 +387,7 @@ ifneq (,$(filter install,$(MAKECMDGOALS)))
endif
# Source dirs in logical order
DIRS = general linalg mesh fem
DIRS = general linalg mesh fem fem/libceed
SOURCE_FILES = $(foreach dir,$(DIRS),$(wildcard $(SRC)$(dir)/*.cpp))
RELSRC_FILES = $(patsubst $(SRC)%,%,$(SOURCE_FILES))
OBJECT_FILES = $(patsubst $(SRC)%,$(BLD)%,$(SOURCE_FILES:.cpp=.o))
@@ -550,6 +550,9 @@ install: $(if $(static),$(BLD)libmfem.a) $(if $(shared),$(BLD)libmfem.$(SO_EXT))
mkdir -p $(PREFIX_INC)/mfem/$$dir && \
$(INSTALL) -m 640 $(SRC)$$dir/*.okl $(PREFIX_INC)/mfem/$$dir; \
done
# install libCEED q-function headers
mkdir -p $(PREFIX_INC)/mfem/fem/libceed
$(INSTALL) -m 640 $(SRC)fem/libceed/*.h $(PREFIX_INC)/mfem/fem/libceed
# install config.mk in $(PREFIX_SHARE)
mkdir -p $(PREFIX_SHARE)
$(MAKE) -C $(BLD)config config-mk CONFIG_MK=config-install.mk
@@ -629,6 +632,7 @@ status info:
$(info MFEM_USE_HIP = $(MFEM_USE_HIP))
$(info MFEM_USE_RAJA = $(MFEM_USE_RAJA))
$(info MFEM_USE_OCCA = $(MFEM_USE_OCCA))
$(info MFEM_USE_CEED = $(MFEM_USE_CEED))
$(info MFEM_CXX = $(value MFEM_CXX))
$(info MFEM_CPPFLAGS = $(value MFEM_CPPFLAGS))
$(info MFEM_CXXFLAGS = $(value MFEM_CXXFLAGS))
-5
View File
@@ -74,13 +74,8 @@ public:
virtual const int *GetEdgeVertices(int) const = 0;
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const = 0;
virtual int GetNFaces() const = 0;
virtual int GetNFaceVertices(int fi) const = 0;
virtual const int *GetFaceVertices(int fi) const = 0;
/// Mark the longest edge by assuming/changing the order of the vertices.
-5
View File
@@ -51,14 +51,9 @@ public:
virtual const int *GetEdgeVertices(int ei) const
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 4; return 6; }
virtual int GetNFaces() const { return 6; }
virtual int GetNFaceVertices(int) const { return 4; }
virtual const int *GetFaceVertices(int fi) const
{ return geom_t::FaceVert[fi]; }
+160 -304
View File
@@ -430,8 +430,6 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
else
{
const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
if (bdr_el)
{
Array<int> vdofs;
@@ -442,7 +440,7 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
{
for (int j = 0; j < n; j++)
{
pm(k,j) = nodes(vdofs[n*k+j]);
pm(k,j) = (*Nodes)(vdofs[n*k+j]);
}
}
ElTr->SetFE(bdr_el);
@@ -494,8 +492,6 @@ void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
else // curved mesh
{
const FiniteElement *face_el = Nodes->FESpace()->GetFaceElement(FaceNo);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
if (face_el)
{
Array<int> vdofs;
@@ -506,7 +502,7 @@ void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
{
for (int j = 0; j < n; j++)
{
pm(i, j) = nodes(vdofs[n*i+j]);
pm(i, j) = (*Nodes)(vdofs[n*i+j]);
}
}
FTr->SetFE(face_el);
@@ -3302,10 +3298,6 @@ Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
Array<int> rdofs;
DenseMatrix phys_pts;
int max_nv = 0;
DenseMatrix node_coordinates(spaceDim*pow(2, Dim), r_num_elem);
H1_FECollection vertex_fec(1, Dim);
for (int el = 0; el < orig_mesh->GetNE(); el++)
{
Geometry::Type geom = orig_mesh->GetElementBaseGeometry(el);
@@ -3320,7 +3312,6 @@ Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
orig_mesh->GetElementTransformation(el)->Transform(rfe->GetNodes(),
phys_pts);
const int *c2h_map = rfec.GetDofMap(geom);
const int *vertex_map = vertex_fec.GetDofMap(geom);
for (int i = 0; i < phys_pts.Width(); i++)
{
vertices[rdofs[i]].SetCoords(spaceDim, phys_pts.GetColumn(i));
@@ -3335,24 +3326,9 @@ Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
v[k] = rdofs[c2h_map[cid]];
}
for (int k = 0; k < nvert; k++)
{
for (int j = 0; j < spaceDim; ++j)
{
node_coordinates(k*spaceDim + j, NumOfElements)
= vertices[v[vertex_map[k]]](j);
}
}
AddElement(elem);
}
}
SetCurvature(1, true, spaceDim);
Vector node_coordinates_vec(
node_coordinates.Data(),
node_coordinates.Width()*node_coordinates.Height());
SetNodes(node_coordinates_vec);
// Add refined boundary elements
for (int el = 0; el < orig_mesh->GetNBE(); el++)
{
@@ -3453,28 +3429,6 @@ void Mesh::KnotInsert(Array<KnotVector *> &kv)
UpdateNURBS();
}
void Mesh::KnotInsert(Array<Vector *> &kv)
{
if (NURBSext == NULL)
{
mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
}
if (kv.Size() != NURBSext->GetNKV())
{
mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
}
NURBSext->ConvertToPatches(*Nodes);
NURBSext->KnotInsert(kv);
last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
sequence++;
UpdateNURBS();
}
void Mesh::NURBSUniformRefinement()
{
// do not check for NURBSext since this method is protected
@@ -4805,13 +4759,7 @@ void Mesh::GenerateNCFaceInfo()
for (unsigned i = 0; i < list.slaves.size(); i++)
{
const NCMesh::Slave &slave = list.slaves[i];
if (slave.index < 0 || // degenerate slave face
slave.index >= nfaces || // ghost slave
slave.master >= nfaces) // has ghost master
{
continue;
}
if (slave.index >= nfaces || slave.master >= nfaces) { continue; }
FaceInfo &slave_fi = faces_info[slave.index];
FaceInfo &master_fi = faces_info[slave.master];
@@ -4964,39 +4912,15 @@ STable3D *Mesh::GetElementToFaceTable(int ret_ftbl)
return NULL;
}
// shift cyclically 3 integers so that the smallest is first
static inline
void Rotate3(int &a, int &b, int &c)
{
if (a < b)
{
if (a > c)
{
ShiftRight(a, b, c);
}
}
else
{
if (b < c)
{
ShiftRight(c, b, a);
}
else
{
ShiftRight(a, b, c);
}
}
}
void Mesh::ReorientTetMesh()
{
int *v;
if (Dim != 3 || !(meshgen & 1))
{
return;
}
DeleteLazyTables();
DSTable *old_v_to_v = NULL;
Table *old_elem_vert = NULL;
@@ -5009,7 +4933,7 @@ void Mesh::ReorientTetMesh()
{
if (GetElementType(i) == Element::TETRAHEDRON)
{
int *v = elements[i]->GetVertices();
v = elements[i]->GetVertices();
Rotate3(v[0], v[1], v[2]);
if (v[0] < v[3])
@@ -5018,7 +4942,7 @@ void Mesh::ReorientTetMesh()
}
else
{
ShiftRight(v[0], v[1], v[3]);
ShiftL2R(v[0], v[1], v[3]);
}
}
}
@@ -5027,7 +4951,7 @@ void Mesh::ReorientTetMesh()
{
if (GetBdrElementType(i) == Element::TRIANGLE)
{
int *v = boundary[i]->GetVertices();
v = boundary[i]->GetVertices();
Rotate3(v[0], v[1], v[2]);
}
@@ -5859,7 +5783,7 @@ void Mesh::SetVertices(const Vector &vert_coord)
}
}
void Mesh::GetNode(int i, double *coord) const
void Mesh::GetNode(int i, double *coord)
{
if (Nodes)
{
@@ -6010,19 +5934,16 @@ void Mesh::UniformRefinement2D()
const int oedge = NumOfVertices;
const int oelem = oedge + NumOfEdges;
Array<Element*> new_elements;
Array<Element*> new_boundary;
vertices.SetSize(oelem + quad_counter);
new_elements.SetSize(4 * NumOfElements);
elements.SetSize(4 * NumOfElements);
quad_counter = 0;
for (int i = 0, j = 0; i < NumOfElements; i++)
for (int i = 0; i < NumOfElements; i++)
{
const Element::Type el_type = elements[i]->GetType();
const int attr = elements[i]->GetAttribute();
int *v = elements[i]->GetVertices();
const int *e = el_to_edge->GetRow(i);
const int j = NumOfElements + 3 * i;
int vv[2];
if (el_type == Element::TRIANGLE)
@@ -6036,14 +5957,12 @@ void Mesh::UniformRefinement2D()
AverageVertices(vv, 2, oedge+e[ei]);
}
new_elements[j++] =
new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
new_elements[j++] =
new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
new_elements[j++] =
new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
new_elements[j++] =
new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
elements[j+0] = new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
elements[j+1] = new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
elements[j+2] = new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
v[1] = oedge+e[0];
v[2] = oedge+e[2];
}
else if (el_type == Element::QUADRILATERAL)
{
@@ -6060,36 +5979,34 @@ void Mesh::UniformRefinement2D()
AverageVertices(vv, 2, oedge+e[ei]);
}
new_elements[j++] =
new Quadrilateral(v[0], oedge+e[0], oelem+qe, oedge+e[3], attr);
new_elements[j++] =
new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oelem+qe, attr);
new_elements[j++] =
new Quadrilateral(oelem+qe, oedge+e[1], v[2], oedge+e[2], attr);
new_elements[j++] =
new Quadrilateral(oedge+e[3], oelem+qe, oedge+e[2], v[3], attr);
elements[j+0] = new Quadrilateral(oedge+e[0], v[1], oedge+e[1],
oelem+qe, attr);
elements[j+1] = new Quadrilateral(oelem+qe, oedge+e[1],
v[2], oedge+e[2], attr);
elements[j+2] = new Quadrilateral(oedge+e[3], oelem+qe,
oedge+e[2], v[3], attr);
v[1] = oedge+e[0];
v[2] = oelem+qe;
v[3] = oedge+e[3];
}
else
{
MFEM_ABORT("unknown element type: " << el_type);
}
FreeElement(elements[i]);
}
mfem::Swap(elements, new_elements);
// refine boundary elements
new_boundary.SetSize(2 * NumOfBdrElements);
for (int i = 0, j = 0; i < NumOfBdrElements; i++)
boundary.SetSize(2 * NumOfBdrElements);
for (int i = 0; i < NumOfBdrElements; i++)
{
const int attr = boundary[i]->GetAttribute();
int *v = boundary[i]->GetVertices();
const int j = NumOfBdrElements + i;
new_boundary[j++] = new Segment(v[0], oedge+be_to_edge[i], attr);
new_boundary[j++] = new Segment(oedge+be_to_edge[i], v[1], attr);
boundary[j] = new Segment(oedge+be_to_edge[i], v[1], attr);
FreeElement(boundary[i]);
v[1] = oedge+be_to_edge[i];
}
mfem::Swap(boundary, new_boundary);
static const double A = 0.0, B = 0.5, C = 1.0;
static double tri_children[2*3*4] =
@@ -6107,17 +6024,17 @@ void Mesh::UniformRefinement2D()
A,B, B,B, B,C, A,C // upper-left
};
CoarseFineTr.point_matrices[Geometry::TRIANGLE]
.UseExternalData(tri_children, 2, 3, 4);
CoarseFineTr.point_matrices[Geometry::SQUARE]
.UseExternalData(quad_children, 2, 4, 4);
CoarseFineTr.point_matrices[Geometry::TRIANGLE].
UseExternalData(tri_children, 2, 3, 4);
CoarseFineTr.point_matrices[Geometry::SQUARE].
UseExternalData(quad_children, 2, 4, 4);
CoarseFineTr.embeddings.SetSize(elements.Size());
for (int i = 0; i < elements.Size(); i++)
{
Embedding &emb = CoarseFineTr.embeddings[i];
emb.parent = i / 4;
emb.matrix = i % 4;
emb.parent = (i < NumOfElements) ? i : (i - NumOfElements) / 3;
emb.matrix = (i < NumOfElements) ? 0 : (i - NumOfElements) % 3 + 1;
}
NumOfVertices = vertices.Size();
@@ -6249,20 +6166,17 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
const int oface = oedge + NumOfEdges;
const int oelem = oface + NumOfQuadFaces;
Array<Element*> new_elements;
Array<Element*> new_boundary;
vertices.SetSize(oelem + hex_counter);
new_elements.SetSize(8 * NumOfElements);
CoarseFineTr.embeddings.SetSize(new_elements.Size());
elements.SetSize(8 * NumOfElements);
CoarseFineTr.embeddings.SetSize(elements.Size());
hex_counter = 0;
for (int i = 0, j = 0; i < NumOfElements; i++)
for (int i = 0; i < NumOfElements; i++)
{
const Element::Type el_type = elements[i]->GetType();
const int attr = elements[i]->GetAttribute();
int *v = elements[i]->GetVertices();
const int *e = el_to_edge->GetRow(i);
const int j = NumOfElements + 7 * i;
int vv[4], ev[12];
if (e2v.Size())
@@ -6408,54 +6322,51 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
const int (&mv)[4][4] = mv_all[rt];
#ifndef MFEM_USE_MEMALLOC
new_elements[j+0] =
new Tetrahedron(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
new_elements[j+1] =
new Tetrahedron(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
new_elements[j+2] =
new Tetrahedron(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
new_elements[j+3] =
new Tetrahedron(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
elements[j+0] = new Tetrahedron(oedge+e[0], v[1],
oedge+e[3], oedge+e[4], attr);
elements[j+1] = new Tetrahedron(oedge+e[1], oedge+e[3],
v[2], oedge+e[5], attr);
elements[j+2] = new Tetrahedron(oedge+e[2], oedge+e[4],
oedge+e[5], v[3], attr);
for (int k = 0; k < 4; k++)
{
new_elements[j+4+k] =
elements[j+k+3] =
new Tetrahedron(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
}
#else
Tetrahedron *tet;
new_elements[j+0] = tet = TetMemory.Alloc();
tet->Init(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
new_elements[j+1] = tet = TetMemory.Alloc();
elements[j+0] = tet = TetMemory.Alloc();
tet->Init(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
new_elements[j+2] = tet = TetMemory.Alloc();
elements[j+1] = tet = TetMemory.Alloc();
tet->Init(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
new_elements[j+3] = tet = TetMemory.Alloc();
elements[j+2] = tet = TetMemory.Alloc();
tet->Init(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
for (int k = 0; k < 4; k++)
{
new_elements[j+4+k] = tet = TetMemory.Alloc();
elements[j+k+3] = tet = TetMemory.Alloc();
tet->Init(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
}
#endif
for (int k = 0; k < 4; k++)
v[1] = oedge+e[0];
v[2] = oedge+e[1];
v[3] = oedge+e[2];
((Tetrahedron*)elements[i])->SetRefinementFlag(0);
CoarseFineTr.embeddings[i].parent = i;
CoarseFineTr.embeddings[i].matrix = 0;
for (int k = 0; k < 3; k++)
{
CoarseFineTr.embeddings[j+k].parent = i;
CoarseFineTr.embeddings[j+k].matrix = k;
CoarseFineTr.embeddings[j+k].matrix = k+1;
}
for (int k = 0; k < 4; k++)
{
CoarseFineTr.embeddings[j+4+k].parent = i;
CoarseFineTr.embeddings[j+4+k].matrix = 4*(rt+1)+k;
CoarseFineTr.embeddings[j+k+3].parent = i;
CoarseFineTr.embeddings[j+k+3].matrix = 4*(rt+1)+k;
}
j += 8;
}
break;
@@ -6485,37 +6396,33 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
const int qf3 = f2qf[f[3]];
const int qf4 = f2qf[f[4]];
new_elements[j++] =
new Wedge(v[0], oedge+e[0], oedge+e[2],
oedge+e[6], oface+qf2, oface+qf4, attr);
elements[j+0] = new Wedge(oedge+e[1], oedge+e[2], oedge+e[0],
oface+qf3, oface+qf4, oface+qf2,
attr);
elements[j+1] = new Wedge(oedge+e[0], v[1], oedge+e[1],
oface+qf2, oedge+e[7], oface+qf3,
attr);
elements[j+2] = new Wedge(oedge+e[2], oedge+e[1], v[2],
oface+qf4, oface+qf3, oedge+e[8],
attr);
elements[j+3] = new Wedge(oedge+e[6], oface+qf2, oface+qf4,
v[3], oedge+e[3], oedge+e[5],
attr);
elements[j+4] = new Wedge(oface+qf3, oface+qf4, oface+qf2,
oedge+e[4], oedge+e[5], oedge+e[3],
attr);
elements[j+5] = new Wedge(oface+qf2, oedge+e[7], oface+qf3,
oedge+e[3], v[4], oedge+e[4],
attr);
elements[j+6] = new Wedge(oface+qf4, oface+qf3, oedge+e[8],
oedge+e[5], oedge+e[4], v[5],
attr);
new_elements[j++] =
new Wedge(oedge+e[1], oedge+e[2], oedge+e[0],
oface+qf3, oface+qf4, oface+qf2, attr);
new_elements[j++] =
new Wedge(oedge+e[0], v[1], oedge+e[1],
oface+qf2, oedge+e[7], oface+qf3, attr);
new_elements[j++] =
new Wedge(oedge+e[2], oedge+e[1], v[2],
oface+qf4, oface+qf3, oedge+e[8], attr);
new_elements[j++] =
new Wedge(oedge+e[6], oface+qf2, oface+qf4,
v[3], oedge+e[3], oedge+e[5], attr);
new_elements[j++] =
new Wedge(oface+qf3, oface+qf4, oface+qf2,
oedge+e[4], oedge+e[5], oedge+e[3], attr);
new_elements[j++] =
new Wedge(oface+qf2, oedge+e[7], oface+qf3,
oedge+e[3], v[4], oedge+e[4], attr);
new_elements[j++] =
new Wedge(oface+qf4, oface+qf3, oedge+e[8],
oedge+e[5], oedge+e[4], v[5], attr);
v[1] = oedge+e[0];
v[2] = oedge+e[2];
v[3] = oedge+e[6];
v[4] = oface+qf2;
v[5] = oface+qf4;
}
break;
@@ -6557,38 +6464,35 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
AverageVertices(vv, 2, oedge+e[ei]);
}
new_elements[j++] =
new Hexahedron(v[0], oedge+e[0], oface+qf[0],
oedge+e[3], oedge+e[8], oface+qf[1],
oelem+he, oface+qf[4], attr);
new_elements[j++] =
new Hexahedron(oedge+e[0], v[1], oedge+e[1],
oface+qf[0], oface+qf[1], oedge+e[9],
oface+qf[2], oelem+he, attr);
new_elements[j++] =
new Hexahedron(oface+qf[0], oedge+e[1], v[2],
oedge+e[2], oelem+he, oface+qf[2],
oedge+e[10], oface+qf[3], attr);
new_elements[j++] =
new Hexahedron(oedge+e[3], oface+qf[0], oedge+e[2],
v[3], oface+qf[4], oelem+he,
oface+qf[3], oedge+e[11], attr);
new_elements[j++] =
new Hexahedron(oedge+e[8], oface+qf[1], oelem+he,
oface+qf[4], v[4], oedge+e[4],
oface+qf[5], oedge+e[7], attr);
new_elements[j++] =
new Hexahedron(oface+qf[1], oedge+e[9], oface+qf[2],
oelem+he, oedge+e[4], v[5],
oedge+e[5], oface+qf[5], attr);
new_elements[j++] =
new Hexahedron(oelem+he, oface+qf[2], oedge+e[10],
oface+qf[3], oface+qf[5], oedge+e[5],
v[6], oedge+e[6], attr);
new_elements[j++] =
new Hexahedron(oface+qf[4], oelem+he, oface+qf[3],
oedge+e[11], oedge+e[7], oface+qf[5],
oedge+e[6], v[7], attr);
elements[j+0] = new Hexahedron(oedge+e[0], v[1], oedge+e[1],
oface+qf[0], oface+qf[1], oedge+e[9],
oface+qf[2], oelem+he, attr);
elements[j+1] = new Hexahedron(oface+qf[0], oedge+e[1], v[2],
oedge+e[2], oelem+he, oface+qf[2],
oedge+e[10], oface+qf[3], attr);
elements[j+2] = new Hexahedron(oedge+e[3], oface+qf[0], oedge+e[2],
v[3], oface+qf[4], oelem+he,
oface+qf[3], oedge+e[11], attr);
elements[j+3] = new Hexahedron(oedge+e[8], oface+qf[1], oelem+he,
oface+qf[4], v[4], oedge+e[4],
oface+qf[5], oedge+e[7], attr);
elements[j+4] = new Hexahedron(oface+qf[1], oedge+e[9], oface+qf[2],
oelem+he, oedge+e[4], v[5],
oedge+e[5], oface+qf[5], attr);
elements[j+5] = new Hexahedron(oelem+he, oface+qf[2], oedge+e[10],
oface+qf[3], oface+qf[5], oedge+e[5],
v[6], oedge+e[6], attr);
elements[j+6] = new Hexahedron(oface+qf[4], oelem+he, oface+qf[3],
oedge+e[11], oedge+e[7], oface+qf[5],
oedge+e[6], v[7], attr);
v[1] = oedge+e[0];
v[2] = oface+qf[0];
v[3] = oedge+e[3];
v[4] = oedge+e[8];
v[5] = oface+qf[1];
v[6] = oelem+he;
v[7] = oface+qf[4];
}
break;
@@ -6596,18 +6500,16 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
MFEM_ABORT("Unknown 3D element type \"" << el_type << "\"");
break;
}
FreeElement(elements[i]);
}
mfem::Swap(elements, new_elements);
// refine boundary elements
new_boundary.SetSize(4 * NumOfBdrElements);
for (int i = 0, j = 0; i < NumOfBdrElements; i++)
boundary.SetSize(4 * NumOfBdrElements);
for (int i = 0; i < NumOfBdrElements; i++)
{
const Element::Type bdr_el_type = boundary[i]->GetType();
const int attr = boundary[i]->GetAttribute();
int *v = boundary[i]->GetVertices();
const int *e = bel_to_edge->GetRow(i);
const int j = NumOfBdrElements + 3 * i;
int ev[4];
if (e2v.Size())
@@ -6619,36 +6521,34 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
if (bdr_el_type == Element::TRIANGLE)
{
new_boundary[j++] =
new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
new_boundary[j++] =
new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
new_boundary[j++] =
new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
new_boundary[j++] =
new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
boundary[j+0] = new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
boundary[j+1] = new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
boundary[j+2] = new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
v[1] = oedge+e[0];
v[2] = oedge+e[2];
}
else if (bdr_el_type == Element::QUADRILATERAL)
{
const int qf =
(f2qf.Size() == 0) ? be_to_face[i] : f2qf[be_to_face[i]];
new_boundary[j++] =
new Quadrilateral(v[0], oedge+e[0], oface+qf, oedge+e[3], attr);
new_boundary[j++] =
new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oface+qf, attr);
new_boundary[j++] =
new Quadrilateral(oface+qf, oedge+e[1], v[2], oedge+e[2], attr);
new_boundary[j++] =
new Quadrilateral(oedge+e[3], oface+qf, oedge+e[2], v[3], attr);
boundary[j+0] = new Quadrilateral(oedge+e[0], v[1], oedge+e[1],
oface+qf, attr);
boundary[j+1] = new Quadrilateral(oface+qf, oedge+e[1], v[2],
oedge+e[2], attr);
boundary[j+2] = new Quadrilateral(oedge+e[3], oface+qf,
oedge+e[2], v[3], attr);
v[1] = oedge+e[0];
v[2] = oface+qf;
v[3] = oedge+e[3];
}
else
{
MFEM_ABORT("boundary Element is not a triangle or a quad!");
}
FreeElement(boundary[i]);
}
mfem::Swap(boundary, new_boundary);
static const double A = 0.0, B = 0.5, C = 1.0;
static double tet_children[3*4*16] =
@@ -6699,21 +6599,20 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p)
A,B,B, B,B,B, B,C,B, A,C,B, A,B,C, B,B,C, B,C,C, A,C,C
};
CoarseFineTr.point_matrices[Geometry::TETRAHEDRON]
.UseExternalData(tet_children, 3, 4, 16);
CoarseFineTr.point_matrices[Geometry::PRISM]
.UseExternalData(pri_children, 3, 6, 8);
CoarseFineTr.point_matrices[Geometry::CUBE]
.UseExternalData(hex_children, 3, 8, 8);
CoarseFineTr.point_matrices[Geometry::TETRAHEDRON].
UseExternalData(tet_children, 3, 4, 16);
CoarseFineTr.point_matrices[Geometry::PRISM].
UseExternalData(pri_children, 3, 6, 8);
CoarseFineTr.point_matrices[Geometry::CUBE].
UseExternalData(hex_children, 3, 8, 8);
for (int i = 0; i < elements.Size(); i++)
{
// tetrahedron elements are handled above:
// Tetrahedron elements are handled above:
if (elements[i]->GetType() == Element::TETRAHEDRON) { continue; }
Embedding &emb = CoarseFineTr.embeddings[i];
emb.parent = i / 8;
emb.matrix = i % 8;
emb.parent = (i < NumOfElements) ? i : (i - NumOfElements) / 7;
emb.matrix = (i < NumOfElements) ? 0 : (i - NumOfElements) % 7 + 1;
}
NumOfVertices = vertices.Size();
@@ -7297,13 +7196,13 @@ void Mesh::GeneralRefinement(const Array<Refinement> &refinements,
else if (nonconforming < 0)
{
// determine if nonconforming refinement is suitable
if ((meshgen & 2) || (meshgen & 4))
if (meshgen & 2)
{
nonconforming = 1; // tensor product elements and wedges
nonconforming = 1;
}
else
{
nonconforming = 0; // simplices
nonconforming = 0;
}
}
@@ -7358,9 +7257,8 @@ void Mesh::EnsureNCMesh(bool triangles_nonconforming)
if (!ncmesh)
{
if ((meshgen & 0x2) /* quads/hexes */ ||
(meshgen & 0x4) /* wedges */ ||
(triangles_nonconforming && Dim == 2 && (meshgen & 0x1)))
if ((meshgen & 2) /* quads/hexes */ ||
(triangles_nonconforming && Dim == 2 && (meshgen & 1)))
{
MFEM_VERIFY(GetNumGeometries(Dim) <= 1,
"mixed meshes are not supported");
@@ -7783,7 +7681,12 @@ void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
void Mesh::InitRefinementTransforms()
{
// initialize CoarseFineTr
CoarseFineTr.Clear();
map<Geometry::Type,DenseTensor> &pms = CoarseFineTr.point_matrices;
map<Geometry::Type,DenseTensor>::iterator pms_iter;
for (pms_iter = pms.begin(); pms_iter != pms.end(); ++pms_iter)
{
pms_iter->second.SetSize(0, 0, 0);
}
CoarseFineTr.embeddings.SetSize(NumOfElements);
for (int i = 0; i < NumOfElements; i++)
{
@@ -10113,51 +10016,4 @@ Mesh *Extrude2D(Mesh *mesh, const int nz, const double sz)
return mesh3d;
}
#ifdef MFEM_DEBUG
void Mesh::DebugDump(std::ostream &out) const
{
// dump vertices and edges (NCMesh "nodes")
out << NumOfVertices + NumOfEdges << "\n";
for (int i = 0; i < NumOfVertices; i++)
{
const double *v = GetVertex(i);
out << i << " " << v[0] << " " << v[1] << " " << v[2]
<< " 0 0 " << i << " -1 0\n";
}
Array<int> ev;
for (int i = 0; i < NumOfEdges; i++)
{
GetEdgeVertices(i, ev);
double mid[3] = {0, 0, 0};
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < spaceDim; k++)
{
mid[k] += GetVertex(ev[j])[k];
}
}
out << NumOfVertices+i << " "
<< mid[0]/2 << " " << mid[1]/2 << " " << mid[2]/2 << " "
<< ev[0] << " " << ev[1] << " -1 " << i << " 0\n";
}
// dump elements
out << NumOfElements << "\n";
for (int i = 0; i < NumOfElements; i++)
{
const Element* e = elements[i];
out << e->GetNVertices() << " ";
for (int j = 0; j < e->GetNVertices(); j++)
{
out << e->GetVertices()[j] << " ";
}
out << e->GetAttribute() << " 0 " << i << "\n";
}
// dump faces
out << "0\n";
}
#endif
}
+30 -9
View File
@@ -386,6 +386,11 @@ protected:
return FaceIsInterior(FaceNo) || (faces_info[FaceNo].Elem2Inf >= 0);
}
// shift cyclically 3 integers left-to-right
inline static void ShiftL2R(int &, int &, int &);
// shift cyclically 3 integers so that the smallest is first
inline static void Rotate3(int &, int &, int &);
void FreeElement(Element *E);
void GenerateFaces();
@@ -1009,7 +1014,7 @@ public:
// Nodes are only active for higher order meshes, and share locations with
// the vertices, plus all the higher- order control points within the element
// and along the edges and on the faces.
void GetNode(int i, double *coord) const;
void GetNode(int i, double *coord);
void SetNode(int i, const double *coord);
// Node operations for curved mesh.
@@ -1117,7 +1122,6 @@ public:
///@{ @name NURBS mesh refinement methods
void KnotInsert(Array<KnotVector *> &kv);
void KnotInsert(Array<Vector *> &kv);
/* For each knot vector:
new_degree = max(old_degree, min(old_degree + rel_degree, degree)). */
void DegreeElevate(int rel_degree, int degree = 16);
@@ -1266,11 +1270,6 @@ public:
/// Destroys Mesh.
virtual ~Mesh() { DestroyPointers(); }
#ifdef MFEM_DEBUG
/// Output an NCMesh-compatible debug dump.
void DebugDump(std::ostream &out) const;
#endif
};
/** Overload operator<< for std::ostream and Mesh; valid also for the derived
@@ -1359,13 +1358,35 @@ public:
};
// shift cyclically 3 integers left-to-right
inline void ShiftRight(int &a, int &b, int &c)
// inline functions
inline void Mesh::ShiftL2R(int &a, int &b, int &c)
{
int t = a;
a = c; c = b; b = t;
}
inline void Mesh::Rotate3(int &a, int &b, int &c)
{
if (a < b)
{
if (a > c)
{
ShiftL2R(a, b, c);
}
}
else
{
if (b < c)
{
ShiftL2R(c, b, a);
}
else
{
ShiftL2R(a, b, c);
}
}
}
}
#endif
+517 -1318
View File
File diff suppressed because it is too large Load Diff
+60 -121
View File
@@ -15,7 +15,6 @@
#include "../config/config.hpp"
#include "../general/hash.hpp"
#include "../general/globals.hpp"
#include "../general/sort_pairs.hpp"
#include "../linalg/densemat.hpp"
#include "element.hpp"
#include "vertex.hpp"
@@ -60,23 +59,24 @@ struct Embedding
struct CoarseFineTransformations
{
/// Matrices for IsoparametricTransformation organized by Geometry::Type
DenseTensor point_matrices[Geometry::NumGeom];
std::map<Geometry::Type, DenseTensor> point_matrices;
/// Fine element positions in their parents.
Array<Embedding> embeddings;
const DenseTensor &GetPointMatrices(Geometry::Type geom) const;
void GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine,
Array<int> &coarse_to_ref_type,
Table &ref_type_to_matrix,
Array<Geometry::Type> &ref_type_to_geom) const;
void Clear();
bool IsInitialized() const;
void Clear() { point_matrices.clear(); embeddings.DeleteAll(); }
long MemoryUsage() const;
};
/** \brief A class for non-conforming AMR on higher-order hexahedral, prismatic,
/** \brief A class for non-conforming AMR on higher-order hexahedral,
* quadrilateral or triangular meshes.
*
* The class is used as follows:
@@ -152,13 +152,10 @@ public:
{
int index; ///< Mesh number
int element; ///< NCMesh::Element containing this vertex/edge/face
char local; ///< local number within 'element'
char geom; ///< Geometry::Type (faces only) (char storage to save RAM)
int local; ///< local number within 'element'
MeshId(int index = -1, int element = -1, char local = -1, char geom = -1)
: index(index), element(element), local(local), geom(geom) {}
Geometry::Type Geom() const { return Geometry::Type(geom); }
MeshId(int index = -1, int element = -1, int local = -1)
: index(index), element(element), local(local) {}
};
/** Nonconforming edge/face that has more than one neighbor. The neighbors
@@ -167,9 +164,8 @@ public:
{
int slaves_begin, slaves_end; ///< slave faces
Master(int index, int element, char local, char geom, int sb, int se)
: MeshId(index, element, local, geom)
, slaves_begin(sb), slaves_end(se) {}
Master(int index, int element, int local, int sb, int se)
: MeshId(index, element, local), slaves_begin(sb), slaves_end(se) {}
};
/// Nonconforming edge/face within a bigger edge/face.
@@ -179,9 +175,8 @@ public:
int edge_flags; ///< edge orientation flags
DenseMatrix point_matrix; ///< position within the master edge/face
Slave(int index, int element, char local, char geom)
: MeshId(index, element, local, geom)
, master(-1), edge_flags(0) {}
Slave(int index, int element, int local)
: MeshId(index, element, local), master(-1), edge_flags(0) {}
/// Return the point matrix oriented according to the master and slave edges
void OrientedPointMatrix(DenseMatrix &oriented_matrix) const;
@@ -291,11 +286,10 @@ public:
processor independent. TODO: this seems only partially true? */
int GetEdgeNCOrientation(const MeshId &edge_id) const;
/** Return Mesh vertex and edge indices of a face identified by 'face_id'.
The return value is the number of face vertices. */
int GetFaceVerticesEdges(const MeshId &face_id,
int vert_index[4], int edge_index[4],
int edge_orientation[4]) const;
/// Return Mesh vertex and edge indices of a face identified by 'face_id'.
void GetFaceVerticesEdges(const MeshId &face_id,
int vert_index[4], int edge_index[4],
int edge_orientation[4]) const;
/** Given an edge (by its vertex indices v1 and v2) return the first
(geometric) parent edge that exists in the Mesh or -1 if there is no such
@@ -311,29 +305,14 @@ public:
Array<int> &bdr_vertices,
Array<int> &bdr_edges);
/// Return element geometry type. @a index is the Mesh element number.
Geometry::Type GetElementGeometry(int index) const
{ return elements[leaf_elements[index]].Geom(); }
/// Return the type of elements in the mesh.
Geometry::Type GetElementGeometry() const { return elements[0].geom; }
/// Return face geometry type. @a index is the Mesh face number.
Geometry::Type GetFaceGeometry(int index) const
{ return Geometry::Type(face_geom[index]); }
/// Return the number of root elements.
int GetNumRootElements() { return root_state.Size(); }
Geometry::Type GetFaceGeometry() const { return Geometry::SQUARE; }
/// Return the distance of leaf 'i' from the root.
int GetElementDepth(int i) const;
/** Return the size reduction compared to the root element (ignoring local
stretching and curvature). */
int GetElementSizeReduction(int i) const;
/// Return the faces and face attributes of leaf element 'i'.
void GetElementFacesAttributes(int i, Array<int> &faces,
Array<int> &fattr) const;
/// I/O: Print the "vertex_parents" section of the mesh file (ver. >= 1.1).
void PrintVertexParents(std::ostream &out) const;
@@ -360,17 +339,15 @@ public:
void PrintStats(std::ostream &out = mfem::out) const;
typedef int64_t RefCoord;
protected: // interface for Mesh to be able to construct itself from NCMesh
friend class Mesh;
/// Return the basic Mesh arrays for the current finest level.
void GetMeshComponents(Array<mfem::Vertex> &mvertices,
Array<mfem::Element*> &melements,
Array<mfem::Element*> &mboundary) const;
void GetMeshComponents(Array<mfem::Vertex>& mvertices,
Array<mfem::Element*>& melements,
Array<mfem::Element*>& mboundary) const;
/** Get edge and face numbering from 'mesh' (i.e., set all Edge::index and
Face::index) after a new mesh was created from us. */
@@ -381,7 +358,6 @@ protected: // implementation
int Dim, spaceDim; ///< dimensions of the elements and the vertex coordinates
bool Iso; ///< true if the mesh only contains isotropic refinements
int Geoms; ///< bit mask of element geometries present, see InitGeomFlags()
/** A Node can hold a vertex, an edge, or both. Elements directly point to
their corner nodes, but edge nodes also exist and can be accessed using
@@ -435,7 +411,7 @@ protected: // implementation
to its vertex nodes. */
struct Element
{
char geom; ///< Geometry::Type of the element (char for storage only)
Geometry::Type geom; ///< Geometry::Type of the element
char ref_type; ///< bit mask of X,Y,Z refinements (bits 0,1,2 respectively)
char flag; ///< generic flag/marker, can be used by algorithms
int index; ///< element number in the Mesh, -1 if refined
@@ -449,8 +425,6 @@ protected: // implementation
int parent; ///< parent element, -1 if this is a root element, -2 if free
Element(Geometry::Type geom, int attr);
Geometry::Type Geom() const { return Geometry::Type(geom); }
};
// primary data
@@ -496,7 +470,6 @@ protected: // implementation
NCList vertex_list; ///< lazy-initialized list of vertices, see GetVertexList
Array<int> boundary_faces; ///< subset of all faces, set by BuildFaceList
Array<char> face_geom; ///< face geometry by face index, set by OnMeshUpdated
Table element_vertex; ///< leaf-element to vertex table, see FindSetNeighbors
@@ -518,15 +491,10 @@ protected: // implementation
virtual int GetNumGhostElements() const { return 0; }
virtual int GetNumGhostVertices() const { return 0; }
void InitGeomFlags();
bool HavePrisms() const { return Geoms & (1 << Geometry::PRISM); }
// refinement/derefinement
Array<Refinement> ref_stack; ///< stack of scheduled refinements (temporary)
HashTable<Node> shadow; ///< temporary storage for reparented nodes
Array<Triple<int, int, int> > reparents; ///< scheduled node reparents (tmp)
Table derefinements; ///< possible derefinements, see GetDerefinementTable
@@ -551,16 +519,13 @@ protected: // implementation
}
int NewHexahedron(int n0, int n1, int n2, int n3,
int n4, int n5, int n6, int n7, int attr,
int n4, int n5, int n6, int n7,
int attr,
int fattr0, int fattr1, int fattr2,
int fattr3, int fattr4, int fattr5);
int NewWedge(int n0, int n1, int n2,
int n3, int n4, int n5, int attr,
int fattr0, int fattr1,
int fattr2, int fattr3, int fattr4);
int NewQuadrilateral(int n0, int n1, int n2, int n3, int attr,
int NewQuadrilateral(int n0, int n1, int n2, int n3,
int attr,
int eattr0, int eattr1, int eattr2, int eattr3);
int NewTriangle(int n0, int n1, int n2,
@@ -568,62 +533,57 @@ protected: // implementation
mfem::Element* NewMeshElement(int geom) const;
int QuadFaceSplitType(int v1, int v2, int v3, int v4, int mid[5]
= NULL /*optional output of mid-edge nodes*/) const;
int GetMidEdgeNode(int vn1, int vn2);
int GetMidFaceNode(int en1, int en2, int en3, int en4);
bool TriFaceSplit(int v1, int v2, int v3, int mid[3] = NULL) const;
int FaceSplitType(int v1, int v2, int v3, int v4, int mid[4]
= NULL /*optional output of mid-edge nodes*/) const;
void ForceRefinement(int vn1, int vn2, int vn3, int vn4);
void FindEdgeElements(int vn1, int vn2, int vn3, int vn4,
Array<MeshId> &prisms) const;
void CheckAnisoPrism(int vn1, int vn2, int vn3, int vn4,
const Refinement *refs, int nref);
void CheckAnisoFace(int vn1, int vn2, int vn3, int vn4,
int mid12, int mid34, int level = 0);
void CheckIsoFace(int vn1, int vn2, int vn3, int vn4,
int en1, int en2, int en3, int en4, int midf);
void ReparentNode(int node, int new_p1, int new_p2);
int FindMidEdgeNode(int node1, int node2) const;
int GetMidEdgeNode(int node1, int node2);
int GetMidFaceNode(int en1, int en2, int en3, int en4);
void ReferenceElement(int elem);
void UnreferenceElement(int elem, Array<int> &elemFaces);
void RefElement(int elem);
void UnrefElement(int elem, Array<int> &elemFaces);
Face* GetFace(Element &elem, int face_no);
void RegisterFaces(int elem, int *fattr = NULL);
void DeleteUnusedFaces(const Array<int> &elemFaces);
int FindAltParents(int node1, int node2);
bool NodeSetX1(int node, int* n);
bool NodeSetX2(int node, int* n);
bool NodeSetY1(int node, int* n);
bool NodeSetY2(int node, int* n);
bool NodeSetZ1(int node, int* n);
bool NodeSetZ2(int node, int* n);
void CollectDerefinements(int elem, Array<Connection> &list);
/// Return el.node[index] correctly, even if the element is refined.
int RetrieveNode(const Element &el, int index);
/// Extended version of find_node: works if 'el' is refined.
int FindNodeExt(const Element &el, int node, bool abort = true);
/// Extended version of find_node: works if 'el' is refined; optional abort.
int FindNodeExt(const Element &el, int node, bool abort = false);
// face/edge lists
static int find_node(const Element &el, int node);
static int find_element_edge(const Element &el, int vn0, int vn1,
bool abort = true);
static int find_local_face(int geom, int a, int b, int c);
static int find_element_edge(const Element &el, int vn0, int vn1);
static int find_hex_face(int a, int b, int c);
int ReorderFacePointMat(int v0, int v1, int v2, int v3,
int elem, DenseMatrix& mat) const;
struct PointMatrix;
void TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
const PointMatrix& pm, int level, Face* eface[4]);
void TraverseTriFace(int vn0, int vn1, int vn2,
const PointMatrix& pm, int level);
void TraverseFace(int vn0, int vn1, int vn2, int vn3,
const PointMatrix& pm, int level);
void TraverseEdge(int vn0, int vn1, double t0, double t1, int flags,
int level);
@@ -666,9 +626,8 @@ protected: // implementation
void CollectEdgeVertices(int v0, int v1, Array<int> &indices);
void CollectTriFaceVertices(int v0, int v1, int v2, Array<int> &indices);
void CollectQuadFaceVertices(int v0, int v1, int v2, int v3,
Array<int> &indices);
void CollectFaceVertices(int v0, int v1, int v2, int v3,
Array<int> &indices);
void BuildElementToVertexTable();
void UpdateElementToVertexTable()
@@ -676,15 +635,6 @@ protected: // implementation
if (element_vertex.Size() < 0) { BuildElementToVertexTable(); }
}
int GetVertexRootCoord(int elem, RefCoord coord[3]) const;
void CollectIncidentElements(int elem, const RefCoord coord[3],
Array<int> &list) const;
/** Return elements neighboring to a local vertex of element 'elem'. Only
elements from within the same refinement tree ('cousins') are returned.
Complexity is proportional to the depth of elem's refinement tree. */
void FindVertexCousins(int elem, int local, Array<int> &cousins) const;
// coarse/fine transformations
@@ -740,13 +690,6 @@ protected: // implementation
PointMatrix(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
{ np = 4; points[0] = p0; points[1] = p1; points[2] = p2; points[3] = p3; }
PointMatrix(const Point& p0, const Point& p1, const Point& p2,
const Point& p3, const Point& p4, const Point& p5)
{
np = 6;
points[0] = p0; points[1] = p1; points[2] = p2;
points[3] = p3; points[4] = p4; points[5] = p5;
}
PointMatrix(const Point& p0, const Point& p1, const Point& p2,
const Point& p3, const Point& p4, const Point& p5,
const Point& p6, const Point& p7)
@@ -764,13 +707,11 @@ protected: // implementation
static PointMatrix pm_tri_identity;
static PointMatrix pm_quad_identity;
static PointMatrix pm_prism_identity;
static PointMatrix pm_hex_identity;
static const PointMatrix& GetGeomIdentity(Geometry::Type geom);
static const PointMatrix& GetGeomIdentity(int geom);
void GetPointMatrix(Geometry::Type geom, const char* ref_path,
DenseMatrix& matrix);
void GetPointMatrix(int geom, const char* ref_path, DenseMatrix& matrix);
typedef std::map<std::string, int> RefPathMap;
@@ -807,10 +748,9 @@ protected: // implementation
void FindFaceNodes(int face, int node[4]);
int EdgeSplitLevel(int vn1, int vn2) const;
int TriFaceSplitLevel(int vn1, int vn2, int vn3) const;
void QuadFaceSplitLevel(int vn1, int vn2, int vn3, int vn4,
int& h_level, int& v_level) const;
int EdgeSplitLevel(int vn1, int vn2) const;
void FaceSplitLevel(int vn1, int vn2, int vn3, int vn4,
int& h_level, int& v_level) const;
void CountSplits(int elem, int splits[3]) const;
void GetLimitRefinements(Array<Refinement> &refinements, int max_level);
@@ -825,10 +765,9 @@ protected: // implementation
(triangles, quads, cubes) */
struct GeomInfo
{
int nv, ne, nf; // number of: vertices, edges, faces
int edges[12][2]; // edge vertices (up to 12 edges)
int faces[6][4]; // face vertices (up to 6 faces)
int nfv[6]; // number of face vertices
int nv, ne, nf, nfv; // number of: vertices, edges, faces, face vertices
int edges[12][2]; // edge vertices (up to 12 edges)
int faces[6][4]; // face vertices (up to 6 faces)
bool initialized;
GeomInfo() : initialized(false) {}
@@ -837,7 +776,7 @@ protected: // implementation
static GeomInfo GI[Geometry::NumGeom];
static GeomInfo &gi_hex, &gi_wedge, &gi_quad, &gi_tri;
static GeomInfo &gi_hex, &gi_quad, &gi_tri;
#ifdef MFEM_DEBUG
public:
-469
View File
@@ -1,469 +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.
namespace mfem
{
static int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
// derefinement tables
static int quad_deref_table[3][4 + 4] =
{
{ 0, 1, 1, 0, /**/ 1, 1, 0, 0 }, // 1 - X
{ 0, 0, 1, 1, /**/ 0, 0, 1, 1 }, // 2 - Y
{ 0, 1, 2, 3, /**/ 1, 1, 3, 3 } // 3 - iso
};
static int hex_deref_table[7][8 + 6] =
{
{ 0, 1, 1, 0, 0, 1, 1, 0, /**/ 1, 1, 1, 0, 0, 0 }, // 1 - X
{ 0, 0, 1, 1, 0, 0, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 2 - Y
{ 0, 1, 2, 3, 0, 1, 2, 3, /**/ 1, 1, 1, 3, 3, 3 }, // 3 - XY
{ 0, 0, 0, 0, 1, 1, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 4 - Z
{ 0, 1, 1, 0, 3, 2, 2, 3, /**/ 1, 1, 1, 3, 3, 3 }, // 5 - XZ
{ 0, 0, 1, 1, 2, 2, 3, 3, /**/ 0, 0, 0, 3, 3, 3 }, // 6 - YZ
{ 0, 1, 2, 3, 4, 5, 6, 7, /**/ 1, 1, 1, 7, 7, 7 } // 7 - iso
};
static int prism_deref_table[7][6 + 5] =
{
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 1
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 2
{ 0, 1, 2, 0, 1, 2, /**/ 0, 0, 0, 1, 0 }, // 3 - XY
{ 0, 0, 0, 1, 1, 1, /**/ 0, 1, 0, 0, 0 }, // 4 - Z
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 5
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 6
{ 0, 1, 2, 4, 5, 6, /**/ 0, 5, 0, 5, 0 } // 7 - iso
};
// child ordering tables
static char quad_hilbert_child_order[8][4] =
{
{0,1,2,3}, {0,3,2,1}, {1,2,3,0}, {1,0,3,2},
{2,3,0,1}, {2,1,0,3}, {3,0,1,2}, {3,2,1,0}
};
static char quad_hilbert_child_state[8][4] =
{
{1,0,0,5}, {0,1,1,4}, {3,2,2,7}, {2,3,3,6},
{5,4,4,1}, {4,5,5,0}, {7,6,6,3}, {6,7,7,2}
};
static char hex_hilbert_child_order[24][8] =
{
{0,1,2,3,7,6,5,4}, {0,3,7,4,5,6,2,1}, {0,4,5,1,2,6,7,3},
{1,0,3,2,6,7,4,5}, {1,2,6,5,4,7,3,0}, {1,5,4,0,3,7,6,2},
{2,1,5,6,7,4,0,3}, {2,3,0,1,5,4,7,6}, {2,6,7,3,0,4,5,1},
{3,0,4,7,6,5,1,2}, {3,2,1,0,4,5,6,7}, {3,7,6,2,1,5,4,0},
{4,0,1,5,6,2,3,7}, {4,5,6,7,3,2,1,0}, {4,7,3,0,1,2,6,5},
{5,1,0,4,7,3,2,6}, {5,4,7,6,2,3,0,1}, {5,6,2,1,0,3,7,4},
{6,2,3,7,4,0,1,5}, {6,5,1,2,3,0,4,7}, {6,7,4,5,1,0,3,2},
{7,3,2,6,5,1,0,4}, {7,4,0,3,2,1,5,6}, {7,6,5,4,0,1,2,3}
};
static char hex_hilbert_child_state[24][8] =
{
{1,2,2,7,7,21,21,17}, {2,0,0,22,22,16,16,8}, {0,1,1,15,15,6,6,23},
{4,5,5,10,10,18,18,14}, {5,3,3,19,19,13,13,11}, {3,4,4,12,12,9,9,20},
{8,7,7,17,17,23,23,2}, {6,8,8,0,0,15,15,22}, {7,6,6,21,21,1,1,16},
{11,10,10,14,14,20,20,5}, {9,11,11,3,3,12,12,19}, {10,9,9,18,18,4,4,13},
{13,14,14,5,5,19,19,10}, {14,12,12,20,20,11,11,4}, {12,13,13,9,9,3,3,18},
{16,17,17,2,2,22,22,7}, {17,15,15,23,23,8,8,1}, {15,16,16,6,6,0,0,21},
{20,19,19,11,11,14,14,3}, {18,20,20,4,4,10,10,12}, {19,18,18,13,13,5,5,9},
{23,22,22,8,8,17,17,0}, {21,23,23,1,1,7,7,15}, {22,21,21,16,16,2,2,6}
};
// child/parent reference domain transforms
typedef NCMesh::RefCoord RefCoord;
// reference domain coordinates as fixed point numbers
const RefCoord T_HALF = (1ll << 59);
const RefCoord T_ONE = (1ll << 60);
const RefCoord T_TWO = (1ll << 61);
// (scaling factors have a different fixed point multiplier)
const RefCoord S_HALF = 1;
const RefCoord S_ONE = 2;
const RefCoord S_TWO = 4;
static RefCoord tri_corners[3][3] =
{
{ 0, 0, 0},
{T_ONE, 0, 0},
{ 0, T_ONE, 0}
};
static RefCoord quad_corners[4][3] =
{
{ 0, 0, 0},
{T_ONE, 0, 0},
{T_ONE, T_ONE, 0},
{ 0, T_ONE, 0}
};
static RefCoord hex_corners[8][3] =
{
{ 0, 0, 0},
{T_ONE, 0, 0},
{T_ONE, T_ONE, 0},
{ 0, T_ONE, 0},
{ 0, 0, T_ONE},
{T_ONE, 0, T_ONE},
{T_ONE, T_ONE, T_ONE},
{ 0, T_ONE, T_ONE}
};
static RefCoord prism_corners[6][3] =
{
{ 0, 0, 0},
{T_ONE, 0, 0},
{ 0, T_ONE, 0},
{ 0, 0, T_ONE},
{T_ONE, 0, T_ONE},
{ 0, T_ONE, T_ONE}
};
typedef RefCoord RefPoint[3];
static RefPoint* geom_corners[7] =
{
NULL, // point
NULL, // segment
tri_corners,
quad_corners,
NULL, // tetrahedron
hex_corners,
prism_corners
};
// reference domain transform: 3 scales, 3 translations
struct RefTrf
{
RefCoord s[3], t[3];
void Apply(const RefCoord src[3], RefCoord dst[3]) const;
};
static RefTrf quad_parent_rt1[2] =
{
{ {S_HALF, S_ONE, 0}, { 0, 0, 0} },
{ {S_HALF, S_ONE, 0}, {T_HALF, 0, 0} }
};
static RefTrf quad_child_rt1[2] =
{
{ {S_TWO, S_ONE, 0}, { 0, 0, 0} },
{ {S_TWO, S_ONE, 0}, {-T_ONE, 0, 0} }
};
static RefTrf quad_parent_rt2[2] =
{
{ {S_ONE, S_HALF, 0}, {0, 0, 0} },
{ {S_ONE, S_HALF, 0}, {0, T_HALF, 0} }
};
static RefTrf quad_child_rt2[2] =
{
{ {S_ONE, S_TWO, 0}, {0, 0, 0} },
{ {S_ONE, S_TWO, 0}, {0, -T_ONE, 0} }
};
static RefTrf quad_parent_rt3[4] =
{
{ {S_HALF, S_HALF, 0}, { 0, 0, 0} },
{ {S_HALF, S_HALF, 0}, {T_HALF, 0, 0} },
{ {S_HALF, S_HALF, 0}, {T_HALF, T_HALF, 0} },
{ {S_HALF, S_HALF, 0}, { 0, T_HALF, 0} }
};
static RefTrf quad_child_rt3[4] =
{
{ {S_TWO, S_TWO, 0}, { 0, 0, 0} },
{ {S_TWO, S_TWO, 0}, {-T_ONE, 0, 0} },
{ {S_TWO, S_TWO, 0}, {-T_ONE, -T_ONE, 0} },
{ {S_TWO, S_TWO, 0}, { 0, -T_ONE, 0} }
};
static RefTrf* quad_parent[4] =
{
NULL,
quad_parent_rt1,
quad_parent_rt2,
quad_parent_rt3
};
static RefTrf* quad_child[4] =
{
NULL,
quad_child_rt1,
quad_child_rt2,
quad_child_rt3
};
static RefTrf hex_parent_rt1[2] =
{
{ {S_HALF, S_ONE, S_ONE}, { 0, 0, 0} },
{ {S_HALF, S_ONE, S_ONE}, {T_HALF, 0, 0} }
};
static RefTrf hex_child_rt1[2] =
{
{ {S_TWO, S_ONE, S_ONE}, { 0, 0, 0} },
{ {S_TWO, S_ONE, S_ONE}, {-T_ONE, 0, 0} }
};
static RefTrf hex_parent_rt2[2] =
{
{ {S_ONE, S_HALF, S_ONE}, {0, 0, 0} },
{ {S_ONE, S_HALF, S_ONE}, {0, T_HALF, 0} }
};
static RefTrf hex_child_rt2[2] =
{
{ {S_ONE, S_TWO, S_ONE}, {0, 0, 0} },
{ {S_ONE, S_TWO, S_ONE}, {0, -T_ONE, 0} }
};
static RefTrf hex_parent_rt3[4] =
{
{ {S_HALF, S_HALF, S_ONE}, { 0, 0, 0} },
{ {S_HALF, S_HALF, S_ONE}, {T_HALF, 0, 0} },
{ {S_HALF, S_HALF, S_ONE}, {T_HALF, T_HALF, 0} },
{ {S_HALF, S_HALF, S_ONE}, { 0, T_HALF, 0} }
};
static RefTrf hex_child_rt3[4] =
{
{ {S_TWO, S_TWO, S_ONE}, { 0, 0, 0} },
{ {S_TWO, S_TWO, S_ONE}, {-T_ONE, 0, 0} },
{ {S_TWO, S_TWO, S_ONE}, {-T_ONE, -T_ONE, 0} },
{ {S_TWO, S_TWO, S_ONE}, { 0, -T_ONE, 0} }
};
static RefTrf hex_parent_rt4[2] =
{
{ {S_ONE, S_ONE, S_HALF}, {0, 0, 0} },
{ {S_ONE, S_ONE, S_HALF}, {0, 0, T_HALF} }
};
static RefTrf hex_child_rt4[2] =
{
{ {S_ONE, S_ONE, S_TWO}, {0, 0, 0} },
{ {S_ONE, S_ONE, S_TWO}, {0, 0, -T_ONE} }
};
static RefTrf hex_parent_rt5[4] =
{
{ {S_HALF, S_ONE, S_HALF}, { 0, 0, 0} },
{ {S_HALF, S_ONE, S_HALF}, {T_HALF, 0, 0} },
{ {S_HALF, S_ONE, S_HALF}, {T_HALF, 0, T_HALF} },
{ {S_HALF, S_ONE, S_HALF}, { 0, 0, T_HALF} }
};
static RefTrf hex_child_rt5[4] =
{
{ {S_TWO, S_ONE, S_TWO}, { 0, 0, 0} },
{ {S_TWO, S_ONE, S_TWO}, {-T_ONE, 0, 0} },
{ {S_TWO, S_ONE, S_TWO}, {-T_ONE, 0, -T_ONE} },
{ {S_TWO, S_ONE, S_TWO}, { 0, 0, -T_ONE} }
};
static RefTrf hex_parent_rt6[4] =
{
{ {S_ONE, S_HALF, S_HALF}, {0, 0, 0} },
{ {S_ONE, S_HALF, S_HALF}, {0, T_HALF, 0} },
{ {S_ONE, S_HALF, S_HALF}, {0, 0, T_HALF} },
{ {S_ONE, S_HALF, S_HALF}, {0, T_HALF, T_HALF} }
};
static RefTrf hex_child_rt6[4] =
{
{ {S_ONE, S_TWO, S_TWO}, {0, 0, 0} },
{ {S_ONE, S_TWO, S_TWO}, {0, -T_ONE, 0} },
{ {S_ONE, S_TWO, S_TWO}, {0, 0, -T_ONE} },
{ {S_ONE, S_TWO, S_TWO}, {0, -T_ONE, -T_ONE} }
};
static RefTrf hex_parent_rt7[8] =
{
{ {S_HALF, S_HALF, S_HALF}, { 0, 0, 0} },
{ {S_HALF, S_HALF, S_HALF}, {T_HALF, 0, 0} },
{ {S_HALF, S_HALF, S_HALF}, {T_HALF, T_HALF, 0} },
{ {S_HALF, S_HALF, S_HALF}, { 0, T_HALF, 0} },
{ {S_HALF, S_HALF, S_HALF}, { 0, 0, T_HALF} },
{ {S_HALF, S_HALF, S_HALF}, {T_HALF, 0, T_HALF} },
{ {S_HALF, S_HALF, S_HALF}, {T_HALF, T_HALF, T_HALF} },
{ {S_HALF, S_HALF, S_HALF}, { 0, T_HALF, T_HALF} }
};
static RefTrf hex_child_rt7[8] =
{
{ {S_TWO, S_TWO, S_TWO}, { 0, 0, 0} },
{ {S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, 0} },
{ {S_TWO, S_TWO, S_TWO}, {-T_ONE, -T_ONE, 0} },
{ {S_TWO, S_TWO, S_TWO}, { 0, -T_ONE, 0} },
{ {S_TWO, S_TWO, S_TWO}, { 0, 0, -T_ONE} },
{ {S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, -T_ONE} },
{ {S_TWO, S_TWO, S_TWO}, {-T_ONE, -T_ONE, -T_ONE} },
{ {S_TWO, S_TWO, S_TWO}, { 0, -T_ONE, -T_ONE} }
};
static RefTrf* hex_parent[8] =
{
NULL,
hex_parent_rt1,
hex_parent_rt2,
hex_parent_rt3,
hex_parent_rt4,
hex_parent_rt5,
hex_parent_rt6,
hex_parent_rt7
};
static RefTrf* hex_child[8] =
{
NULL,
hex_child_rt1,
hex_child_rt2,
hex_child_rt3,
hex_child_rt4,
hex_child_rt5,
hex_child_rt6,
hex_child_rt7
};
static RefTrf tri_parent_rt3[4] =
{
{ { S_HALF, S_HALF, 0}, { 0, 0, 0} },
{ { S_HALF, S_HALF, 0}, {T_HALF, 0, 0} },
{ { S_HALF, S_HALF, 0}, { 0, T_HALF, 0} },
{ {-S_HALF, -S_HALF, 0}, {T_HALF, T_HALF, 0} }
};
static RefTrf tri_child_rt3[4] =
{
{ { S_TWO, S_TWO, 0}, { 0, 0, 0} },
{ { S_TWO, S_TWO, 0}, {-T_ONE, 0, 0} },
{ { S_TWO, S_TWO, 0}, { 0, -T_ONE, 0} },
{ {-S_TWO, -S_TWO, 0}, { T_ONE, T_ONE, 0} }
};
static RefTrf* tri_parent[4] =
{
NULL, NULL, NULL,
tri_parent_rt3
};
static RefTrf* tri_child[4] =
{
NULL, NULL, NULL,
tri_child_rt3
};
static RefTrf prism_parent_rt3[4] =
{
{ { S_HALF, S_HALF, S_ONE}, { 0, 0, 0} },
{ { S_HALF, S_HALF, S_ONE}, {T_HALF, 0, 0} },
{ { S_HALF, S_HALF, S_ONE}, { 0, T_HALF, 0} },
{ {-S_HALF, -S_HALF, S_ONE}, {T_HALF, T_HALF, 0} }
};
static RefTrf prism_child_rt3[4] =
{
{ { S_TWO, S_TWO, S_ONE}, { 0, 0, 0} },
{ { S_TWO, S_TWO, S_ONE}, {-T_ONE, 0, 0} },
{ { S_TWO, S_TWO, S_ONE}, { 0, -T_ONE, 0} },
{ {-S_TWO, -S_TWO, S_ONE}, { T_ONE, T_ONE, 0} }
};
static RefTrf prism_parent_rt4[2] =
{
{ {S_ONE, S_ONE, S_HALF}, {0, 0, 0} },
{ {S_ONE, S_ONE, S_HALF}, {0, 0, T_HALF} }
};
static RefTrf prism_child_rt4[2] =
{
{ {S_ONE, S_ONE, S_TWO}, {0, 0, 0} },
{ {S_ONE, S_ONE, S_TWO}, {0, 0, -T_ONE} }
};
static RefTrf prism_parent_rt7[8] =
{
{ { S_HALF, S_HALF, S_HALF}, { 0, 0, 0} },
{ { S_HALF, S_HALF, S_HALF}, {T_HALF, 0, 0} },
{ { S_HALF, S_HALF, S_HALF}, { 0, T_HALF, 0} },
{ {-S_HALF, -S_HALF, S_HALF}, {T_HALF, T_HALF, 0} },
{ { S_HALF, S_HALF, S_HALF}, { 0, 0, T_HALF} },
{ { S_HALF, S_HALF, S_HALF}, {T_HALF, 0, T_HALF} },
{ { S_HALF, S_HALF, S_HALF}, { 0, T_HALF, T_HALF} },
{ {-S_HALF, -S_HALF, S_HALF}, {T_HALF, T_HALF, T_HALF} }
};
static RefTrf prism_child_rt7[8] =
{
{ { S_TWO, S_TWO, S_TWO}, { 0, 0, 0} },
{ { S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, 0} },
{ { S_TWO, S_TWO, S_TWO}, { 0, -T_ONE, 0} },
{ {-S_TWO, -S_TWO, S_TWO}, { T_ONE, T_ONE, 0} },
{ { S_TWO, S_TWO, S_TWO}, { 0, 0, -T_ONE} },
{ { S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, -T_ONE} },
{ { S_TWO, S_TWO, S_TWO}, { 0, -T_ONE, -T_ONE} },
{ {-S_TWO, -S_TWO, S_TWO}, { T_ONE, T_ONE, -T_ONE} }
};
static RefTrf* prism_parent[8] =
{
NULL, NULL, NULL,
prism_parent_rt3,
prism_parent_rt4,
NULL, NULL,
prism_parent_rt7
};
static RefTrf* prism_child[8] =
{
NULL, NULL, NULL,
prism_child_rt3,
prism_child_rt4,
NULL, NULL,
prism_child_rt7
};
static RefTrf** geom_parent[7] =
{
NULL,
NULL,
tri_parent,
quad_parent,
NULL,
hex_parent,
prism_parent
};
static RefTrf** geom_child[7] =
{
NULL,
NULL,
tri_child,
quad_child,
NULL,
hex_child,
prism_child
};
} // namespace mfem
-38
View File
@@ -582,19 +582,9 @@ void NURBSPatch::KnotInsert(int dir, const KnotVector &newkv)
}
}
void NURBSPatch::KnotInsert(Array<Vector *> &newkv)
{
for (int dir = 0; dir < kv.Size(); dir++)
{
KnotInsert(dir, *newkv[dir]);
}
}
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
void NURBSPatch::KnotInsert(int dir, const Vector &knot)
{
if (knot.Size() == 0 ) { return; }
if (dir >= kv.Size() || dir < 0)
{
mfem_error("NURBSPatch::KnotInsert : Incorrect direction!");
@@ -2992,34 +2982,6 @@ void NURBSExtension::KnotInsert(Array<KnotVector *> &kv)
}
}
void NURBSExtension::KnotInsert(Array<Vector *> &kv)
{
Array<int> edges;
Array<int> orient;
Array<Vector *> pkv(Dimension());
for (int p = 0; p < patches.Size(); p++)
{
patchTopo->GetElementEdges(p, edges, orient);
if (Dimension()==2)
{
pkv[0] = kv[KnotInd(edges[0])];
pkv[1] = kv[KnotInd(edges[1])];
}
else
{
pkv[0] = kv[KnotInd(edges[0])];
pkv[1] = kv[KnotInd(edges[3])];
pkv[2] = kv[KnotInd(edges[8])];
}
patches[p]->KnotInsert(pkv);
}
}
void NURBSExtension::GetPatchNets(const Vector &coords, int vdim)
{
if (Dimension() == 2)
-3
View File
@@ -119,9 +119,7 @@ public:
void KnotInsert (int dir, const KnotVector &knot);
void KnotInsert (int dir, const Vector &knot);
void KnotInsert(Array<Vector *> &knot);
void KnotInsert(Array<KnotVector *> &knot);
void DegreeElevate(int t);
void UniformRefinement();
@@ -391,7 +389,6 @@ public:
void DegreeElevate(int rel_degree, int degree = 16);
void UniformRefinement();
void KnotInsert(Array<KnotVector *> &kv);
void KnotInsert(Array<Vector *> &kv);
};
+14 -185
View File
@@ -2399,32 +2399,6 @@ int ParMesh::GetSharedFace(int sface) const
}
}
// shift cyclically 3 integers a, b, c, so that the smallest of
// order[a], order[b], order[c] is first
static inline
void Rotate3Indirect(int &a, int &b, int &c,
const Array<std::int64_t> &order)
{
if (order[a] < order[b])
{
if (order[a] > order[c])
{
ShiftRight(a, b, c);
}
}
else
{
if (order[b] < order[c])
{
ShiftRight(c, b, a);
}
else
{
ShiftRight(a, b, c);
}
}
}
void ParMesh::ReorientTetMesh()
{
if (Dim != 3 || !(meshgen & 1))
@@ -2432,109 +2406,7 @@ void ParMesh::ReorientTetMesh()
return;
}
DeleteLazyTables();
DSTable *old_v_to_v = NULL;
Table *old_elem_vert = NULL;
if (Nodes)
{
PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
}
// create a GroupCommunicator over shared vertices
GroupCommunicator svert_comm(gtopo);
{
// initialize svert_comm
Table &gr_svert = svert_comm.GroupLDofTable();
// gr_svert differs from group_svert - the latter does not store gr. 0
gr_svert.SetDims(GetNGroups(), svert_lvert.Size());
gr_svert.GetI()[0] = 0;
for (int gr = 1; gr <= GetNGroups(); gr++)
{
gr_svert.GetI()[gr] = group_svert.GetI()[gr-1];
}
for (int k = 0; k < svert_lvert.Size(); k++)
{
gr_svert.GetJ()[k] = group_svert.GetJ()[k];
}
svert_comm.Finalize();
}
// communicate the local index of each shared vertex from the group master to
// other ranks in the group
Array<int> svert_master_rank(svert_lvert.Size());
Array<int> svert_master_index(svert_lvert);
{
for (int i = 0; i < group_svert.Size(); i++)
{
int rank = gtopo.GetGroupMasterRank(i+1);
for (int j = 0; j < group_svert.RowSize(i); j++)
{
svert_master_rank[group_svert.GetRow(i)[j]] = rank;
}
}
svert_comm.Bcast(svert_master_index);
}
// the pairs (master rank, master local index) define a globally consistent
// vertex ordering
Array<std::int64_t> glob_vert_order(vertices.Size());
{
Array<int> lvert_svert(vertices.Size());
lvert_svert = -1;
for (int i = 0; i < svert_lvert.Size(); i++)
{
lvert_svert[svert_lvert[i]] = i;
}
for (int i = 0; i < vertices.Size(); i++)
{
int s = lvert_svert[i];
if (s >= 0)
{
glob_vert_order[i] =
(std::int64_t(svert_master_rank[s]) << 32) + svert_master_index[s];
}
else
{
glob_vert_order[i] = (std::int64_t(MyRank) << 32) + i;
}
}
}
// rotate tetrahedra so that vertex zero is the lowest (global) index vertex,
// vertex 1 is the second lowest (global) index and vertices 2 and 3 preserve
// positive orientation of the element
for (int i = 0; i < NumOfElements; i++)
{
if (GetElementType(i) == Element::TETRAHEDRON)
{
int *v = elements[i]->GetVertices();
Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
if (glob_vert_order[v[0]] < glob_vert_order[v[3]])
{
Rotate3Indirect(v[1], v[2], v[3], glob_vert_order);
}
else
{
ShiftRight(v[0], v[1], v[3]);
}
}
}
// rotate also boundary triangles
for (int i = 0; i < NumOfBdrElements; i++)
{
if (GetBdrElementType(i) == Element::TRIANGLE)
{
int *v = boundary[i]->GetVertices();
Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
}
}
Mesh::ReorientTetMesh();
const bool check_consistency = true;
if (check_consistency)
@@ -2561,56 +2433,37 @@ void ParMesh::ReorientTetMesh()
for (int i = 0; i < stria_flag.Size(); i++)
{
const int *v = shared_trias[i].v;
if (glob_vert_order[v[0]] < glob_vert_order[v[1]])
if (v[0] < v[1])
{
stria_flag[i] = (glob_vert_order[v[0]] < glob_vert_order[v[2]]) ? 0 : 2;
stria_flag[i] = (v[0] < v[2]) ? 0 : 2;
}
else // v[1] < v[0]
{
stria_flag[i] = (glob_vert_order[v[1]] < glob_vert_order[v[2]]) ? 1 : 2;
stria_flag[i] = (v[1] < v[2]) ? 1 : 2;
}
}
Array<int> stria_master_flag(stria_flag);
stria_comm.Bcast(stria_master_flag);
for (int i = 0; i < stria_flag.Size(); i++)
{
const int *v = shared_trias[i].v;
MFEM_VERIFY(stria_flag[i] == stria_master_flag[i],
"inconsistent vertex ordering found, shared triangle "
<< i << ": ("
<< v[0] << ", " << v[1] << ", " << v[2] << "), "
<< "local flag: " << stria_flag[i]
<< ", master flag: " << stria_master_flag[i]);
"inconsistent vertex ordering found");
}
}
// rotate shared triangle faces
// Rotate shared triangle faces.
// Note that no communication is needed to ensure that the shared
// faces are rotated in the same way in both processors. This is
// automatic due to various things, e.g. the global to local vertex
// mapping preserves the global order; also the way new vertices
// are introduced during refinement is essential.
for (int i = 0; i < shared_trias.Size(); i++)
{
int *v = shared_trias[i].v;
Rotate3Indirect(v[0], v[1], v[2], glob_vert_order);
Rotate3(v[0], v[1], v[2]);
}
// finalize
if (!Nodes)
{
GetElementToFaceTable();
GenerateFaces();
if (el_to_edge)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
}
else
{
DoNodeReorder(old_v_to_v, old_elem_vert);
delete old_elem_vert;
delete old_v_to_v;
}
// the local edge and face numbering is changed therefore we need to
// The local edge and face numbering is changed therefore we need to
// update sedge_ledge and sface_lface.
FinalizeParTopo();
}
@@ -3202,9 +3055,6 @@ bool ParMesh::NonconformingDerefinement(Array<double> &elem_error,
long glob_size = ReduceInt(derefs.Size());
if (!glob_size) { return false; }
// Destroy face-neighbor data only when actually de-refining.
DeleteFaceNbrData();
pncmesh->Derefine(derefs);
ParMesh* mesh2 = new ParMesh(*pncmesh);
@@ -3232,18 +3082,7 @@ bool ParMesh::NonconformingDerefinement(Array<double> &elem_error,
return true;
}
void ParMesh::Rebalance()
{
RebalanceImpl(NULL); // default SFC-based partition
}
void ParMesh::Rebalance(const Array<int> &partition)
{
RebalanceImpl(&partition);
}
void ParMesh::RebalanceImpl(const Array<int> *partition)
{
if (Conforming())
{
@@ -3270,7 +3109,7 @@ void ParMesh::RebalanceImpl(const Array<int> *partition)
DeleteFaceNbrData();
pncmesh->Rebalance(partition);
pncmesh->Rebalance();
ParMesh* pmesh2 = new ParMesh(*pncmesh);
pncmesh->OnMeshUpdated(pmesh2);
@@ -4171,7 +4010,6 @@ void ParMesh::PrintAsOne(std::ostream &out)
"# SQUARE = 3\n"
"# TETRAHEDRON = 4\n"
"# CUBE = 5\n"
"# PRISM = 6\n"
"#\n";
out << "\ndimension\n" << Dim;
@@ -4270,15 +4108,6 @@ void ParMesh::PrintAsOne(std::ostream &out)
{
switch (Dim)
{
case 1:
for (i = 0; i < svert_lvert.Size(); i++)
{
ints.Append(Geometry::POINT);
ints.Append(svert_lvert[i]);
ne++;
}
break;
case 2:
for (i = 0; i < shared_edges.Size(); i++)
{
+1 -10
View File
@@ -141,9 +141,6 @@ protected:
virtual bool NonconformingDerefinement(Array<double> &elem_error,
double threshold, int nc_limit = 0,
int op = 1);
void RebalanceImpl(const Array<int> *partition);
void DeleteFaceNbrData();
bool WantSkipSharedMaster(const NCMesh::Master &master) const;
@@ -289,15 +286,9 @@ public:
/// Utility function: sum integers from all processors (Allreduce).
virtual long ReduceInt(int value) const;
/** Load balance the mesh by equipartitioning the global space-filling
sequence of elements. Works for nonconforming meshes only. */
/// Load balance the mesh. NC meshes only.
void Rebalance();
/** Load balance a nonconforming mesh using a user-defined partition.
Each local element 'i' is migrated to processor rank 'partition[i]',
for 0 <= i < GetNE(). */
void Rebalance(const Array<int> &partition);
/** Print the part of the mesh in the calling processor adding the interface
as boundary (for visualization purposes) using the mfem v1.0 format. */
virtual void Print(std::ostream &out = mfem::out) const;
+123 -272
View File
@@ -156,7 +156,7 @@ void ParNCMesh::UpdateVertices()
Element &el = elements[leaf_elements[i]];
if (el.rank == MyRank)
{
for (int j = 0; j < GI[el.Geom()].nv; j++)
for (int j = 0; j < GI[(int) el.geom].nv; j++)
{
int &vindex = nodes[el.node[j]].vert_index;
if (vindex < 0) { vindex = NVertices++; }
@@ -227,30 +227,6 @@ void ParNCMesh::OnMeshUpdated(Mesh *mesh)
MFEM_ASSERT(NFaces == NEdges, "");
MFEM_ASSERT(NGhostFaces == NGhostEdges, "");
}
// update face_geom for ghost faces
face_geom.SetSize(NFaces + NGhostFaces, Geometry::SQUARE);
for (int i = 0; i < NGhostElements; i++)
{
Element &el = elements[leaf_elements[NElements + i]]; // ghost element
GeomInfo &gi = GI[el.Geom()];
for (int j = 0; j < gi.nf; j++)
{
const int *fv = gi.faces[j];
Face* face = faces.Find(el.node[fv[0]], el.node[fv[1]],
el.node[fv[2]], el.node[fv[3]]);
MFEM_ASSERT(face, "face not found!");
static const Geometry::Type types[5] =
{
Geometry::INVALID, Geometry::INVALID,
Geometry::SEGMENT, Geometry::TRIANGLE, Geometry::SQUARE
};
face_geom[face->index] = types[gi.nfv[j]];
}
}
}
void ParNCMesh::ElementSharesFace(int elem, int local, int face)
@@ -281,9 +257,6 @@ void ParNCMesh::BuildFaceList()
// This is an extension of NCMesh::BuildFaceList() which also determines
// face ownership and prepares face processor groups.
// (special case for prisms: to be able to handle edge-face constraints)
if (HavePrisms()) { GetEdgeList(); }
int nfaces = NFaces + NGhostFaces;
tmp_owner.SetSize(nfaces);
@@ -443,25 +416,13 @@ void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
for (unsigned i = 0; i < list.masters.size(); i++)
{
const Master &master = list.masters[i];
char &master_flag = tmp_shared_flag[master.index];
char master_old_flag = master_flag;
char master_old_flag = tmp_shared_flag[master.index];
for (int j = master.slaves_begin; j < master.slaves_end; j++)
{
int si = list.slaves[j].index;
if (si >= 0)
{
char &slave_flag = tmp_shared_flag[si];
master_flag |= slave_flag;
slave_flag |= master_old_flag;
}
else // special case: prism edge-face constraint
{
if (entity_owner[1][-1-si] != MyRank)
{
master_flag |= 0x2;
}
}
char &slave_flag = tmp_shared_flag[list.slaves[j].index];
tmp_shared_flag[master.index] |= slave_flag;
slave_flag |= master_old_flag;
}
}
@@ -483,8 +444,7 @@ void ParNCMesh::MakeSharedList(const NCList &list, NCList &shared)
}
for (unsigned i = 0; i < list.slaves.size(); i++)
{
int si = list.slaves[i].index;
if (si >= 0 && tmp_shared_flag[si] == 0x3)
if (tmp_shared_flag[list.slaves[i].index] == 0x3)
{
shared.slaves.push_back(list.slaves[i]);
}
@@ -634,9 +594,7 @@ void ParNCMesh::CalculatePMatrixGroups()
ranks.SetSize(0);
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
{
int si = face_list.slaves[j].index;
int owner = (si >= 0) ? entity_owner[2][si] // standard face dependency
/* */ : entity_owner[1][-1 - si]; // prism edge-face dep
int owner = entity_owner[2][face_list.slaves[j].index];
ranks.Append(groups[owner][0]);
}
ranks.Sort();
@@ -644,8 +602,8 @@ void ParNCMesh::CalculatePMatrixGroups()
AddConnections(2, master_face.index, ranks);
int nfv = GetFaceVerticesEdges(master_face, v, e, eo);
for (int j = 0; j < nfv; j++)
GetFaceVerticesEdges(master_face, v, e, eo);
for (int j = 0; j < 4; j++)
{
AddConnections(0, v[j], ranks);
AddConnections(1, e[j], ranks);
@@ -677,23 +635,20 @@ int ParNCMesh::get_face_orientation(Face &face, Element &e1, Element &e2,
{
// get local face number (remember that p1, p2, p3 are not in order, and
// p4 is not stored)
int lf = find_local_face(e[i]->Geom(),
find_node(*e[i], face.p1),
find_node(*e[i], face.p2),
find_node(*e[i], face.p3));
int lf = find_hex_face(find_node(*e[i], face.p1),
find_node(*e[i], face.p2),
find_node(*e[i], face.p3));
// optional output
if (local) { local[i] = lf; }
// get node IDs for the face as seen from e[i]
const int* fv = GI[e[i]->Geom()].faces[lf];
const int* fv = GI[Geometry::CUBE].faces[lf];
for (int j = 0; j < 4; j++)
{
ids[i][j] = e[i]->node[fv[j]];
}
}
return (ids[0][3] >= 0) ? Mesh::GetQuadOrientation(ids[0], ids[1])
/* */ : Mesh::GetTriOrientation(ids[0], ids[1]);
return Mesh::GetQuadOrientation(ids[0], ids[1]);
}
void ParNCMesh::CalcFaceOrientations()
@@ -838,8 +793,6 @@ void ParNCMesh::NeighborProcessors(Array<int> &neighbors)
{
UpdateLayers();
// TODO: look at groups instead?
std::set<int> ranks;
for (int i = 0; i < ghost_layer.Size(); i++)
{
@@ -851,9 +804,30 @@ void ParNCMesh::NeighborProcessors(Array<int> &neighbors)
//// ParMesh compatibility /////////////////////////////////////////////////////
struct CompareShared // TODO: use lambda when C++11 available
{
const Array<int> &elem_local, &leaf_glob_order, &shared_local;
CompareShared
(const Array<int> &el, const Array<int> &lgo, const Array<int> &sl)
: elem_local(el), leaf_glob_order(lgo), shared_local(sl) {}
inline bool operator()(const int a, const int b)
{
int el_loc_a = elem_local[shared_local[a]];
int el_loc_b = elem_local[shared_local[b]];
int lgo_a = leaf_glob_order[el_loc_a >> 4];
int lgo_b = leaf_glob_order[el_loc_b >> 4];
if (lgo_a != lgo_b) { return lgo_a < lgo_b; }
return (el_loc_a & 0xf) < (el_loc_b & 0xf);
}
};
void ParNCMesh::MakeSharedTable(int ngroups, int ent, Array<int> &shared_local,
Table &group_shared, Array<char> *entity_geom,
char geom)
Table &group_shared)
{
const Array<GroupId> &conf_group = entity_conf_group[ent];
@@ -865,8 +839,6 @@ void ParNCMesh::MakeSharedTable(int ngroups, int ent, Array<int> &shared_local,
{
if (conf_group[i])
{
if (entity_geom && (*entity_geom)[i] != geom) { continue; }
num_shared++;
group_shared.AddAColumnInRow(conf_group[i]-1);
}
@@ -880,8 +852,6 @@ void ParNCMesh::MakeSharedTable(int ngroups, int ent, Array<int> &shared_local,
{
if (conf_group[i])
{
if (entity_geom && (*entity_geom)[i] != geom) { continue; }
shared_local[j] = i;
group_shared.AddConnection(conf_group[i]-1, j);
j++;
@@ -896,18 +866,8 @@ void ParNCMesh::MakeSharedTable(int ngroups, int ent, Array<int> &shared_local,
int *row = group_shared.GetRow(i);
Array<int> ref_row(row, size);
ref_row.Sort([&](const int a, const int b)
{
int el_loc_a = entity_elem_local[ent][shared_local[a]];
int el_loc_b = entity_elem_local[ent][shared_local[b]];
int lgo_a = leaf_glob_order[el_loc_a >> 4];
int lgo_b = leaf_glob_order[el_loc_b >> 4];
if (lgo_a != lgo_b) { return lgo_a < lgo_b; }
return (el_loc_a & 0xf) < (el_loc_b & 0xf);
});
ref_row.Sort(
CompareShared(entity_elem_local[ent], leaf_glob_order, shared_local));
}
}
@@ -953,16 +913,15 @@ void ParNCMesh::GetConformingSharedStructures(ParMesh &pmesh)
}
// create shared to local index mappings and group tables
int ng = pmesh.gtopo.NGroups();
MakeSharedTable(ng, 0, pmesh.svert_lvert, pmesh.group_svert);
MakeSharedTable(ng, 1, pmesh.sedge_ledge, pmesh.group_sedge);
int ngroups = pmesh.gtopo.NGroups();
MakeSharedTable(ngroups, 0, pmesh.svert_lvert, pmesh.group_svert);
MakeSharedTable(ngroups, 1, pmesh.sedge_ledge, pmesh.group_sedge);
MakeSharedTable(ngroups, 2, pmesh.sface_lface, pmesh.group_squad);
Array<int> slt, slq;
MakeSharedTable(ng, 2, slt, pmesh.group_stria, &face_geom, Geometry::TRIANGLE);
MakeSharedTable(ng, 2, slq, pmesh.group_squad, &face_geom, Geometry::SQUARE);
pmesh.sface_lface = slt;
pmesh.sface_lface.Append(slq);
// create an empty group_stria (we currently don't have triangle faces)
pmesh.group_stria.MakeI(ngroups-1);
pmesh.group_stria.MakeJ();
pmesh.group_stria.ShiftUpI();
// create shared_edges
for (int i = 0; i < pmesh.shared_edges.Size(); i++)
@@ -980,23 +939,11 @@ void ParNCMesh::GetConformingSharedStructures(ParMesh &pmesh)
pmesh.shared_edges[i] = new Segment(v, 1);
}
// create shared_trias
pmesh.shared_trias.SetSize(slt.Size());
for (int i = 0; i < slt.Size(); i++)
// create shared_faces
pmesh.shared_quads.SetSize(pmesh.sface_lface.Size());
for (int i = 0; i < pmesh.shared_quads.Size(); i++)
{
int el_loc = entity_elem_local[2][slt[i]];
MeshId face_id(-1, leaf_elements[(el_loc >> 4)], (el_loc & 0xf));
int v[4], e[4], eo[4];
GetFaceVerticesEdges(face_id, v, e, eo);
pmesh.shared_trias[i].Set(v);
}
// create shared_quads
pmesh.shared_quads.SetSize(slq.Size());
for (int i = 0; i < slq.Size(); i++)
{
int el_loc = entity_elem_local[2][slq[i]];
int el_loc = entity_elem_local[2][pmesh.sface_lface[i]];
MeshId face_id(-1, leaf_elements[(el_loc >> 4)], (el_loc & 0xf));
int e[4], eo[4];
@@ -1012,6 +959,12 @@ void ParNCMesh::GetConformingSharedStructures(ParMesh &pmesh)
leaf_glob_order.DeleteAll();
}
bool ParNCMesh::compare_ranks_indices(const Element* a, const Element* b)
{
return (a->rank != b->rank) ? a->rank < b->rank
/* */ : a->index < b->index;
}
void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
{
ClearAuxPM();
@@ -1050,7 +1003,6 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
{
const Slave &sf = full_list.slaves[j];
if (sf.index < 0) { continue; }
MFEM_ASSERT(mf.element >= 0 && sf.element >= 0, "");
Element* e[2] = { &elements[mf.element], &elements[sf.element] };
@@ -1072,11 +1024,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
// same on different processors, this is important for ExchangeFaceNbrData)
fnbr.Sort();
fnbr.Unique();
fnbr.Sort([](const Element* a, const Element* b)
{
return (a->rank != b->rank) ? a->rank < b->rank
/* */ : a->index < b->index;
});
fnbr.Sort(compare_ranks_indices);
// put the ranks into 'face_nbr_group'
for (int i = 0; i < fnbr.Size(); i++)
@@ -1208,7 +1156,6 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
{
const Slave &sf = full_list.slaves[j];
if (sf.index < 0) { continue; }
MFEM_ASSERT(sf.element >= 0 && mf.element >= 0, "");
Element &sfe = elements[sf.element];
@@ -1356,18 +1303,13 @@ void ParNCMesh::Prune()
{
if (PruneTree(i)) { DerefineElement(i); }
}
Update();
}
void ParNCMesh::Refine(const Array<Refinement> &refinements)
{
if (NRanks == 1)
{
NCMesh::Refine(refinements);
return;
}
for (int i = 0; i < refinements.Size(); i++)
{
const Refinement &ref = refinements[i];
@@ -1766,7 +1708,7 @@ void ParNCMesh::CheckDerefinementNCLevel(const Table &deref_table,
//// Rebalance /////////////////////////////////////////////////////////////////
void ParNCMesh::Rebalance(const Array<int> *custom_partition)
void ParNCMesh::Rebalance()
{
send_rebalance_dofs.clear();
recv_rebalance_dofs.clear();
@@ -1774,46 +1716,30 @@ void ParNCMesh::Rebalance(const Array<int> *custom_partition)
Array<int> old_elements;
leaf_elements.GetSubArray(0, NElements, old_elements);
if (!custom_partition) // SFC based partitioning
// figure out new assignments for Element::rank
long local_elems = NElements, total_elems = 0;
MPI_Allreduce(&local_elems, &total_elems, 1, MPI_LONG, MPI_SUM, MyComm);
long first_elem_global = 0;
MPI_Scan(&local_elems, &first_elem_global, 1, MPI_LONG, MPI_SUM, MyComm);
first_elem_global -= local_elems;
Array<int> new_ranks(leaf_elements.Size());
new_ranks = -1;
for (int i = 0, j = 0; i < leaf_elements.Size(); i++)
{
Array<int> new_ranks(leaf_elements.Size());
new_ranks = -1;
// figure out new assignments for Element::rank
long local_elems = NElements, total_elems = 0;
MPI_Allreduce(&local_elems, &total_elems, 1, MPI_LONG, MPI_SUM, MyComm);
long first_elem_global = 0;
MPI_Scan(&local_elems, &first_elem_global, 1, MPI_LONG, MPI_SUM, MyComm);
first_elem_global -= local_elems;
for (int i = 0, j = 0; i < leaf_elements.Size(); i++)
if (elements[leaf_elements[i]].rank == MyRank)
{
if (elements[leaf_elements[i]].rank == MyRank)
{
new_ranks[i] = Partition(first_elem_global + (j++), total_elems);
}
new_ranks[i] = Partition(first_elem_global + (j++), total_elems);
}
int target_elements = PartitionFirstIndex(MyRank+1, total_elems)
- PartitionFirstIndex(MyRank, total_elems);
// assign the new ranks and send elements (plus ghosts) to new owners
RedistributeElements(new_ranks, target_elements, true);
}
else // whatever partitioning the user has passed
{
MFEM_VERIFY(custom_partition->Size() == NElements,
"Size of the partition array must match the number "
"of local mesh elements (ParMesh::GetNE()).");
Array<int> new_ranks;
custom_partition->Copy(new_ranks);
int target_elements = PartitionFirstIndex(MyRank+1, total_elems)
- PartitionFirstIndex(MyRank, total_elems);
new_ranks.SetSize(leaf_elements.Size(), -1); // make room for ghosts
RedistributeElements(new_ranks, -1, true);
}
// assign the new ranks and send elements (plus ghosts) to new owners
RedistributeElements(new_ranks, target_elements, true);
// set up the old index array
old_index_or_rank.SetSize(NElements);
@@ -1828,22 +1754,28 @@ void ParNCMesh::Rebalance(const Array<int> *custom_partition)
Prune();
}
struct CompareRanks // TODO: use lambda when C++11 available
{
typedef BlockArray<NCMesh::Element> ElemArray;
const ElemArray &elements;
CompareRanks(const ElemArray &elements) : elements(elements) {}
inline bool operator()(const int a, const int b)
{
return elements[a].rank < elements[b].rank;
}
};
void ParNCMesh::RedistributeElements(Array<int> &new_ranks, int target_elements,
bool record_comm)
{
bool sfc = (target_elements >= 0);
UpdateLayers();
// *** STEP 1: communicate new rank assignments for the ghost layer ***
NeighborElementRankMessage::Map send_ghost_ranks, recv_ghost_ranks;
ghost_layer.Sort([&](const int a, const int b)
{
return elements[a].rank < elements[b].rank;
});
ghost_layer.Sort(CompareRanks(elements));
{
Array<int> rank_neighbors;
@@ -1921,17 +1853,12 @@ void ParNCMesh::RedistributeElements(Array<int> &new_ranks, int target_elements,
el.rank = new_ranks[i];
}
int nsent = 0, nrecv = 0; // for debug check
RebalanceMessage::Map send_elems;
{
// sort elements we own by the new rank
Array<int> owned_elements;
owned_elements.MakeRef(leaf_elements.GetData(), NElements);
owned_elements.Sort([&](const int a, const int b)
{
return elements[a].rank < elements[b].rank;
});
owned_elements.Sort(CompareRanks(elements));
Array<int> batch;
batch.Reserve(1024);
@@ -1973,16 +1900,7 @@ void ParNCMesh::RedistributeElements(Array<int> &new_ranks, int target_elements,
// disrupting the termination mechanism in Step 4.
}
if (sfc)
{
msg.Isend(rank, MyComm);
}
else
{
// custom partitioning needs synchronous sends
msg.Issend(rank, MyComm);
}
nsent++;
msg.Isend(rank, MyComm);
// also: record what elements we sent (excluding the ghosts)
// so that SendRebalanceDofs can later send data for them
@@ -1998,108 +1916,41 @@ void ParNCMesh::RedistributeElements(Array<int> &new_ranks, int target_elements,
// *** STEP 3: receive elements from others ***
/* We don't know from whom we're going to receive so we need to probe.
Fortunately, we do know how many elements we're going to own eventually
so the termination condition is easy. */
RebalanceMessage msg;
msg.SetNCMesh(this);
if (sfc)
while (received_elements < target_elements)
{
/* We don't know from whom we're going to receive, so we need to probe.
However, for the default SFC partitioning, we do know how many elements
we're going to own eventually, so the termination condition is easy. */
int rank, size;
RebalanceMessage::Probe(rank, size, MyComm);
while (received_elements < target_elements)
// receive message; note: elements are created as the message is decoded
msg.Recv(rank, size, MyComm);
for (int i = 0; i < msg.Size(); i++)
{
int rank, size;
RebalanceMessage::Probe(rank, size, MyComm);
int elem_rank = msg.values[i];
elements[msg.elements[i]].rank = elem_rank;
// receive message; note: elements are created as the message is decoded
msg.Recv(rank, size, MyComm);
nrecv++;
for (int i = 0; i < msg.Size(); i++)
{
int elem_rank = msg.values[i];
elements[msg.elements[i]].rank = elem_rank;
if (elem_rank == MyRank) { received_elements++; }
}
// save the ranks we received from, for later use in RecvRebalanceDofs
if (record_comm)
{
recv_rebalance_dofs[rank].SetNCMesh(this);
}
if (elem_rank == MyRank) { received_elements++; }
}
Update();
RebalanceMessage::WaitAllSent(send_elems);
}
else
{
/* The case (target_elements < 0) is used for custom partitioning.
Here we need to employ the "non-blocking consensus" algorithm
(https://scorec.rpi.edu/REPORTS/2015-9.pdf) to determine when the
element exchange is finished. The algorithm uses a non-blocking
barrier. */
MPI_Request barrier = MPI_REQUEST_NULL;
int done = 0;
while (!done)
// save the ranks we received from, for later use in RecvRebalanceDofs
if (record_comm)
{
int rank, size;
while (RebalanceMessage::IProbe(rank, size, MyComm))
{
// receive message; note: elements are created as the msg is decoded
msg.Recv(rank, size, MyComm);
nrecv++;
for (int i = 0; i < msg.Size(); i++)
{
elements[msg.elements[i]].rank = msg.values[i];
}
// save the ranks we received from, for later use in RecvRebalanceDofs
if (record_comm)
{
recv_rebalance_dofs[rank].SetNCMesh(this);
}
}
if (barrier != MPI_REQUEST_NULL)
{
MPI_Test(&barrier, &done, MPI_STATUS_IGNORE);
}
else
{
if (RebalanceMessage::TestAllSent(send_elems))
{
int err = MPI_Ibarrier(MyComm, &barrier);
MFEM_VERIFY(err == MPI_SUCCESS, "");
MFEM_VERIFY(barrier != MPI_REQUEST_NULL, "");
}
}
recv_rebalance_dofs[rank].SetNCMesh(this);
}
Update();
}
Update();
// make sure we can delete all send buffers
NeighborElementRankMessage::WaitAllSent(send_ghost_ranks);
#ifdef MFEM_DEBUG
int glob_sent, glob_recv;
MPI_Reduce(&nsent, &glob_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
MPI_Reduce(&nrecv, &glob_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
if (MyRank == 0)
{
MFEM_ASSERT(glob_sent == glob_recv,
"(glob_sent, glob_recv) = ("
<< glob_sent << ", " << glob_recv << ")");
}
#endif
NeighborElementRankMessage::WaitAllSent(send_elems);
}
@@ -2391,8 +2242,8 @@ void ParNCMesh::AdjustMeshIds(Array<MeshId> ids[], int rank)
if (contains_rank[entity_pmat_group[2][face_id.index]])
{
int v[4], e[4], eo[4], pos, k;
int nfv = GetFaceVerticesEdges(face_id, v, e, eo);
for (int j = 0; j < nfv; j++)
GetFaceVerticesEdges(face_id, v, e, eo);
for (int j = 0; j < 4; j++)
{
if ((pos = find_v.FindSorted(Pair<int, int>(v[j], 0))) != -1)
{
@@ -2416,7 +2267,7 @@ void ParNCMesh::ChangeVertexMeshIdElement(NCMesh::MeshId &id, int elem)
Element &el = elements[elem];
MFEM_ASSERT(el.ref_type == 0, "");
GeomInfo& gi = GI[el.Geom()];
GeomInfo& gi = GI[(int) el.geom];
for (int i = 0; i < gi.nv; i++)
{
if (nodes[el.node[i]].vert_index == id.index)
@@ -2432,14 +2283,14 @@ void ParNCMesh::ChangeVertexMeshIdElement(NCMesh::MeshId &id, int elem)
void ParNCMesh::ChangeEdgeMeshIdElement(NCMesh::MeshId &id, int elem)
{
Element &old = elements[id.element];
const int *ev = GI[old.Geom()].edges[(int) id.local];
const int *ev = GI[(int) old.geom].edges[id.local];
Node* node = nodes.Find(old.node[ev[0]], old.node[ev[1]]);
MFEM_ASSERT(node != NULL, "Edge not found.");
Element &el = elements[elem];
MFEM_ASSERT(el.ref_type == 0, "");
GeomInfo& gi = GI[el.Geom()];
GeomInfo& gi = GI[(int) el.geom];
for (int i = 0; i < gi.ne; i++)
{
const int* ev = gi.edges[i];
@@ -2538,17 +2389,17 @@ void ParNCMesh::DecodeMeshIds(std::istream &is, Array<MeshId> ids[])
id.local = read<char>(is);
// find vertex/edge/face index
GeomInfo &gi = GI[el.Geom()];
GeomInfo &gi = GI[(int) el.geom];
switch (type)
{
case 0:
{
id.index = nodes[el.node[(int) id.local]].vert_index;
id.index = nodes[el.node[id.local]].vert_index;
break;
}
case 1:
{
const int* ev = gi.edges[(int) id.local];
const int* ev = gi.edges[id.local];
Node* node = nodes.Find(el.node[ev[0]], el.node[ev[1]]);
MFEM_ASSERT(node && node->HasEdge(), "edge not found.");
id.index = node->edge_index;
@@ -2556,7 +2407,7 @@ void ParNCMesh::DecodeMeshIds(std::istream &is, Array<MeshId> ids[])
}
default:
{
const int* fv = gi.faces[(int) id.local];
const int* fv = gi.faces[id.local];
Face* face = faces.Find(el.node[fv[0]], el.node[fv[1]],
el.node[fv[2]], el.node[fv[3]]);
MFEM_ASSERT(face, "face not found.");
+10 -19
View File
@@ -87,12 +87,8 @@ public:
virtual void Derefine(const Array<int> &derefs);
/** Migrate leaf elements of the global refinement hierarchy (including ghost
elements) so that each processor owns the same number of leaves (+-1).
The default partitioning strategy is based on equal splitting of the
space-filling sequence of leaf elements (custom_partition == NULL).
Alternatively, a used-defined element-rank assignemnt array can be
passed. */
void Rebalance(const Array<int> *custom_partition = NULL);
elements) so that each processor owns the same number of leaves (+-1). */
void Rebalance();
// interface for ParFiniteElementSpace
@@ -104,6 +100,9 @@ public:
int GetNGhostFaces() const { return NGhostFaces; }
int GetNGhostElements() const { return NGhostElements; }
Geometry::Type GetGhostFaceGeometry(int ghost_face_id) const
{ return Geometry::SQUARE; }
// Return a list of vertices/edges/faces shared by this processor and at
// least one other processor. These are subsets of NCMesh::<entity>_list. */
const NCList& GetSharedVertices() { GetVertexList(); return shared_vertices; }
@@ -169,12 +168,6 @@ public:
/// Return true if the specified vertex/edge/face is a ghost.
bool IsGhost(int entity, int index) const
{
if (index < 0) // special case prism edge-face constraint
{
MFEM_ASSERT(entity == 2, "");
entity = 1;
index = -1 - index;
}
switch (entity)
{
case 0: return index >= NVertices;
@@ -263,7 +256,7 @@ protected: // implementation
GroupList groups; // comm group list; NOTE: groups[0] = { MyRank }
GroupMap group_id; // search index over groups
// owner rank for each vertex, edge and face (encoded as singleton group)
// owner rank for each vertex, edge and face (encoded as singleton groups)
Array<GroupId> entity_owner[3];
// P matrix comm pattern groups for each vertex/edge/face (0/1/2)
Array<GroupId> entity_pmat_group[3];
@@ -343,8 +336,7 @@ protected: // implementation
void UpdateLayers();
void MakeSharedTable(int ngroups, int ent, Array<int> &shared_local,
Table &group_shared, Array<char> *entity_geom = NULL,
char geom = 0);
Table &group_shared);
/** Uniquely encodes a set of leaf elements in the refinement hierarchy of
an NCMesh. Can be dumped to a stream, sent to another processor, loaded,
@@ -514,10 +506,7 @@ protected: // implementation
/** Assign new Element::rank to leaf elements and send them to their new
owners, keeping the ghost layer up to date. Used by Rebalance() and
Derefine(). 'target_elements' is the number of elements this rank
is supposed to own after the exchange. If this number is not known
apriori, the parameter can be set to -1, but more expensive communication
(synchronous sends and a barrier) will be used in that case. */
Derefine(). */
void RedistributeElements(Array<int> &new_ranks, int target_elements,
bool record_comm);
@@ -537,6 +526,8 @@ protected: // implementation
long GroupsMemoryUsage() const;
static bool compare_ranks_indices(const Element* a, const Element* b);
friend class NeighborRowMessage;
};
-5
View File
@@ -46,14 +46,9 @@ public:
virtual const int *GetEdgeVertices(int ei) const { return NULL; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
virtual int GetNFaceVertices(int) const { return 0; }
virtual const int *GetFaceVertices(int fi) const { return NULL; }
virtual Element *Duplicate(Mesh *m) const
-5
View File
@@ -53,14 +53,9 @@ public:
virtual const int *GetEdgeVertices(int ei) const
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
virtual int GetNFaceVertices(int) const { return 0; }
virtual const int *GetFaceVertices(int fi) const { return NULL; }
virtual Element *Duplicate(Mesh *m) const
-5
View File
@@ -52,14 +52,9 @@ public:
virtual const int *GetEdgeVertices(int ei) const { return NULL; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
virtual int GetNFaceVertices(int) const { return 0; }
virtual const int *GetFaceVertices(int fi) const { return NULL; }
virtual Element *Duplicate(Mesh *m) const
+1 -4
View File
@@ -40,16 +40,13 @@ Tetrahedron::Tetrahedron(int ind1, int ind2, int ind3, int ind4, int attr)
transform = 0;
}
void Tetrahedron::Init(int ind1, int ind2, int ind3, int ind4, int attr,
int ref_flag)
void Tetrahedron::Init(int ind1, int ind2, int ind3, int ind4, int attr)
{
attribute = attr;
indices[0] = ind1;
indices[1] = ind2;
indices[2] = ind3;
indices[3] = ind4;
refinement_flag = ref_flag;
transform = 0;
}
void Tetrahedron::ParseRefinementFlag(int refinement_edges[2], int &type,
+1 -7
View File
@@ -52,8 +52,7 @@ public:
Tetrahedron(int ind1, int ind2, int ind3, int ind4, int attr = 1);
/// Initialize the vertex indices and the attribute of a Tetrahedron.
void Init(int ind1, int ind2, int ind3, int ind4, int attr = 1,
int ref_flag = 0);
void Init(int ind1, int ind2, int ind3, int ind4, int attr = 1);
/// Return element's type.
virtual Type GetType() const { return Element::TETRAHEDRON; }
@@ -100,14 +99,9 @@ public:
virtual const int *GetEdgeVertices(int ei) const
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 3; return 4; }
virtual int GetNFaces() const { return 4; }
virtual int GetNFaceVertices(int) const { return 3; }
virtual const int *GetFaceVertices(int fi) const
{ MFEM_ABORT("not implemented"); return NULL; }
-5
View File
@@ -80,14 +80,9 @@ public:
virtual const int *GetEdgeVertices(int ei) const
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
virtual int GetNFaceVertices(int) const { return 0; }
virtual const int *GetFaceVertices(int fi) const
{ MFEM_ABORT("not implemented"); return NULL; }

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