Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9b4a76cf9 | ||
|
|
7488a91216 | ||
|
|
e18916ec92 | ||
|
|
8decda8986 | ||
|
|
e03e973342 | ||
|
|
6b8414d820 | ||
|
|
69810197f6 | ||
|
|
2a9cd29202 | ||
|
|
b1784fc793 | ||
|
|
4b9971d377 | ||
|
|
1a6f2226b3 | ||
|
|
675b507a7c | ||
|
|
3a3bbeae4d | ||
|
|
dfc7f9f66d | ||
|
|
d245dc8485 | ||
|
|
5dbf3c8b9f | ||
|
|
407705c8de | ||
|
|
0627783135 | ||
|
|
c9fc6dfada | ||
|
|
c226405378 | ||
|
|
c6faec60f8 | ||
|
|
3cb600cf8b | ||
|
|
b610b36173 | ||
|
|
930daccddb | ||
|
|
d84b46bc45 | ||
|
|
51060626fe | ||
|
|
ff69c272cc | ||
|
|
6bc7fa8216 | ||
|
|
26f1114105 | ||
|
|
45bbc247a4 | ||
|
|
0e812c6a01 | ||
|
|
177fd7f80d | ||
|
|
b64de4db84 | ||
|
|
0e6ec8be7e | ||
|
|
a09ce63307 | ||
|
|
40f4200bb5 | ||
|
|
14c9a845a2 | ||
|
|
09a93c96fb | ||
|
|
9a3d7a34ad | ||
|
|
05fcff7325 | ||
|
|
87869636cc | ||
|
|
9aa6823a0b | ||
|
|
90c4f4fd78 | ||
|
|
bde675abff | ||
|
|
065817c5e1 | ||
|
|
689c259c80 | ||
|
|
05cf4c40b0 | ||
|
|
9ddb97e100 | ||
|
|
b69a90eb16 |
@@ -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
@@ -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})
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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@")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.")
|
||||
@@ -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@
|
||||
|
||||
|
||||
@@ -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@
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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). */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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; )
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+60
-121
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user